@glubean/runner 0.1.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.
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Metric threshold evaluator.
3
+ *
4
+ * Collects metric data points during a run, then evaluates user-defined
5
+ * thresholds against aggregated values (avg, min, max, percentiles, count).
6
+ *
7
+ * @module
8
+ */
9
+ import type { ThresholdAggregation, ThresholdConfig, ThresholdExpression, ThresholdSummary } from "@glubean/sdk";
10
+ /**
11
+ * Collects raw metric values during a run, grouped by metric name.
12
+ * Call `add()` for each metric event, then `getValues()` to retrieve.
13
+ */
14
+ export declare class MetricCollector {
15
+ private data;
16
+ /** Record a single metric data point. */
17
+ add(name: string, value: number): void;
18
+ /** Get all recorded values for a metric (empty array if none). */
19
+ getValues(name: string): number[];
20
+ /** Get all metric names that have data. */
21
+ getNames(): string[];
22
+ }
23
+ /** Compute an aggregation over a set of values. */
24
+ export declare function aggregate(values: number[], agg: ThresholdAggregation): number;
25
+ interface ParsedExpression {
26
+ operator: "<" | "<=";
27
+ value: number;
28
+ }
29
+ /** Parse a threshold expression like "<200" or "<=500". */
30
+ export declare function parseExpression(expr: ThresholdExpression): ParsedExpression | null;
31
+ /**
32
+ * Evaluate all thresholds against collected metric data.
33
+ *
34
+ * @param config - User-defined threshold configuration
35
+ * @param collector - MetricCollector with recorded data points
36
+ * @returns Summary with individual results and overall pass/fail
37
+ */
38
+ export declare function evaluateThresholds(config: ThresholdConfig, collector: MetricCollector): ThresholdSummary;
39
+ export {};
40
+ //# sourceMappingURL=thresholds.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thresholds.d.ts","sourceRoot":"","sources":["../src/thresholds.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAEV,oBAAoB,EACpB,eAAe,EACf,mBAAmB,EAEnB,gBAAgB,EACjB,MAAM,cAAc,CAAC;AAItB;;;GAGG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,IAAI,CAA+B;IAE3C,yCAAyC;IACzC,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAStC,kEAAkE;IAClE,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE;IAIjC,2CAA2C;IAC3C,QAAQ,IAAI,MAAM,EAAE;CAGrB;AAWD,mDAAmD;AACnD,wBAAgB,SAAS,CACvB,MAAM,EAAE,MAAM,EAAE,EAChB,GAAG,EAAE,oBAAoB,GACxB,MAAM,CAqBR;AAID,UAAU,gBAAgB;IACxB,QAAQ,EAAE,GAAG,GAAG,IAAI,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,2DAA2D;AAC3D,wBAAgB,eAAe,CAAC,IAAI,EAAE,mBAAmB,GAAG,gBAAgB,GAAG,IAAI,CAOlF;AA8BD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,eAAe,EACvB,SAAS,EAAE,eAAe,GACzB,gBAAgB,CAmDlB"}
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Metric threshold evaluator.
3
+ *
4
+ * Collects metric data points during a run, then evaluates user-defined
5
+ * thresholds against aggregated values (avg, min, max, percentiles, count).
6
+ *
7
+ * @module
8
+ */
9
+ // ── Metric Collector ──────────────────────────────────────────────────────────
10
+ /**
11
+ * Collects raw metric values during a run, grouped by metric name.
12
+ * Call `add()` for each metric event, then `getValues()` to retrieve.
13
+ */
14
+ export class MetricCollector {
15
+ data = new Map();
16
+ /** Record a single metric data point. */
17
+ add(name, value) {
18
+ const arr = this.data.get(name);
19
+ if (arr) {
20
+ arr.push(value);
21
+ }
22
+ else {
23
+ this.data.set(name, [value]);
24
+ }
25
+ }
26
+ /** Get all recorded values for a metric (empty array if none). */
27
+ getValues(name) {
28
+ return this.data.get(name) ?? [];
29
+ }
30
+ /** Get all metric names that have data. */
31
+ getNames() {
32
+ return [...this.data.keys()];
33
+ }
34
+ }
35
+ // ── Aggregation ───────────────────────────────────────────────────────────────
36
+ /** Compute a percentile from a sorted array (nearest-rank method). */
37
+ function percentile(sorted, p) {
38
+ if (sorted.length === 0)
39
+ return 0;
40
+ const idx = Math.ceil((p / 100) * sorted.length) - 1;
41
+ return sorted[Math.max(0, idx)];
42
+ }
43
+ /** Compute an aggregation over a set of values. */
44
+ export function aggregate(values, agg) {
45
+ if (values.length === 0)
46
+ return 0;
47
+ switch (agg) {
48
+ case "count":
49
+ return values.length;
50
+ case "avg":
51
+ return values.reduce((a, b) => a + b, 0) / values.length;
52
+ case "min":
53
+ return Math.min(...values);
54
+ case "max":
55
+ return Math.max(...values);
56
+ case "p50":
57
+ case "p90":
58
+ case "p95":
59
+ case "p99": {
60
+ const sorted = [...values].sort((a, b) => a - b);
61
+ const p = parseInt(agg.slice(1), 10);
62
+ return percentile(sorted, p);
63
+ }
64
+ }
65
+ }
66
+ /** Parse a threshold expression like "<200" or "<=500". */
67
+ export function parseExpression(expr) {
68
+ const match = expr.match(/^(<=?)\s*(-?\d+(?:\.\d+)?)$/);
69
+ if (!match)
70
+ return null;
71
+ return {
72
+ operator: match[1],
73
+ value: parseFloat(match[2]),
74
+ };
75
+ }
76
+ /** Evaluate whether an actual value passes a threshold expression. */
77
+ function passes(actual, expr) {
78
+ return expr.operator === "<" ? actual < expr.value : actual <= expr.value;
79
+ }
80
+ // ── Threshold Evaluation ──────────────────────────────────────────────────────
81
+ /** Normalize shorthand string to full rules object. */
82
+ function normalizeRules(input) {
83
+ if (typeof input === "string") {
84
+ return { avg: input };
85
+ }
86
+ return input;
87
+ }
88
+ const VALID_AGGREGATIONS = new Set([
89
+ "avg",
90
+ "min",
91
+ "max",
92
+ "p50",
93
+ "p90",
94
+ "p95",
95
+ "p99",
96
+ "count",
97
+ ]);
98
+ /**
99
+ * Evaluate all thresholds against collected metric data.
100
+ *
101
+ * @param config - User-defined threshold configuration
102
+ * @param collector - MetricCollector with recorded data points
103
+ * @returns Summary with individual results and overall pass/fail
104
+ */
105
+ export function evaluateThresholds(config, collector) {
106
+ const results = [];
107
+ for (const [metricName, rawRules] of Object.entries(config)) {
108
+ const rules = normalizeRules(rawRules);
109
+ const values = collector.getValues(metricName);
110
+ for (const [aggKey, expr] of Object.entries(rules)) {
111
+ const agg = aggKey;
112
+ if (!VALID_AGGREGATIONS.has(agg) || !expr)
113
+ continue;
114
+ const parsed = parseExpression(expr);
115
+ if (!parsed) {
116
+ // Invalid expression — treat as fail
117
+ results.push({
118
+ metric: metricName,
119
+ aggregation: agg,
120
+ threshold: expr,
121
+ actual: NaN,
122
+ pass: false,
123
+ });
124
+ continue;
125
+ }
126
+ if (values.length === 0) {
127
+ // No data for this metric — skip (not a failure)
128
+ results.push({
129
+ metric: metricName,
130
+ aggregation: agg,
131
+ threshold: expr,
132
+ actual: 0,
133
+ pass: true,
134
+ });
135
+ continue;
136
+ }
137
+ const actual = aggregate(values, agg);
138
+ results.push({
139
+ metric: metricName,
140
+ aggregation: agg,
141
+ threshold: expr,
142
+ actual: Math.round(actual * 100) / 100,
143
+ pass: passes(actual, parsed),
144
+ });
145
+ }
146
+ }
147
+ return {
148
+ results,
149
+ pass: results.every((r) => r.pass),
150
+ };
151
+ }
152
+ //# sourceMappingURL=thresholds.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thresholds.js","sourceRoot":"","sources":["../src/thresholds.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAWH,iFAAiF;AAEjF;;;GAGG;AACH,MAAM,OAAO,eAAe;IAClB,IAAI,GAAG,IAAI,GAAG,EAAoB,CAAC;IAE3C,yCAAyC;IACzC,GAAG,CAAC,IAAY,EAAE,KAAa;QAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,GAAG,EAAE,CAAC;YACR,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClB,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;IAED,kEAAkE;IAClE,SAAS,CAAC,IAAY;QACpB,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IACnC,CAAC;IAED,2CAA2C;IAC3C,QAAQ;QACN,OAAO,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAC/B,CAAC;CACF;AAED,iFAAiF;AAEjF,sEAAsE;AACtE,SAAS,UAAU,CAAC,MAAgB,EAAE,CAAS;IAC7C,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAClC,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACrD,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;AAClC,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,SAAS,CACvB,MAAgB,EAChB,GAAyB;IAEzB,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAElC,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,OAAO;YACV,OAAO,MAAM,CAAC,MAAM,CAAC;QACvB,KAAK,KAAK;YACR,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC;QAC3D,KAAK,KAAK;YACR,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC;QAC7B,KAAK,KAAK;YACR,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC;QAC7B,KAAK,KAAK,CAAC;QACX,KAAK,KAAK,CAAC;QACX,KAAK,KAAK,CAAC;QACX,KAAK,KAAK,CAAC,CAAC,CAAC;YACX,MAAM,MAAM,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YACjD,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACrC,OAAO,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;AACH,CAAC;AASD,2DAA2D;AAC3D,MAAM,UAAU,eAAe,CAAC,IAAyB;IACvD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACxD,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,OAAO;QACL,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAe;QAChC,KAAK,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;KAC5B,CAAC;AACJ,CAAC;AAED,sEAAsE;AACtE,SAAS,MAAM,CAAC,MAAc,EAAE,IAAsB;IACpD,OAAO,IAAI,CAAC,QAAQ,KAAK,GAAG,CAAC,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC;AAC5E,CAAC;AAED,iFAAiF;AAEjF,uDAAuD;AACvD,SAAS,cAAc,CACrB,KAAiD;IAEjD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;IACxB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAuB;IACvD,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,OAAO;CACR,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAChC,MAAuB,EACvB,SAA0B;IAE1B,MAAM,OAAO,GAAsB,EAAE,CAAC;IAEtC,KAAK,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5D,MAAM,KAAK,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;QACvC,MAAM,MAAM,GAAG,SAAS,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QAE/C,KAAK,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACnD,MAAM,GAAG,GAAG,MAA8B,CAAC;YAC3C,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI;gBAAE,SAAS;YAEpD,MAAM,MAAM,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;YACrC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,qCAAqC;gBACrC,OAAO,CAAC,IAAI,CAAC;oBACX,MAAM,EAAE,UAAU;oBAClB,WAAW,EAAE,GAAG;oBAChB,SAAS,EAAE,IAAI;oBACf,MAAM,EAAE,GAAG;oBACX,IAAI,EAAE,KAAK;iBACZ,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YAED,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACxB,iDAAiD;gBACjD,OAAO,CAAC,IAAI,CAAC;oBACX,MAAM,EAAE,UAAU;oBAClB,WAAW,EAAE,GAAG;oBAChB,SAAS,EAAE,IAAI;oBACf,MAAM,EAAE,CAAC;oBACT,IAAI,EAAE,IAAI;iBACX,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YAED,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;YACtC,OAAO,CAAC,IAAI,CAAC;gBACX,MAAM,EAAE,UAAU;gBAClB,WAAW,EAAE,GAAG;gBAChB,SAAS,EAAE,IAAI;gBACf,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,GAAG,GAAG;gBACtC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;aAC7B,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO;QACL,OAAO;QACP,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;KACnC,CAAC;AACJ,CAAC"}
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@glubean/runner",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": {
7
+ "types": "./dist/index.d.ts",
8
+ "import": "./dist/index.js"
9
+ }
10
+ },
11
+ "files": [
12
+ "dist"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc -p tsconfig.build.json",
16
+ "test": "vitest run"
17
+ },
18
+ "dependencies": {
19
+ "@glubean/sdk": "^0.1.0",
20
+ "ky": "^1.14.0",
21
+ "tsx": "^4.19.0"
22
+ },
23
+ "devDependencies": {
24
+ "@types/node": "^22.0.0",
25
+ "zod": "^4.3.6"
26
+ }
27
+ }