@pagopa/dx-savemoney 0.2.5 → 0.3.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 +33 -27
- package/dist/azure/analyzer.d.ts +49 -21
- package/dist/azure/analyzer.d.ts.map +1 -1
- package/dist/azure/analyzer.js +369 -93
- package/dist/azure/analyzer.js.map +1 -1
- package/dist/azure/analyzers/advisor.d.ts +68 -0
- package/dist/azure/analyzers/advisor.d.ts.map +1 -0
- package/dist/azure/analyzers/advisor.js +234 -0
- package/dist/azure/analyzers/advisor.js.map +1 -0
- package/dist/azure/analyzers/index.d.ts +8 -0
- package/dist/azure/analyzers/index.d.ts.map +1 -0
- package/dist/azure/analyzers/index.js +6 -0
- package/dist/azure/analyzers/index.js.map +1 -0
- package/dist/azure/analyzers/registry.d.ts +29 -0
- package/dist/azure/analyzers/registry.d.ts.map +1 -0
- package/dist/azure/analyzers/registry.js +79 -0
- package/dist/azure/analyzers/registry.js.map +1 -0
- package/dist/azure/analyzers/subscription.d.ts +53 -0
- package/dist/azure/analyzers/subscription.d.ts.map +1 -0
- package/dist/azure/analyzers/subscription.js +18 -0
- package/dist/azure/analyzers/subscription.js.map +1 -0
- package/dist/azure/analyzers/types.d.ts +62 -0
- package/dist/azure/analyzers/types.d.ts.map +1 -0
- package/dist/azure/analyzers/types.js +15 -0
- package/dist/azure/analyzers/types.js.map +1 -0
- package/dist/azure/config.d.ts.map +1 -1
- package/dist/azure/config.js +2 -0
- package/dist/azure/config.js.map +1 -1
- package/dist/azure/index.d.ts +1 -0
- package/dist/azure/index.d.ts.map +1 -1
- package/dist/azure/index.js +1 -0
- package/dist/azure/index.js.map +1 -1
- package/dist/azure/report.d.ts.map +1 -1
- package/dist/azure/report.js +178 -29
- package/dist/azure/report.js.map +1 -1
- package/dist/azure/resources/app-service.d.ts +2 -1
- package/dist/azure/resources/app-service.d.ts.map +1 -1
- package/dist/azure/resources/app-service.js +3 -3
- package/dist/azure/resources/app-service.js.map +1 -1
- package/dist/azure/resources/container-app.d.ts +2 -1
- package/dist/azure/resources/container-app.d.ts.map +1 -1
- package/dist/azure/resources/container-app.js +9 -9
- package/dist/azure/resources/container-app.js.map +1 -1
- package/dist/azure/resources/public-ip.d.ts +2 -1
- package/dist/azure/resources/public-ip.d.ts.map +1 -1
- package/dist/azure/resources/public-ip.js +2 -2
- package/dist/azure/resources/public-ip.js.map +1 -1
- package/dist/azure/resources/static-web-app.d.ts +2 -1
- package/dist/azure/resources/static-web-app.d.ts.map +1 -1
- package/dist/azure/resources/static-web-app.js +3 -3
- package/dist/azure/resources/static-web-app.js.map +1 -1
- package/dist/azure/resources/storage.d.ts +2 -1
- package/dist/azure/resources/storage.d.ts.map +1 -1
- package/dist/azure/resources/storage.js +2 -2
- package/dist/azure/resources/storage.js.map +1 -1
- package/dist/azure/resources/vm.d.ts +2 -1
- package/dist/azure/resources/vm.d.ts.map +1 -1
- package/dist/azure/resources/vm.js +3 -3
- package/dist/azure/resources/vm.js.map +1 -1
- package/dist/azure/types.d.ts +34 -1
- package/dist/azure/types.d.ts.map +1 -1
- package/dist/azure/utils.d.ts +35 -3
- package/dist/azure/utils.d.ts.map +1 -1
- package/dist/azure/utils.js +70 -29
- package/dist/azure/utils.js.map +1 -1
- package/dist/finding.d.ts +114 -0
- package/dist/finding.d.ts.map +1 -0
- package/dist/finding.js +51 -0
- package/dist/finding.js.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/schema.d.ts +5 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +14 -0
- package/dist/schema.js.map +1 -1
- package/package.json +4 -1
- package/src/__tests__/finding.test.ts +149 -0
- package/src/azure/__tests__/analyzer-tags.test.ts +74 -0
- package/src/azure/__tests__/report.test.ts +27 -0
- package/src/azure/__tests__/utils.test.ts +164 -2
- package/src/azure/analyzer.ts +513 -182
- package/src/azure/analyzers/__tests__/advisor.test.ts +367 -0
- package/src/azure/analyzers/advisor.ts +324 -0
- package/src/azure/analyzers/index.ts +14 -0
- package/src/azure/analyzers/registry.ts +196 -0
- package/src/azure/analyzers/subscription.ts +56 -0
- package/src/azure/analyzers/types.ts +66 -0
- package/src/azure/config.ts +2 -0
- package/src/azure/index.ts +1 -0
- package/src/azure/report.ts +206 -35
- package/src/azure/resources/app-service.ts +4 -0
- package/src/azure/resources/container-app.ts +10 -0
- package/src/azure/resources/public-ip.ts +3 -0
- package/src/azure/resources/static-web-app.ts +4 -0
- package/src/azure/resources/storage.ts +3 -0
- package/src/azure/resources/vm.ts +4 -0
- package/src/azure/types.ts +35 -1
- package/src/azure/utils.ts +110 -39
- package/src/finding.ts +152 -0
- package/src/index.ts +19 -1
- package/src/schema.ts +14 -0
|
@@ -5,6 +5,7 @@ import type { ComputeManagementClient } from "@azure/arm-compute";
|
|
|
5
5
|
import type { MonitorClient } from "@azure/arm-monitor";
|
|
6
6
|
import * as armResources from "@azure/arm-resources";
|
|
7
7
|
import type { AnalysisResult, Thresholds } from "../../types.js";
|
|
8
|
+
import { type MetricsCache } from "../utils.js";
|
|
8
9
|
/**
|
|
9
10
|
* Analyzes an Azure Virtual Machine for potential cost optimization.
|
|
10
11
|
*
|
|
@@ -15,5 +16,5 @@ import type { AnalysisResult, Thresholds } from "../../types.js";
|
|
|
15
16
|
* @param verbose - Whether verbose logging is enabled
|
|
16
17
|
* @returns Analysis result with cost risk and reason
|
|
17
18
|
*/
|
|
18
|
-
export declare function analyzeVM(resource: armResources.GenericResource, monitorClient: MonitorClient, computeClient: ComputeManagementClient, timespanDays: number, thresholds?: Thresholds, verbose?: boolean): Promise<AnalysisResult>;
|
|
19
|
+
export declare function analyzeVM(resource: armResources.GenericResource, monitorClient: MonitorClient, computeClient: ComputeManagementClient, timespanDays: number, thresholds?: Thresholds, verbose?: boolean, cache?: MetricsCache): Promise<AnalysisResult>;
|
|
19
20
|
//# sourceMappingURL=vm.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vm.d.ts","sourceRoot":"","sources":["../../../src/azure/resources/vm.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,KAAK,YAAY,MAAM,sBAAsB,CAAC;AAGrD,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"vm.d.ts","sourceRoot":"","sources":["../../../src/azure/resources/vm.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,KAAK,YAAY,MAAM,sBAAsB,CAAC;AAGrD,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAGjE,OAAO,EAEL,KAAK,YAAY,EAIlB,MAAM,aAAa,CAAC;AAErB;;;;;;;;;GASG;AACH,wBAAsB,SAAS,CAC7B,QAAQ,EAAE,YAAY,CAAC,eAAe,EACtC,aAAa,EAAE,aAAa,EAC5B,aAAa,EAAE,uBAAuB,EACtC,YAAY,EAAE,MAAM,EACpB,UAAU,GAAE,UAA+B,EAC3C,OAAO,UAAQ,EACf,KAAK,CAAC,EAAE,YAAY,GACnB,OAAO,CAAC,cAAc,CAAC,CA6FzB"}
|
|
@@ -14,7 +14,7 @@ import { getMetric, verboseLog, verboseLogAnalysisResult, verboseLogResourceStar
|
|
|
14
14
|
* @param verbose - Whether verbose logging is enabled
|
|
15
15
|
* @returns Analysis result with cost risk and reason
|
|
16
16
|
*/
|
|
17
|
-
export async function analyzeVM(resource, monitorClient, computeClient, timespanDays, thresholds = DEFAULT_THRESHOLDS, verbose = false) {
|
|
17
|
+
export async function analyzeVM(resource, monitorClient, computeClient, timespanDays, thresholds = DEFAULT_THRESHOLDS, verbose = false, cache) {
|
|
18
18
|
verboseLogResourceStart(verbose, resource.name || "unknown", "Virtual Machine (microsoft.compute/virtualmachines)");
|
|
19
19
|
verboseLog(verbose, "Resource details:", resource);
|
|
20
20
|
const costRisk = "high";
|
|
@@ -61,8 +61,8 @@ export async function analyzeVM(resource, monitorClient, computeClient, timespan
|
|
|
61
61
|
// Continue with metric analysis if instance view fails
|
|
62
62
|
}
|
|
63
63
|
// Check metrics for low utilization
|
|
64
|
-
const cpuUsage = await getMetric(monitorClient, resource.id, "Percentage CPU", "Average", timespanDays);
|
|
65
|
-
const networkIn = await getMetric(monitorClient, resource.id, "Network In Total", "Average", timespanDays);
|
|
64
|
+
const cpuUsage = await getMetric(monitorClient, resource.id, "Percentage CPU", "Average", timespanDays, cache);
|
|
65
|
+
const networkIn = await getMetric(monitorClient, resource.id, "Network In Total", "Average", timespanDays, cache);
|
|
66
66
|
if (cpuUsage !== null && cpuUsage < thresholds.vm.cpuPercent) {
|
|
67
67
|
reason += `Low CPU usage (avg ${cpuUsage.toFixed(2)}%). `;
|
|
68
68
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vm.js","sourceRoot":"","sources":["../../../src/azure/resources/vm.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAI7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EACL,SAAS,
|
|
1
|
+
{"version":3,"file":"vm.js","sourceRoot":"","sources":["../../../src/azure/resources/vm.ts"],"names":[],"mappings":"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"}
|
package/dist/azure/types.d.ts
CHANGED
|
@@ -2,16 +2,31 @@
|
|
|
2
2
|
* Azure-specific types
|
|
3
3
|
*/
|
|
4
4
|
import type * as armResources from "@azure/arm-resources";
|
|
5
|
+
import type { Finding } from "../finding.js";
|
|
5
6
|
import type { AnalysisResult, BaseConfig, CostRisk, Thresholds } from "../types.js";
|
|
6
7
|
/**
|
|
7
8
|
* Azure configuration extending base config
|
|
8
9
|
*/
|
|
9
10
|
export type AzureConfig = BaseConfig & {
|
|
11
|
+
/**
|
|
12
|
+
* Maximum number of resources analyzed in parallel within a single
|
|
13
|
+
* subscription. Defaults to 8 when not provided. Set to 1 for a fully
|
|
14
|
+
* sequential run (useful for debugging or to be gentler on quotas).
|
|
15
|
+
*/
|
|
16
|
+
concurrency?: number;
|
|
10
17
|
/**
|
|
11
18
|
* Only analyze resources that match ALL the given tag key-value pairs.
|
|
12
19
|
* If omitted, all resources are analyzed.
|
|
13
20
|
*/
|
|
14
21
|
filterTags?: Map<string, string>;
|
|
22
|
+
/**
|
|
23
|
+
* Which finding sources to include in the run.
|
|
24
|
+
* - `"custom"` → enables the per-resource analyzer plugins
|
|
25
|
+
* - `"advisor"` → enables the Azure Advisor subscription-level analyzer
|
|
26
|
+
*
|
|
27
|
+
* Defaults to `["advisor", "custom"]` when omitted (i.e. all sources).
|
|
28
|
+
*/
|
|
29
|
+
sources?: AzureSource[];
|
|
15
30
|
subscriptionIds: string[];
|
|
16
31
|
/**
|
|
17
32
|
* Analysis thresholds. Defaults from DEFAULT_THRESHOLDS are used when not provided.
|
|
@@ -20,10 +35,22 @@ export type AzureConfig = BaseConfig & {
|
|
|
20
35
|
verbose?: boolean;
|
|
21
36
|
};
|
|
22
37
|
/**
|
|
23
|
-
* Detailed report for a single Azure resource with full resource object
|
|
38
|
+
* Detailed report for a single Azure resource with full resource object.
|
|
39
|
+
*
|
|
40
|
+
* Phase 1 introduces the optional `findings` field carrying the unified
|
|
41
|
+
* `Finding[]` model alongside the legacy `analysis` summary. The two are
|
|
42
|
+
* kept in sync by the orchestrator so existing report formats keep
|
|
43
|
+
* working untouched while new consumers (GUI, JSON exports, Phase 2
|
|
44
|
+
* pricing aggregation) can read structured findings directly.
|
|
24
45
|
*/
|
|
25
46
|
export type AzureDetailedResourceReport = {
|
|
26
47
|
analysis: AnalysisResult;
|
|
48
|
+
/**
|
|
49
|
+
* Structured findings attached to the resource. Always populated by the
|
|
50
|
+
* orchestrator (possibly empty). Optional only for backward compatibility
|
|
51
|
+
* with serialised payloads produced before Phase 1.
|
|
52
|
+
*/
|
|
53
|
+
findings?: Finding[];
|
|
27
54
|
resource: armResources.GenericResource;
|
|
28
55
|
};
|
|
29
56
|
/**
|
|
@@ -39,4 +66,10 @@ export type AzureResourceReport = {
|
|
|
39
66
|
suspectedUnused: boolean;
|
|
40
67
|
type: string;
|
|
41
68
|
};
|
|
69
|
+
/**
|
|
70
|
+
* Finding sources that are valid for Azure analysis.
|
|
71
|
+
* Narrowed from `FindingSource` to exclude "aws", which is not a valid
|
|
72
|
+
* filter for Azure runs and would silently produce an empty report.
|
|
73
|
+
*/
|
|
74
|
+
export type AzureSource = "advisor" | "custom";
|
|
42
75
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/azure/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,KAAK,YAAY,MAAM,sBAAsB,CAAC;AAE1D,OAAO,KAAK,EACV,cAAc,EACd,UAAU,EACV,QAAQ,EACR,UAAU,EACX,MAAM,aAAa,CAAC;AAErB;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG;IACrC;;;OAGG;IACH,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B;;OAEG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/azure/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,KAAK,YAAY,MAAM,sBAAsB,CAAC;AAE1D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,KAAK,EACV,cAAc,EACd,UAAU,EACV,QAAQ,EACR,UAAU,EACX,MAAM,aAAa,CAAC;AAErB;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG;IACrC;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;IACxB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B;;OAEG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,MAAM,2BAA2B,GAAG;IACxC,QAAQ,EAAE,cAAc,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IACrB,QAAQ,EAAE,YAAY,CAAC,eAAe,CAAC;CACxC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,OAAO,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC"}
|
package/dist/azure/utils.d.ts
CHANGED
|
@@ -4,6 +4,17 @@
|
|
|
4
4
|
import type { MonitorClient } from "@azure/arm-monitor";
|
|
5
5
|
import type * as armResources from "@azure/arm-resources";
|
|
6
6
|
import type { AnalysisResult } from "../types.js";
|
|
7
|
+
/** Per-run in-memory cache for Azure Monitor metric responses. */
|
|
8
|
+
export type MetricsCache = Map<string, Promise<null | number>>;
|
|
9
|
+
/**
|
|
10
|
+
* Minimal interface required by `getMetric` — only the `metrics.list` shape.
|
|
11
|
+
* Using a structural type instead of the full `MonitorClient` keeps tests
|
|
12
|
+
* strongly typed without unsafe casts and lets non-Azure callers supply a
|
|
13
|
+
* compatible mock.
|
|
14
|
+
*/
|
|
15
|
+
export type MonitorClientLike = {
|
|
16
|
+
metrics: Pick<MonitorClient["metrics"], "list">;
|
|
17
|
+
};
|
|
7
18
|
type MetricDataPoint = {
|
|
8
19
|
average?: number;
|
|
9
20
|
count?: number;
|
|
@@ -28,16 +39,30 @@ export declare function aggregateDataPoints(dataPoints: MetricDataPoint[], aggre
|
|
|
28
39
|
*/
|
|
29
40
|
export declare function extractAggregatedValue(metricData: MetricDataPoint, aggregation: string): null | number;
|
|
30
41
|
/**
|
|
31
|
-
*
|
|
42
|
+
* @internal — exposed for tests only.
|
|
43
|
+
*/
|
|
44
|
+
export declare function _metricsCacheSize(): number;
|
|
45
|
+
/**
|
|
46
|
+
* Fetches a specific metric for a resource from Azure Monitor, with an
|
|
47
|
+
* in-memory cache to deduplicate concurrent and repeated lookups within
|
|
48
|
+
* the same run.
|
|
32
49
|
*
|
|
33
|
-
*
|
|
50
|
+
* Concurrent callers for the same `(resourceId, metricName, aggregation,
|
|
51
|
+
* timespanDays)` tuple share the same underlying request.
|
|
52
|
+
*
|
|
53
|
+
* Pass an explicit `cache` (created per run in the orchestrator) to keep
|
|
54
|
+
* concurrent analysis runs isolated from each other. When omitted, the
|
|
55
|
+
* module-scoped fallback cache is used — safe for sequential runs.
|
|
56
|
+
*
|
|
57
|
+
* @param monitorClient - Azure Monitor client (or compatible mock)
|
|
34
58
|
* @param resourceId - The Azure resource ID
|
|
35
59
|
* @param metricName - The name of the metric to fetch (e.g., "Percentage CPU")
|
|
36
60
|
* @param aggregation - The aggregation type (e.g., "Average", "Total")
|
|
37
61
|
* @param timespanDays - Number of days to look back for metrics
|
|
62
|
+
* @param cache - Optional run-scoped cache; falls back to the module-scoped one
|
|
38
63
|
* @returns The metric value or null if unavailable
|
|
39
64
|
*/
|
|
40
|
-
export declare function getMetric(monitorClient:
|
|
65
|
+
export declare function getMetric(monitorClient: MonitorClientLike, resourceId: string, metricName: string, aggregation: string, timespanDays: number, cache?: MetricsCache): Promise<null | number>;
|
|
41
66
|
/**
|
|
42
67
|
* Returns true if the resource matches ALL specified tag key-value pairs.
|
|
43
68
|
* If filterTags is empty or undefined, always returns true (no filtering).
|
|
@@ -46,6 +71,13 @@ export declare function getMetric(monitorClient: MonitorClient, resourceId: stri
|
|
|
46
71
|
* @param filterTags - Map of required tag key→value pairs
|
|
47
72
|
*/
|
|
48
73
|
export declare function matchesTags(resource: armResources.GenericResource, filterTags: Map<string, string> | undefined): boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Clears the module-scoped fallback metrics cache.
|
|
76
|
+
*
|
|
77
|
+
* Only needed when `getMetric` is called without an explicit `cache`
|
|
78
|
+
* argument. Prefer passing a run-scoped `MetricsCache` instead.
|
|
79
|
+
*/
|
|
80
|
+
export declare function resetMetricsCache(): void;
|
|
49
81
|
/**
|
|
50
82
|
* Logs a verbose message, optionally with an object.
|
|
51
83
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/azure/utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,KAAK,YAAY,MAAM,sBAAsB,CAAC;AAI1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,KAAK,eAAe,GAAG;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,eAAe,EAAE,EAC7B,WAAW,EAAE,MAAM,GAClB,IAAI,GAAG,MAAM,CAiCf;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,eAAe,EAC3B,WAAW,EAAE,MAAM,GAClB,IAAI,GAAG,MAAM,CA6Bf;AAED
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/azure/utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,KAAK,YAAY,MAAM,sBAAsB,CAAC;AAI1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,kEAAkE;AAClE,MAAM,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC;AAE/D;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,CAAC;CACjD,CAAC;AAEF,KAAK,eAAe,GAAG;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,eAAe,EAAE,EAC7B,WAAW,EAAE,MAAM,GAClB,IAAI,GAAG,MAAM,CAiCf;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,eAAe,EAC3B,WAAW,EAAE,MAAM,GAClB,IAAI,GAAG,MAAM,CA6Bf;AASD;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,SAAS,CAC7B,aAAa,EAAE,iBAAiB,EAChC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,KAAK,GAAE,YAA2B,GACjC,OAAO,CAAC,IAAI,GAAG,MAAM,CAAC,CAexB;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,QAAQ,EAAE,YAAY,CAAC,eAAe,EACtC,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,GAC1C,OAAO,CAQT;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAExC;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,OAAO,QAUjB;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,cAAc,QAYvB;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,OAAO,EAChB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,QASrB"}
|
package/dist/azure/utils.js
CHANGED
|
@@ -66,42 +66,46 @@ export function extractAggregatedValue(metricData, aggregation) {
|
|
|
66
66
|
return null;
|
|
67
67
|
}
|
|
68
68
|
/**
|
|
69
|
-
*
|
|
69
|
+
* Module-scoped fallback cache. Used when callers of `getMetric` do not
|
|
70
|
+
* supply a run-scoped cache. Prefer passing an explicit `MetricsCache`
|
|
71
|
+
* instance through `AnalyzerContext` so concurrent runs stay isolated.
|
|
72
|
+
*/
|
|
73
|
+
const metricsCache = new Map();
|
|
74
|
+
/**
|
|
75
|
+
* @internal — exposed for tests only.
|
|
76
|
+
*/
|
|
77
|
+
export function _metricsCacheSize() {
|
|
78
|
+
return metricsCache.size;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Fetches a specific metric for a resource from Azure Monitor, with an
|
|
82
|
+
* in-memory cache to deduplicate concurrent and repeated lookups within
|
|
83
|
+
* the same run.
|
|
84
|
+
*
|
|
85
|
+
* Concurrent callers for the same `(resourceId, metricName, aggregation,
|
|
86
|
+
* timespanDays)` tuple share the same underlying request.
|
|
70
87
|
*
|
|
71
|
-
*
|
|
88
|
+
* Pass an explicit `cache` (created per run in the orchestrator) to keep
|
|
89
|
+
* concurrent analysis runs isolated from each other. When omitted, the
|
|
90
|
+
* module-scoped fallback cache is used — safe for sequential runs.
|
|
91
|
+
*
|
|
92
|
+
* @param monitorClient - Azure Monitor client (or compatible mock)
|
|
72
93
|
* @param resourceId - The Azure resource ID
|
|
73
94
|
* @param metricName - The name of the metric to fetch (e.g., "Percentage CPU")
|
|
74
95
|
* @param aggregation - The aggregation type (e.g., "Average", "Total")
|
|
75
96
|
* @param timespanDays - Number of days to look back for metrics
|
|
97
|
+
* @param cache - Optional run-scoped cache; falls back to the module-scoped one
|
|
76
98
|
* @returns The metric value or null if unavailable
|
|
77
99
|
*/
|
|
78
|
-
export async function getMetric(monitorClient, resourceId, metricName, aggregation, timespanDays) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
return null;
|
|
88
|
-
}
|
|
89
|
-
const metric = result.value[0];
|
|
90
|
-
if (!metric.timeseries || metric.timeseries.length === 0) {
|
|
91
|
-
return null;
|
|
92
|
-
}
|
|
93
|
-
const timeserie = metric.timeseries[0];
|
|
94
|
-
if (!timeserie.data || timeserie.data.length === 0) {
|
|
95
|
-
return null;
|
|
96
|
-
}
|
|
97
|
-
const aggregatedValue = aggregateDataPoints(timeserie.data, aggregation);
|
|
98
|
-
return aggregatedValue;
|
|
99
|
-
}
|
|
100
|
-
catch (error) {
|
|
101
|
-
const logger = getLogger(["savemoney", "azure", "metrics"]);
|
|
102
|
-
logger.error(`Failed to fetch metric ${metricName} for resource ${resourceId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
103
|
-
return null;
|
|
104
|
-
}
|
|
100
|
+
export async function getMetric(monitorClient, resourceId, metricName, aggregation, timespanDays, cache = metricsCache) {
|
|
101
|
+
const key = `${resourceId}|${metricName}|${aggregation}|${timespanDays}`;
|
|
102
|
+
const cached = cache.get(key);
|
|
103
|
+
if (cached !== undefined) {
|
|
104
|
+
return cached;
|
|
105
|
+
}
|
|
106
|
+
const promise = fetchMetric(monitorClient, resourceId, metricName, aggregation, timespanDays);
|
|
107
|
+
cache.set(key, promise);
|
|
108
|
+
return promise;
|
|
105
109
|
}
|
|
106
110
|
/**
|
|
107
111
|
* Returns true if the resource matches ALL specified tag key-value pairs.
|
|
@@ -117,6 +121,15 @@ export function matchesTags(resource, filterTags) {
|
|
|
117
121
|
const resourceTags = resource.tags ?? {};
|
|
118
122
|
return [...filterTags.entries()].every(([key, value]) => resourceTags[key] === value);
|
|
119
123
|
}
|
|
124
|
+
/**
|
|
125
|
+
* Clears the module-scoped fallback metrics cache.
|
|
126
|
+
*
|
|
127
|
+
* Only needed when `getMetric` is called without an explicit `cache`
|
|
128
|
+
* argument. Prefer passing a run-scoped `MetricsCache` instead.
|
|
129
|
+
*/
|
|
130
|
+
export function resetMetricsCache() {
|
|
131
|
+
metricsCache.clear();
|
|
132
|
+
}
|
|
120
133
|
/**
|
|
121
134
|
* Logs a verbose message, optionally with an object.
|
|
122
135
|
*
|
|
@@ -167,4 +180,32 @@ export function verboseLogResourceStart(verbose, resourceName, resourceType) {
|
|
|
167
180
|
logger.debug("=".repeat(80));
|
|
168
181
|
}
|
|
169
182
|
}
|
|
183
|
+
async function fetchMetric(monitorClient, resourceId, metricName, aggregation, timespanDays) {
|
|
184
|
+
try {
|
|
185
|
+
const timespan = `P${timespanDays}D`;
|
|
186
|
+
const result = await monitorClient.metrics.list(resourceId, {
|
|
187
|
+
aggregation,
|
|
188
|
+
metricnames: metricName,
|
|
189
|
+
timespan,
|
|
190
|
+
});
|
|
191
|
+
if (result.value.length === 0) {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
const metric = result.value[0];
|
|
195
|
+
if (!metric.timeseries || metric.timeseries.length === 0) {
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
const timeserie = metric.timeseries[0];
|
|
199
|
+
if (!timeserie.data || timeserie.data.length === 0) {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
const aggregatedValue = aggregateDataPoints(timeserie.data, aggregation);
|
|
203
|
+
return aggregatedValue;
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
const logger = getLogger(["savemoney", "azure", "metrics"]);
|
|
207
|
+
logger.error(`Failed to fetch metric ${metricName} for resource ${resourceId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
170
211
|
//# sourceMappingURL=utils.js.map
|
package/dist/azure/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/azure/utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/azure/utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified Finding model — the single representation of a cost-related
|
|
3
|
+
* observation, regardless of its source (custom analyzers, Azure Advisor,
|
|
4
|
+
* future AWS Trusted Advisor, …).
|
|
5
|
+
*
|
|
6
|
+
* Introduced in Phase 0 of the savemoney evolution roadmap. Existing
|
|
7
|
+
* `AnalysisResult`-based analyzers keep working untouched: an adapter
|
|
8
|
+
* (`findingsFromAnalysisResult`) splits the concatenated `reason` string
|
|
9
|
+
* into one `Finding` per sentence, so downstream consumers can already
|
|
10
|
+
* reason in terms of `Finding[]`.
|
|
11
|
+
*/
|
|
12
|
+
import type { CostRisk } from "./types.js";
|
|
13
|
+
/**
|
|
14
|
+
* A single, atomic observation about a resource.
|
|
15
|
+
*
|
|
16
|
+
* One resource can produce multiple findings (e.g. "no tags" + "low CPU").
|
|
17
|
+
* Findings are designed to be deduplicated by `(resourceId, source, code)`.
|
|
18
|
+
*/
|
|
19
|
+
export type Finding = {
|
|
20
|
+
/**
|
|
21
|
+
* Cloud category. Today every custom finding is "cost"; Advisor may
|
|
22
|
+
* surface other categories that we currently ignore.
|
|
23
|
+
*/
|
|
24
|
+
category: FindingCategory;
|
|
25
|
+
/**
|
|
26
|
+
* Stable machine-readable identifier for the kind of finding, e.g.
|
|
27
|
+
* `vm.deallocated`, `disk.unattached`, `advisor.right-size-vm`.
|
|
28
|
+
* Used for deduplication and grouping. Free-form for now to keep the
|
|
29
|
+
* adapter from existing analyzers cheap; can be tightened later.
|
|
30
|
+
*/
|
|
31
|
+
code: string;
|
|
32
|
+
/**
|
|
33
|
+
* Estimated monthly cost that could be recovered by acting on this
|
|
34
|
+
* finding. Populated by Advisor and (in later phases) by the Retail
|
|
35
|
+
* Prices integration. Absent when the analyzer cannot estimate it.
|
|
36
|
+
*/
|
|
37
|
+
estimatedMonthlySavings?: Money;
|
|
38
|
+
/**
|
|
39
|
+
* Free-text, human-readable description. Backward compatible with the
|
|
40
|
+
* legacy `AnalysisResult.reason` field.
|
|
41
|
+
*/
|
|
42
|
+
reason: string;
|
|
43
|
+
/**
|
|
44
|
+
* Optional, machine-friendly hint about how to remediate. For Advisor
|
|
45
|
+
* this typically maps to `shortDescription.solution`.
|
|
46
|
+
*/
|
|
47
|
+
recommendedAction?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Fully qualified Azure / cloud resource ID this finding refers to.
|
|
50
|
+
*/
|
|
51
|
+
resourceId: string;
|
|
52
|
+
/**
|
|
53
|
+
* Cost risk classification. Maps Advisor's `impact` (High/Medium/Low)
|
|
54
|
+
* 1:1 onto the savemoney scale.
|
|
55
|
+
*/
|
|
56
|
+
severity: CostRisk;
|
|
57
|
+
/**
|
|
58
|
+
* Provenance of the finding.
|
|
59
|
+
*/
|
|
60
|
+
source: FindingSource;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Cloud category a finding belongs to. For now we focus on cost, but the
|
|
64
|
+
* model is open to future Advisor categories.
|
|
65
|
+
*/
|
|
66
|
+
export type FindingCategory = "cost" | "operationalExcellence" | "performance" | "reliability" | "security";
|
|
67
|
+
/**
|
|
68
|
+
* Where the finding originated from.
|
|
69
|
+
*
|
|
70
|
+
* - `custom` → emitted by a savemoney analyzer plugin
|
|
71
|
+
* - `advisor` → fetched from Azure Advisor recommendations
|
|
72
|
+
* - `aws` → reserved for future AWS Trusted Advisor / Compute Optimizer
|
|
73
|
+
*/
|
|
74
|
+
export type FindingSource = "advisor" | "aws" | "custom";
|
|
75
|
+
/**
|
|
76
|
+
* Monetary value associated with a finding, when known.
|
|
77
|
+
* Amounts use ISO 4217 currency codes (e.g. "EUR", "USD").
|
|
78
|
+
*/
|
|
79
|
+
export type Money = {
|
|
80
|
+
amount: number;
|
|
81
|
+
currency: string;
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Aggregate view: one resource with the list of findings emitted for it.
|
|
85
|
+
*
|
|
86
|
+
* This is the type future report generators should consume. The current
|
|
87
|
+
* report layer still works on `AzureDetailedResourceReport`; a helper
|
|
88
|
+
* (`legacyReportFromResourceReport`) bridges the two until the report
|
|
89
|
+
* layer is migrated.
|
|
90
|
+
*/
|
|
91
|
+
export type ResourceReport<TResource = unknown> = {
|
|
92
|
+
findings: Finding[];
|
|
93
|
+
resource: TResource;
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Adapter: converts a legacy `AnalysisResult` (single concatenated
|
|
97
|
+
* reason) into a list of `Finding`s, one per sentence.
|
|
98
|
+
*
|
|
99
|
+
* @param resourceId Fully qualified resource ID
|
|
100
|
+
* @param severity Cost risk classification produced by the analyzer
|
|
101
|
+
* @param reason Concatenated reason string (sentences joined by ". ")
|
|
102
|
+
* @param source Provenance (default: "custom")
|
|
103
|
+
* @param code Optional stable identifier for the finding kind
|
|
104
|
+
* (e.g. `"vm.deallocated"`). Defaults to
|
|
105
|
+
* `"custom.unknown"` when omitted.
|
|
106
|
+
*/
|
|
107
|
+
export declare function findingsFromAnalysisResult(args: {
|
|
108
|
+
code?: string;
|
|
109
|
+
reason: string;
|
|
110
|
+
resourceId: string;
|
|
111
|
+
severity: CostRisk;
|
|
112
|
+
source?: FindingSource;
|
|
113
|
+
}): Finding[];
|
|
114
|
+
//# sourceMappingURL=finding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding.d.ts","sourceRoot":"","sources":["../src/finding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C;;;;;GAKG;AACH,MAAM,MAAM,OAAO,GAAG;IACpB;;;OAGG;IACH,QAAQ,EAAE,eAAe,CAAC;IAC1B;;;;;OAKG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,KAAK,CAAC;IAChC;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,QAAQ,EAAE,QAAQ,CAAC;IACnB;;OAEG;IACH,MAAM,EAAE,aAAa,CAAC;CACvB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,eAAe,GACvB,MAAM,GACN,uBAAuB,GACvB,aAAa,GACb,aAAa,GACb,UAAU,CAAC;AAEf;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,KAAK,GAAG,QAAQ,CAAC;AAEzD;;;GAGG;AACH,MAAM,MAAM,KAAK,GAAG;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,CAAC,SAAS,GAAG,OAAO,IAAI;IAChD,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,QAAQ,EAAE,SAAS,CAAC;CACrB,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE;IAC/C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB,GAAG,OAAO,EAAE,CAcZ"}
|
package/dist/finding.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified Finding model — the single representation of a cost-related
|
|
3
|
+
* observation, regardless of its source (custom analyzers, Azure Advisor,
|
|
4
|
+
* future AWS Trusted Advisor, …).
|
|
5
|
+
*
|
|
6
|
+
* Introduced in Phase 0 of the savemoney evolution roadmap. Existing
|
|
7
|
+
* `AnalysisResult`-based analyzers keep working untouched: an adapter
|
|
8
|
+
* (`findingsFromAnalysisResult`) splits the concatenated `reason` string
|
|
9
|
+
* into one `Finding` per sentence, so downstream consumers can already
|
|
10
|
+
* reason in terms of `Finding[]`.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Adapter: converts a legacy `AnalysisResult` (single concatenated
|
|
14
|
+
* reason) into a list of `Finding`s, one per sentence.
|
|
15
|
+
*
|
|
16
|
+
* @param resourceId Fully qualified resource ID
|
|
17
|
+
* @param severity Cost risk classification produced by the analyzer
|
|
18
|
+
* @param reason Concatenated reason string (sentences joined by ". ")
|
|
19
|
+
* @param source Provenance (default: "custom")
|
|
20
|
+
* @param code Optional stable identifier for the finding kind
|
|
21
|
+
* (e.g. `"vm.deallocated"`). Defaults to
|
|
22
|
+
* `"custom.unknown"` when omitted.
|
|
23
|
+
*/
|
|
24
|
+
export function findingsFromAnalysisResult(args) {
|
|
25
|
+
const { code, reason, resourceId, severity, source = "custom" } = args;
|
|
26
|
+
const sentences = splitReasonIntoSentences(reason);
|
|
27
|
+
if (sentences.length === 0) {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
return sentences.map((sentence) => ({
|
|
31
|
+
category: "cost",
|
|
32
|
+
code: code ?? "custom.unknown",
|
|
33
|
+
reason: sentence,
|
|
34
|
+
resourceId,
|
|
35
|
+
severity,
|
|
36
|
+
source,
|
|
37
|
+
}));
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Splits a concatenated reason string (sentences joined by ". ") into
|
|
41
|
+
* individual non-empty sentences. Mirrors the logic already used by the
|
|
42
|
+
* lint reporter so behaviour stays consistent.
|
|
43
|
+
*/
|
|
44
|
+
function splitReasonIntoSentences(reason) {
|
|
45
|
+
return reason
|
|
46
|
+
.split(/\.\s+|\.$/)
|
|
47
|
+
.map((s) => s.trim())
|
|
48
|
+
.filter((s) => s.length > 0)
|
|
49
|
+
.map((s) => (s.endsWith(".") ? s : `${s}.`));
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=finding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding.js","sourceRoot":"","sources":["../src/finding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAgGH;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,0BAA0B,CAAC,IAM1C;IACC,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,QAAQ,EAAE,GAAG,IAAI,CAAC;IACvE,MAAM,SAAS,GAAG,wBAAwB,CAAC,MAAM,CAAC,CAAC;IACnD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QAClC,QAAQ,EAAE,MAAe;QACzB,IAAI,EAAE,IAAI,IAAI,gBAAgB;QAC9B,MAAM,EAAE,QAAQ;QAChB,UAAU;QACV,QAAQ;QACR,MAAM;KACP,CAAC,CAAC,CAAC;AACN,CAAC;AAED;;;;GAIG;AACH,SAAS,wBAAwB,CAAC,MAAc;IAC9C,OAAO,MAAM;SACV,KAAK,CAAC,WAAW,CAAC;SAClB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;SAC3B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;AACjD,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -12,9 +12,12 @@
|
|
|
12
12
|
*
|
|
13
13
|
* This tool does NOT modify, tag, or delete any resources.
|
|
14
14
|
*/
|
|
15
|
-
export type
|
|
15
|
+
export { type Analyzer, type AnalyzerContext, type AzureClients, createDefaultAnalyzers, } from "./azure/analyzers/index.js";
|
|
16
|
+
export type { AzureConfig, AzureSource } from "./azure/types.js";
|
|
16
17
|
import * as azureModule from "./azure/index.js";
|
|
17
18
|
export declare const azure: typeof azureModule;
|
|
19
|
+
export { type MetricsCache, type MonitorClientLike } from "./azure/utils.js";
|
|
20
|
+
export { type Finding, type FindingCategory, findingsFromAnalysisResult, type FindingSource, type Money, type ResourceReport, } from "./finding.js";
|
|
18
21
|
export * from "./types.js";
|
|
19
22
|
import type { AzureConfig } from "./azure/types.js";
|
|
20
23
|
/**
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,sBAAsB,GACvB,MAAM,4BAA4B,CAAC;AAGpC,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGjE,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAC;AAChD,eAAO,MAAM,KAAK,oBAAc,CAAC;AAEjC,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAG7E,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,eAAe,EACpB,0BAA0B,EAC1B,KAAK,aAAa,EAClB,KAAK,KAAK,EACV,KAAK,cAAc,GACpB,MAAM,cAAc,CAAC;AACtB,cAAc,YAAY,CAAC;AAE3B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAIpD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAE1E"}
|
package/dist/index.js
CHANGED
|
@@ -12,9 +12,12 @@
|
|
|
12
12
|
*
|
|
13
13
|
* This tool does NOT modify, tag, or delete any resources.
|
|
14
14
|
*/
|
|
15
|
+
export { createDefaultAnalyzers, } from "./azure/analyzers/index.js";
|
|
15
16
|
// Export Azure module
|
|
16
17
|
import * as azureModule from "./azure/index.js";
|
|
17
18
|
export const azure = azureModule;
|
|
19
|
+
// Phase 0: unified Finding model and analyzer plugin layer
|
|
20
|
+
export { findingsFromAnalysisResult, } from "./finding.js";
|
|
18
21
|
export * from "./types.js";
|
|
19
22
|
import { loadAzureConfig } from "./azure/config.js";
|
|
20
23
|
/**
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAIL,sBAAsB,GACvB,MAAM,4BAA4B,CAAC;AAKpC,sBAAsB;AACtB,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAC;AAChD,MAAM,CAAC,MAAM,KAAK,GAAG,WAAW,CAAC;AAIjC,2DAA2D;AAC3D,OAAO,EAGL,0BAA0B,GAI3B,MAAM,cAAc,CAAC;AACtB,cAAc,YAAY,CAAC;AAI3B,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,UAAmB;IAClD,OAAO,eAAe,CAAC,UAAU,CAAC,CAAC;AACrC,CAAC"}
|
package/dist/schema.d.ts
CHANGED
|
@@ -86,7 +86,12 @@ export declare const ThresholdsSchema: z.ZodObject<{
|
|
|
86
86
|
*/
|
|
87
87
|
export declare const ConfigSchema: z.ZodObject<{
|
|
88
88
|
azure: z.ZodObject<{
|
|
89
|
+
concurrency: z.ZodOptional<z.ZodNumber>;
|
|
89
90
|
preferredLocation: z.ZodDefault<z.ZodString>;
|
|
91
|
+
sources: z.ZodDefault<z.ZodArray<z.ZodEnum<{
|
|
92
|
+
custom: "custom";
|
|
93
|
+
advisor: "advisor";
|
|
94
|
+
}>>>;
|
|
90
95
|
subscriptionIds: z.ZodArray<z.ZodString>;
|
|
91
96
|
thresholds: z.ZodPipe<z.ZodOptional<z.ZodObject<{
|
|
92
97
|
appService: z.ZodPipe<z.ZodOptional<z.ZodObject<{
|
package/dist/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA4DxB,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAqBlB,CAAC;
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA4DxB,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAqBlB,CAAC;AAkCZ;;;GAGG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAAmD,CAAC;AAI7E,2DAA2D;AAC3D,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAElD,wDAAwD;AACxD,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC"}
|
package/dist/schema.js
CHANGED
|
@@ -81,7 +81,21 @@ export const ThresholdsSchema = z
|
|
|
81
81
|
// ── top-level config schema ──────────────────────────────────────────────────
|
|
82
82
|
const AzureSectionSchema = z
|
|
83
83
|
.object({
|
|
84
|
+
/**
|
|
85
|
+
* Maximum number of resources analyzed in parallel within a single
|
|
86
|
+
* subscription. Defaults to 8 when not provided.
|
|
87
|
+
*/
|
|
88
|
+
concurrency: z.number().int().positive().optional(),
|
|
84
89
|
preferredLocation: z.string().default("italynorth"),
|
|
90
|
+
/**
|
|
91
|
+
* Which finding sources to include. Defaults to all known sources.
|
|
92
|
+
* Authors can narrow the run to e.g. `["advisor"]` to fetch only
|
|
93
|
+
* Azure Advisor recommendations, or `["custom"]` to skip Advisor.
|
|
94
|
+
*/
|
|
95
|
+
sources: z
|
|
96
|
+
.array(z.enum(["advisor", "custom"]))
|
|
97
|
+
.nonempty()
|
|
98
|
+
.default(["advisor", "custom"]),
|
|
85
99
|
subscriptionIds: z
|
|
86
100
|
.array(z.string())
|
|
87
101
|
.min(1, "Config file must contain at least one entry in 'azure.subscriptionIds'"),
|