@pagopa/dx-savemoney 0.4.0 → 0.6.1

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.
Files changed (47) hide show
  1. package/README.md +48 -0
  2. package/dist/azure/__tests__/azqr.test.d.ts +13 -0
  3. package/dist/azure/__tests__/azqr.test.d.ts.map +1 -0
  4. package/dist/azure/__tests__/azqr.test.js +177 -0
  5. package/dist/azure/__tests__/azqr.test.js.map +1 -0
  6. package/dist/azure/__tests__/config.test.js +23 -1
  7. package/dist/azure/__tests__/config.test.js.map +1 -1
  8. package/dist/azure/analyzer.d.ts.map +1 -1
  9. package/dist/azure/analyzer.js +32 -4
  10. package/dist/azure/analyzer.js.map +1 -1
  11. package/dist/azure/analyzers/advisor.d.ts.map +1 -1
  12. package/dist/azure/analyzers/advisor.js.map +1 -1
  13. package/dist/azure/azqr.d.ts +136 -0
  14. package/dist/azure/azqr.d.ts.map +1 -0
  15. package/dist/azure/azqr.js +235 -0
  16. package/dist/azure/azqr.js.map +1 -0
  17. package/dist/azure/config.d.ts.map +1 -1
  18. package/dist/azure/config.js +2 -5
  19. package/dist/azure/config.js.map +1 -1
  20. package/dist/azure/resources/public-ip.d.ts.map +1 -1
  21. package/dist/azure/resources/public-ip.js.map +1 -1
  22. package/dist/azure/types.d.ts +13 -3
  23. package/dist/azure/types.d.ts.map +1 -1
  24. package/dist/finding.d.ts +2 -1
  25. package/dist/finding.d.ts.map +1 -1
  26. package/dist/finding.js.map +1 -1
  27. package/dist/index.d.ts +1 -0
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +1 -0
  30. package/dist/index.js.map +1 -1
  31. package/dist/schema.d.ts +6 -1
  32. package/dist/schema.d.ts.map +1 -1
  33. package/dist/schema.js +11 -3
  34. package/dist/schema.js.map +1 -1
  35. package/package.json +8 -8
  36. package/src/azure/__tests__/azqr.test.ts +238 -0
  37. package/src/azure/__tests__/config.test.ts +33 -1
  38. package/src/azure/__tests__/fixtures/full-override.yaml +3 -0
  39. package/src/azure/analyzer.ts +50 -9
  40. package/src/azure/analyzers/advisor.ts +1 -2
  41. package/src/azure/azqr.ts +270 -0
  42. package/src/azure/config.ts +2 -5
  43. package/src/azure/resources/public-ip.ts +1 -2
  44. package/src/azure/types.ts +13 -3
  45. package/src/finding.ts +3 -6
  46. package/src/index.ts +1 -0
  47. package/src/schema.ts +13 -3
@@ -0,0 +1,136 @@
1
+ /**
2
+ * AZQR report ingestion.
3
+ *
4
+ * Parses an AZQR (`azqr scan --json`) report and turns its `impacted`
5
+ * resources into the unified `Finding` model (`source: "azqr"`), keeping only
6
+ * the rows that carry FinOps signal so the SaveMoney output is not flooded with
7
+ * security / reliability / high-availability best-practice noise. Promoted rows
8
+ * fall into two classes (see {@link classifyAzqrRow}):
9
+ *
10
+ * - **cost** (`category: "cost"`): billable waste — rows AZQR categorises as
11
+ * `Cost`, or orphaned resources whose type actually incurs cost (public IPs,
12
+ * NAT gateways, application gateways, …).
13
+ * - **cleanup** (`category: "operationalExcellence"`): orphaned *free* resources
14
+ * (empty subnets, unattached NSGs, orphan API connections, …). They carry no
15
+ * direct cost but are cleanup candidates worth investigating.
16
+ *
17
+ * Orphans are detected via AZQR's `AOR` (Azure Orphan Resources) check source
18
+ * rather than fragile recommendation-text matching.
19
+ *
20
+ * The AZQR CLI masks subscription IDs by default (e.g.
21
+ * `/subscriptions/xxxxxxxx-…/`). Masked IDs do not match the real resource IDs
22
+ * produced by a live scan, so masked findings cannot be merged onto their
23
+ * resources — {@link isAzqrReportMasked} lets the orchestrator warn and advise
24
+ * re-running with `azqr scan --mask=false`.
25
+ *
26
+ * Scope note (CES-2192 / Fase 1): only `impacted` rows are ingested, enriched
27
+ * with `inventory` metadata when available. AZQR `advisor` rows are ignored to
28
+ * avoid duplicating SaveMoney's own Azure Advisor cost query.
29
+ */
30
+ import { z } from "zod";
31
+ import { type Finding } from "../finding.js";
32
+ /** A single `impacted` row from an AZQR JSON report (fields we consume). */
33
+ declare const azqrImpactedRowSchema: z.ZodObject<{
34
+ category: z.ZodOptional<z.ZodString>;
35
+ impact: z.ZodOptional<z.ZodString>;
36
+ learn: z.ZodOptional<z.ZodString>;
37
+ recommendation: z.ZodString;
38
+ recommendationId: z.ZodOptional<z.ZodString>;
39
+ resourceGroup: z.ZodOptional<z.ZodString>;
40
+ resourceId: z.ZodString;
41
+ resourceName: z.ZodOptional<z.ZodString>;
42
+ resourceType: z.ZodOptional<z.ZodString>;
43
+ source: z.ZodOptional<z.ZodString>;
44
+ subscriptionId: z.ZodOptional<z.ZodString>;
45
+ subscriptionName: z.ZodOptional<z.ZodString>;
46
+ }, z.core.$strip>;
47
+ /** A single `inventory` row from an AZQR JSON report (used for enrichment). */
48
+ declare const azqrInventoryRowSchema: z.ZodObject<{
49
+ location: z.ZodOptional<z.ZodString>;
50
+ resourceId: z.ZodString;
51
+ resourceName: z.ZodOptional<z.ZodString>;
52
+ resourceType: z.ZodOptional<z.ZodString>;
53
+ skuName: z.ZodOptional<z.ZodString>;
54
+ skuTier: z.ZodOptional<z.ZodString>;
55
+ subscriptionId: z.ZodOptional<z.ZodString>;
56
+ }, z.core.$strip>;
57
+ /**
58
+ * Lenient top-level schema. AZQR reports carry many sections we do not use
59
+ * (`advisor`, `defender`, `recommendations`, …); unknown keys are stripped and
60
+ * missing arrays default to empty so a partial/older report still parses.
61
+ */
62
+ declare const azqrReportSchema: z.ZodObject<{
63
+ impacted: z.ZodDefault<z.ZodArray<z.ZodObject<{
64
+ category: z.ZodOptional<z.ZodString>;
65
+ impact: z.ZodOptional<z.ZodString>;
66
+ learn: z.ZodOptional<z.ZodString>;
67
+ recommendation: z.ZodString;
68
+ recommendationId: z.ZodOptional<z.ZodString>;
69
+ resourceGroup: z.ZodOptional<z.ZodString>;
70
+ resourceId: z.ZodString;
71
+ resourceName: z.ZodOptional<z.ZodString>;
72
+ resourceType: z.ZodOptional<z.ZodString>;
73
+ source: z.ZodOptional<z.ZodString>;
74
+ subscriptionId: z.ZodOptional<z.ZodString>;
75
+ subscriptionName: z.ZodOptional<z.ZodString>;
76
+ }, z.core.$strip>>>;
77
+ inventory: z.ZodDefault<z.ZodArray<z.ZodObject<{
78
+ location: z.ZodOptional<z.ZodString>;
79
+ resourceId: z.ZodString;
80
+ resourceName: z.ZodOptional<z.ZodString>;
81
+ resourceType: z.ZodOptional<z.ZodString>;
82
+ skuName: z.ZodOptional<z.ZodString>;
83
+ skuTier: z.ZodOptional<z.ZodString>;
84
+ subscriptionId: z.ZodOptional<z.ZodString>;
85
+ }, z.core.$strip>>>;
86
+ }, z.core.$strip>;
87
+ export type AzqrImpactedRow = z.infer<typeof azqrImpactedRowSchema>;
88
+ export type AzqrInventoryRow = z.infer<typeof azqrInventoryRowSchema>;
89
+ export type AzqrReport = z.infer<typeof azqrReportSchema>;
90
+ /** How a promoted AZQR row is classified for reporting. */
91
+ export type AzqrOpportunityKind = "cleanup" | "cost";
92
+ /**
93
+ * Converts the promoted `impacted` rows of an AZQR report into findings.
94
+ * AZQR carries no monetary estimate, so `estimatedMonthlySavings` is left
95
+ * unset. Billable rows render as `cost` opportunities (keeping AZQR's impact as
96
+ * severity); orphaned free resources render as low-severity
97
+ * `operationalExcellence` cleanup candidates. All carry an `[azqr]` badge.
98
+ */
99
+ export declare function azqrImpactedToFindings(report: AzqrReport): Finding[];
100
+ /**
101
+ * Classifies an `impacted` row for FinOps reporting, or returns `null` to drop
102
+ * it as noise.
103
+ *
104
+ * - `"cost"` — AZQR categorises the row as a cost item, or it is an orphaned
105
+ * resource of a billable type (real savings potential).
106
+ * - `"cleanup"` — an orphaned resource of a *free* type (empty subnets,
107
+ * unattached NSGs, orphan API connections, …): no direct cost, but a cleanup
108
+ * candidate worth investigating.
109
+ * - `null` — everything else (security, reliability, high-availability and
110
+ * other best-practice rows) is treated as noise and dropped.
111
+ *
112
+ * Orphans are identified by AZQR's `AOR` check source, not by matching the
113
+ * recommendation text.
114
+ */
115
+ export declare function classifyAzqrRow(row: AzqrImpactedRow): AzqrOpportunityKind | null;
116
+ /**
117
+ * Detects whether the report still carries AZQR's default subscription-ID
118
+ * masking. Masked resource IDs cannot be matched against a live scan, so the
119
+ * caller should advise re-running with `azqr scan --mask=false`.
120
+ */
121
+ export declare function isAzqrReportMasked(report: AzqrReport): boolean;
122
+ /**
123
+ * Reads and parses an AZQR JSON report from disk.
124
+ *
125
+ * @throws Error when the file cannot be read or is not valid AZQR JSON; the
126
+ * underlying failure is preserved as `cause`.
127
+ */
128
+ export declare function loadAzqrReport(filePath: string): Promise<AzqrReport>;
129
+ /**
130
+ * Validates and parses an unknown value as an AZQR report.
131
+ *
132
+ * @throws Error (with the zod issue as `cause`) when the shape is invalid.
133
+ */
134
+ export declare function parseAzqrReport(raw: unknown): AzqrReport;
135
+ export {};
136
+ //# sourceMappingURL=azqr.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"azqr.d.ts","sourceRoot":"","sources":["../../src/azure/azqr.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AAE7C,4EAA4E;AAC5E,QAAA,MAAM,qBAAqB;;;;;;;;;;;;;iBAazB,CAAC;AAEH,+EAA+E;AAC/E,QAAA,MAAM,sBAAsB;;;;;;;;iBAQ1B,CAAC;AAEH;;;;GAIG;AACH,QAAA,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;iBAGpB,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACpE,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AACtE,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAiC1D,2DAA2D;AAC3D,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,MAAM,CAAC;AAQrD;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,EAAE,CA0BpE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,eAAe,GACnB,mBAAmB,GAAG,IAAI,CAS5B;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAM9D;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAoB1E;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,UAAU,CAQxD"}
@@ -0,0 +1,235 @@
1
+ /**
2
+ * AZQR report ingestion.
3
+ *
4
+ * Parses an AZQR (`azqr scan --json`) report and turns its `impacted`
5
+ * resources into the unified `Finding` model (`source: "azqr"`), keeping only
6
+ * the rows that carry FinOps signal so the SaveMoney output is not flooded with
7
+ * security / reliability / high-availability best-practice noise. Promoted rows
8
+ * fall into two classes (see {@link classifyAzqrRow}):
9
+ *
10
+ * - **cost** (`category: "cost"`): billable waste — rows AZQR categorises as
11
+ * `Cost`, or orphaned resources whose type actually incurs cost (public IPs,
12
+ * NAT gateways, application gateways, …).
13
+ * - **cleanup** (`category: "operationalExcellence"`): orphaned *free* resources
14
+ * (empty subnets, unattached NSGs, orphan API connections, …). They carry no
15
+ * direct cost but are cleanup candidates worth investigating.
16
+ *
17
+ * Orphans are detected via AZQR's `AOR` (Azure Orphan Resources) check source
18
+ * rather than fragile recommendation-text matching.
19
+ *
20
+ * The AZQR CLI masks subscription IDs by default (e.g.
21
+ * `/subscriptions/xxxxxxxx-…/`). Masked IDs do not match the real resource IDs
22
+ * produced by a live scan, so masked findings cannot be merged onto their
23
+ * resources — {@link isAzqrReportMasked} lets the orchestrator warn and advise
24
+ * re-running with `azqr scan --mask=false`.
25
+ *
26
+ * Scope note (CES-2192 / Fase 1): only `impacted` rows are ingested, enriched
27
+ * with `inventory` metadata when available. AZQR `advisor` rows are ignored to
28
+ * avoid duplicating SaveMoney's own Azure Advisor cost query.
29
+ */
30
+ import { readFile } from "node:fs/promises";
31
+ import { z } from "zod";
32
+ /** A single `impacted` row from an AZQR JSON report (fields we consume). */
33
+ const azqrImpactedRowSchema = z.object({
34
+ category: z.string().optional(),
35
+ impact: z.string().optional(),
36
+ learn: z.string().optional(),
37
+ recommendation: z.string(),
38
+ recommendationId: z.string().optional(),
39
+ resourceGroup: z.string().optional(),
40
+ resourceId: z.string(),
41
+ resourceName: z.string().optional(),
42
+ resourceType: z.string().optional(),
43
+ source: z.string().optional(),
44
+ subscriptionId: z.string().optional(),
45
+ subscriptionName: z.string().optional(),
46
+ });
47
+ /** A single `inventory` row from an AZQR JSON report (used for enrichment). */
48
+ const azqrInventoryRowSchema = z.object({
49
+ location: z.string().optional(),
50
+ resourceId: z.string(),
51
+ resourceName: z.string().optional(),
52
+ resourceType: z.string().optional(),
53
+ skuName: z.string().optional(),
54
+ skuTier: z.string().optional(),
55
+ subscriptionId: z.string().optional(),
56
+ });
57
+ /**
58
+ * Lenient top-level schema. AZQR reports carry many sections we do not use
59
+ * (`advisor`, `defender`, `recommendations`, …); unknown keys are stripped and
60
+ * missing arrays default to empty so a partial/older report still parses.
61
+ */
62
+ const azqrReportSchema = z.object({
63
+ impacted: z.array(azqrImpactedRowSchema).default([]),
64
+ inventory: z.array(azqrInventoryRowSchema).default([]),
65
+ });
66
+ /**
67
+ * Azure resource types whose orphaned/unassociated instances actually incur
68
+ * cost. AZQR's `AOR` (Azure Orphan Resources) check mixes these billable
69
+ * resources with free config-hygiene ones (NSGs, subnets, orphan API
70
+ * connections, private endpoints, …): the former are classified as `cost`
71
+ * opportunities, the latter as `cleanup` candidates.
72
+ */
73
+ const BILLABLE_ORPHAN_RESOURCE_TYPES = new Set([
74
+ "microsoft.compute/disks",
75
+ "microsoft.compute/snapshots",
76
+ "microsoft.network/applicationgateways",
77
+ "microsoft.network/ddosprotectionplans",
78
+ "microsoft.network/frontdoorwebapplicationfirewallpolicies",
79
+ "microsoft.network/loadbalancers",
80
+ "microsoft.network/natgateways",
81
+ "microsoft.network/publicipaddresses",
82
+ "microsoft.network/virtualnetworkgateways",
83
+ "microsoft.sql/servers/elasticpools",
84
+ "microsoft.web/serverfarms",
85
+ ]);
86
+ /**
87
+ * AZQR's check source for orphaned resources (the Azure Orphan Resources
88
+ * project). Every `impacted` row tagged with it is a provisioned-but-unused
89
+ * resource, so it is a reliable, text-independent orphan marker.
90
+ */
91
+ const ORPHAN_CHECK_SOURCE = "aor";
92
+ /** AZQR masks subscription GUID segments with runs of `x`. */
93
+ const MASK_MARKER = "xxxxxxxx";
94
+ const COST_REMEDIATION = "Review the resource and remove or right-size it if it is no longer needed to stop incurring cost.";
95
+ const CLEANUP_REMEDIATION = "Orphaned resource with no direct cost. Verify it is unused, then remove it to reduce clutter and management overhead.";
96
+ /**
97
+ * Converts the promoted `impacted` rows of an AZQR report into findings.
98
+ * AZQR carries no monetary estimate, so `estimatedMonthlySavings` is left
99
+ * unset. Billable rows render as `cost` opportunities (keeping AZQR's impact as
100
+ * severity); orphaned free resources render as low-severity
101
+ * `operationalExcellence` cleanup candidates. All carry an `[azqr]` badge.
102
+ */
103
+ export function azqrImpactedToFindings(report) {
104
+ const inventoryById = new Map(report.inventory.map((row) => [row.resourceId.toLowerCase(), row]));
105
+ const findings = [];
106
+ for (const row of report.impacted) {
107
+ const kind = classifyAzqrRow(row);
108
+ if (kind === null)
109
+ continue;
110
+ const inventory = inventoryById.get(row.resourceId.toLowerCase());
111
+ const cleanup = kind === "cleanup";
112
+ const remediation = cleanup ? CLEANUP_REMEDIATION : COST_REMEDIATION;
113
+ findings.push({
114
+ category: cleanup ? "operationalExcellence" : "cost",
115
+ code: row.recommendationId
116
+ ? `azqr.${row.recommendationId}`
117
+ : "azqr.impacted",
118
+ reason: buildReason(row, inventory),
119
+ recommendedAction: row.learn
120
+ ? `${remediation} Learn more: ${row.learn}`
121
+ : remediation,
122
+ resourceId: row.resourceId,
123
+ severity: cleanup ? "low" : mapAzqrImpact(row.impact),
124
+ source: "azqr",
125
+ });
126
+ }
127
+ return findings;
128
+ }
129
+ /**
130
+ * Classifies an `impacted` row for FinOps reporting, or returns `null` to drop
131
+ * it as noise.
132
+ *
133
+ * - `"cost"` — AZQR categorises the row as a cost item, or it is an orphaned
134
+ * resource of a billable type (real savings potential).
135
+ * - `"cleanup"` — an orphaned resource of a *free* type (empty subnets,
136
+ * unattached NSGs, orphan API connections, …): no direct cost, but a cleanup
137
+ * candidate worth investigating.
138
+ * - `null` — everything else (security, reliability, high-availability and
139
+ * other best-practice rows) is treated as noise and dropped.
140
+ *
141
+ * Orphans are identified by AZQR's `AOR` check source, not by matching the
142
+ * recommendation text.
143
+ */
144
+ export function classifyAzqrRow(row) {
145
+ if ((row.category ?? "").toLowerCase().includes("cost")) {
146
+ return "cost";
147
+ }
148
+ if ((row.source ?? "").toLowerCase() !== ORPHAN_CHECK_SOURCE) {
149
+ return null;
150
+ }
151
+ const resourceType = (row.resourceType ?? "").toLowerCase();
152
+ return BILLABLE_ORPHAN_RESOURCE_TYPES.has(resourceType) ? "cost" : "cleanup";
153
+ }
154
+ /**
155
+ * Detects whether the report still carries AZQR's default subscription-ID
156
+ * masking. Masked resource IDs cannot be matched against a live scan, so the
157
+ * caller should advise re-running with `azqr scan --mask=false`.
158
+ */
159
+ export function isAzqrReportMasked(report) {
160
+ return report.impacted.some((row) => row.resourceId.includes(MASK_MARKER) ||
161
+ (row.subscriptionId?.includes(MASK_MARKER) ?? false));
162
+ }
163
+ /**
164
+ * Reads and parses an AZQR JSON report from disk.
165
+ *
166
+ * @throws Error when the file cannot be read or is not valid AZQR JSON; the
167
+ * underlying failure is preserved as `cause`.
168
+ */
169
+ export async function loadAzqrReport(filePath) {
170
+ let content;
171
+ try {
172
+ content = await readFile(filePath, "utf8");
173
+ }
174
+ catch (error) {
175
+ throw new Error(`Cannot read AZQR report at "${filePath}"`, {
176
+ cause: error,
177
+ });
178
+ }
179
+ let json;
180
+ try {
181
+ json = JSON.parse(content);
182
+ }
183
+ catch (error) {
184
+ throw new Error(`AZQR report at "${filePath}" is not valid JSON`, {
185
+ cause: error,
186
+ });
187
+ }
188
+ return parseAzqrReport(json);
189
+ }
190
+ /**
191
+ * Validates and parses an unknown value as an AZQR report.
192
+ *
193
+ * @throws Error (with the zod issue as `cause`) when the shape is invalid.
194
+ */
195
+ export function parseAzqrReport(raw) {
196
+ const result = azqrReportSchema.safeParse(raw);
197
+ if (!result.success) {
198
+ throw new Error(`Invalid AZQR report: ${result.error.message}`, {
199
+ cause: result.error,
200
+ });
201
+ }
202
+ return result.data;
203
+ }
204
+ /**
205
+ * Builds the finding reason from the AZQR recommendation, appending SKU / tier
206
+ * / location context from the matching `inventory` row when available.
207
+ */
208
+ function buildReason(row, inventory) {
209
+ const base = row.recommendation.trim();
210
+ const sentence = base.endsWith(".") ? base : `${base}.`;
211
+ const context = [];
212
+ if (inventory?.skuName)
213
+ context.push(inventory.skuName);
214
+ if (inventory?.skuTier && inventory.skuTier !== inventory.skuName) {
215
+ context.push(inventory.skuTier);
216
+ }
217
+ if (inventory?.location)
218
+ context.push(inventory.location);
219
+ return context.length > 0 ? `${sentence} (${context.join(", ")})` : sentence;
220
+ }
221
+ /**
222
+ * Maps AZQR/APRL `impact` (`High` | `Medium` | `Low`) onto the SaveMoney
223
+ * `CostRisk` scale, defaulting to `low` for unknown values.
224
+ */
225
+ function mapAzqrImpact(impact) {
226
+ switch (impact?.toLowerCase()) {
227
+ case "high":
228
+ return "high";
229
+ case "medium":
230
+ return "medium";
231
+ default:
232
+ return "low";
233
+ }
234
+ }
235
+ //# sourceMappingURL=azqr.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"azqr.js","sourceRoot":"","sources":["../../src/azure/azqr.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,4EAA4E;AAC5E,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IACrC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE;IAC1B,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACvC,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACpC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACnC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACnC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACrC,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACxC,CAAC,CAAC;AAEH,+EAA+E;AAC/E,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACnC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACnC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC9B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC9B,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACtC,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChC,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IACpD,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;CACvD,CAAC,CAAC;AAMH;;;;;;GAMG;AACH,MAAM,8BAA8B,GAAwB,IAAI,GAAG,CAAC;IAClE,yBAAyB;IACzB,6BAA6B;IAC7B,uCAAuC;IACvC,uCAAuC;IACvC,2DAA2D;IAC3D,iCAAiC;IACjC,+BAA+B;IAC/B,qCAAqC;IACrC,0CAA0C;IAC1C,oCAAoC;IACpC,2BAA2B;CAC5B,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,mBAAmB,GAAG,KAAK,CAAC;AAElC,8DAA8D;AAC9D,MAAM,WAAW,GAAG,UAAU,CAAC;AAK/B,MAAM,gBAAgB,GACpB,mGAAmG,CAAC;AAEtG,MAAM,mBAAmB,GACvB,uHAAuH,CAAC;AAE1H;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAAkB;IACvD,MAAM,aAAa,GAAG,IAAI,GAAG,CAC3B,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,WAAW,EAAE,EAAE,GAAG,CAAC,CAAC,CACnE,CAAC;IACF,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,IAAI,KAAK,IAAI;YAAE,SAAS;QAC5B,MAAM,SAAS,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC;QAClE,MAAM,OAAO,GAAG,IAAI,KAAK,SAAS,CAAC;QACnC,MAAM,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,gBAAgB,CAAC;QACrE,QAAQ,CAAC,IAAI,CAAC;YACZ,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,MAAM;YACpD,IAAI,EAAE,GAAG,CAAC,gBAAgB;gBACxB,CAAC,CAAC,QAAQ,GAAG,CAAC,gBAAgB,EAAE;gBAChC,CAAC,CAAC,eAAe;YACnB,MAAM,EAAE,WAAW,CAAC,GAAG,EAAE,SAAS,CAAC;YACnC,iBAAiB,EAAE,GAAG,CAAC,KAAK;gBAC1B,CAAC,CAAC,GAAG,WAAW,gBAAgB,GAAG,CAAC,KAAK,EAAE;gBAC3C,CAAC,CAAC,WAAW;YACf,UAAU,EAAE,GAAG,CAAC,UAAU;YAC1B,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC;YACrD,MAAM,EAAE,MAAM;SACf,CAAC,CAAC;IACL,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,eAAe,CAC7B,GAAoB;IAEpB,IAAI,CAAC,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACxD,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,KAAK,mBAAmB,EAAE,CAAC;QAC7D,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,YAAY,GAAG,CAAC,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IAC5D,OAAO,8BAA8B,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AAC/E,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAkB;IACnD,OAAO,MAAM,CAAC,QAAQ,CAAC,IAAI,CACzB,CAAC,GAAG,EAAE,EAAE,CACN,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,WAAW,CAAC;QACpC,CAAC,GAAG,CAAC,cAAc,EAAE,QAAQ,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC,CACvD,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,QAAgB;IACnD,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC7C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,+BAA+B,QAAQ,GAAG,EAAE;YAC1D,KAAK,EAAE,KAAK;SACb,CAAC,CAAC;IACL,CAAC;IAED,IAAI,IAAa,CAAC;IAClB,IAAI,CAAC;QACH,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,mBAAmB,QAAQ,qBAAqB,EAAE;YAChE,KAAK,EAAE,KAAK;SACb,CAAC,CAAC;IACL,CAAC;IAED,OAAO,eAAe,CAAC,IAAI,CAAC,CAAC;AAC/B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,GAAY;IAC1C,MAAM,MAAM,GAAG,gBAAgB,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IAC/C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,wBAAwB,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE;YAC9D,KAAK,EAAE,MAAM,CAAC,KAAK;SACpB,CAAC,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC;AAED;;;GAGG;AACH,SAAS,WAAW,CAClB,GAAoB,EACpB,SAAuC;IAEvC,MAAM,IAAI,GAAG,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;IACvC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC;IACxD,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,IAAI,SAAS,EAAE,OAAO;QAAE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACxD,IAAI,SAAS,EAAE,OAAO,IAAI,SAAS,CAAC,OAAO,KAAK,SAAS,CAAC,OAAO,EAAE,CAAC;QAClE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC;IACD,IAAI,SAAS,EAAE,QAAQ;QAAE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC1D,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC;AAC/E,CAAC;AAED;;;GAGG;AACH,SAAS,aAAa,CAAC,MAA0B;IAC/C,QAAQ,MAAM,EAAE,WAAW,EAAE,EAAE,CAAC;QAC9B,KAAK,MAAM;YACT,OAAO,MAAM,CAAC;QAChB,KAAK,QAAQ;YACX,OAAO,QAAQ,CAAC;QAClB;YACE,OAAO,KAAK,CAAC;IACjB,CAAC;AACH,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/azure/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAQH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAI9C;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,eAAe,CACnC,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,WAAW,CAAC,CA4CtB;AAED;;;;;GAKG;AACH,wBAAsB,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAW9D"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/azure/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAQH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAI9C;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,eAAe,CACnC,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,WAAW,CAAC,CAyCtB;AAED;;;;;GAKG;AACH,wBAAsB,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAW9D"}
@@ -35,6 +35,7 @@ export async function loadAzureConfig(configPath) {
35
35
  const rawYaml = yaml.load(raw);
36
36
  const parsed = ConfigSchema.parse(rawYaml);
37
37
  return {
38
+ azqrReportPath: parsed.azure.azqrReportPath,
38
39
  concurrency: parsed.azure.concurrency,
39
40
  preferredLocation: parsed.azure.preferredLocation,
40
41
  sources: parsed.azure.sources,
@@ -57,11 +58,7 @@ export async function loadAzureConfig(configPath) {
57
58
  const subscriptionIds = process.env.ARM_SUBSCRIPTION_ID
58
59
  ? process.env.ARM_SUBSCRIPTION_ID.split(",")
59
60
  : (await prompt("Enter Subscription IDs (comma-separated): ")).split(",");
60
- return {
61
- preferredLocation: "italynorth",
62
- subscriptionIds,
63
- timespanDays: 30,
64
- };
61
+ return ConfigSchema.parse({ azure: { subscriptionIds } }).azure;
65
62
  }
66
63
  /**
67
64
  * Prompts user for input via stdin.
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/azure/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/azure/config.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"public-ip.d.ts","sourceRoot":"","sources":["../../../src/azure/resources/public-ip.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAElE,OAAO,KAAK,YAAY,MAAM,sBAAsB,CAAC;AAGrD,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAGpE,OAAO,EAEL,KAAK,YAAY,EAIlB,MAAM,aAAa,CAAC;AAErB;;;;;;;;;GASG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,YAAY,CAAC,eAAe,EACtC,aAAa,EAAE,uBAAuB,EACtC,aAAa,EAAE,aAAa,EAC5B,YAAY,EAAE,MAAM,EACpB,UAAU,GAAE,UAA+B,EAC3C,OAAO,UAAQ,EACf,KAAK,CAAC,EAAE,YAAY,EACpB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,cAAc,CAAC,CAmFzB"}
1
+ {"version":3,"file":"public-ip.d.ts","sourceRoot":"","sources":["../../../src/azure/resources/public-ip.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAElE,OAAO,KAAK,YAAY,MAAM,sBAAsB,CAAC;AAGrD,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAGpE,OAAO,EAEL,KAAK,YAAY,EAIlB,MAAM,aAAa,CAAC;AAErB;;;;;;;;;GASG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,YAAY,CAAC,eAAe,EACtC,aAAa,EAAE,uBAAuB,EACtC,aAAa,EAAE,aAAa,EAC5B,YAAY,EAAE,MAAM,EACpB,UAAU,GAAE,UAA+B,EAC3C,OAAO,UAAQ,EACf,KAAK,CAAC,EAAE,YAAY,EACpB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,cAAc,CAAC,CAkFzB"}
@@ -1 +1 @@
1
- {"version":3,"file":"public-ip.js","sourceRoot":"","sources":["../../../src/azure/resources/public-ip.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"public-ip.js","sourceRoot":"","sources":["../../../src/azure/resources/public-ip.ts"],"names":[],"mappings":"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"}
@@ -3,11 +3,20 @@
3
3
  */
4
4
  import type * as armResources from "@azure/arm-resources";
5
5
  import type { Finding } from "../finding.js";
6
+ import type { Config } from "../schema.js";
6
7
  import type { AnalysisResult, BaseConfig, CostRisk, Thresholds } from "../types.js";
7
8
  /**
8
9
  * Azure configuration extending base config
9
10
  */
10
11
  export type AzureConfig = BaseConfig & {
12
+ /**
13
+ * Optional path to an AZQR (`azqr scan --json`) report. When set, the
14
+ * orchestrator ingests its `impacted` resources, filters them down to
15
+ * FinOps-relevant opportunities and merges them into the run output as
16
+ * findings with `source: "azqr"`. Can be set via YAML (`azure.azqrReportPath`)
17
+ * or the CLI `--azqr-report` flag, which takes precedence.
18
+ */
19
+ azqrReportPath?: string;
11
20
  /**
12
21
  * Maximum number of resources analyzed in parallel within a single
13
22
  * subscription. Defaults to 8 when not provided. Set to 1 for a fully
@@ -30,9 +39,10 @@ export type AzureConfig = BaseConfig & {
30
39
  * - `"custom"` → enables the per-resource analyzer plugins
31
40
  * - `"advisor"` → enables the Azure Advisor subscription-level analyzer
32
41
  *
33
- * Defaults to `["advisor", "custom"]` when omitted (i.e. all sources).
42
+ * Config loading applies the `["advisor", "custom"]` default, so this list
43
+ * is always defined and contains at least one source.
34
44
  */
35
- sources?: AzureSource[];
45
+ sources: Config["azure"]["sources"];
36
46
  subscriptionIds: string[];
37
47
  /**
38
48
  * Analysis thresholds. Defaults from DEFAULT_THRESHOLDS are used when not provided.
@@ -77,7 +87,7 @@ export type AzureResourceReport = {
77
87
  * Narrowed from `FindingSource` to exclude "aws", which is not a valid
78
88
  * filter for Azure runs and would silently produce an empty report.
79
89
  */
80
- export type AzureSource = "advisor" | "custom";
90
+ export type AzureSource = Config["azure"]["sources"][number];
81
91
  export type PricingConfig = {
82
92
  /**
83
93
  * Runtime switch used by the CLI `--no-pricing` flag. Pricing otherwise
@@ -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,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;;;;OAIG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB;;;;;;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;AAE/C,MAAM,MAAM,aAAa,GAAG;IAC1B;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC"}
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,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EACV,cAAc,EACd,UAAU,EACV,QAAQ,EACR,UAAU,EACX,MAAM,aAAa,CAAC;AAErB;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG;IACrC;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC;;;;OAIG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB;;;;;;;OAOG;IACH,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC;IACpC,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,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7D,MAAM,MAAM,aAAa,GAAG;IAC1B;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC"}
package/dist/finding.d.ts CHANGED
@@ -69,9 +69,10 @@ export type FindingCategory = "cost" | "operationalExcellence" | "performance" |
69
69
  *
70
70
  * - `custom` → emitted by a savemoney analyzer plugin
71
71
  * - `advisor` → fetched from Azure Advisor recommendations
72
+ * - `azqr` → ingested from an AZQR (`azqr scan --json`) report
72
73
  * - `aws` → reserved for future AWS Trusted Advisor / Compute Optimizer
73
74
  */
74
- export type FindingSource = "advisor" | "aws" | "custom";
75
+ export type FindingSource = "advisor" | "aws" | "azqr" | "custom";
75
76
  /**
76
77
  * Monetary value associated with a finding, when known.
77
78
  * Amounts use ISO 4217 currency codes (e.g. "EUR", "USD").
@@ -1 +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,CAqBZ"}
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,GACzB,MAAM,GAAG,uBAAuB,GAAG,aAAa,GAAG,aAAa,GAAG,UAAU,CAAC;AAEhF;;;;;;;GAOG;AACH,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,CAAC;AAElE;;;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,CAqBZ"}
@@ -1 +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,sEAAsE;IACtE,qEAAqE;IACrE,oEAAoE;IACpE,oEAAoE;IACpE,kEAAkE;IAClE,8DAA8D;IAC9D,qBAAqB;IACrB,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"}
1
+ {"version":3,"file":"finding.js","sourceRoot":"","sources":["../src/finding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AA6FH;;;;;;;;;;;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,sEAAsE;IACtE,qEAAqE;IACrE,oEAAoE;IACpE,oEAAoE;IACpE,kEAAkE;IAClE,8DAA8D;IAC9D,qBAAqB;IACrB,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
@@ -18,6 +18,7 @@ export { type MetricsCache, type MonitorClientLike } from "./azure/utils.js";
18
18
  import * as azureModule from "./azure/index.js";
19
19
  export declare const azure: typeof azureModule;
20
20
  export { type Finding, type FindingCategory, findingsFromAnalysisResult, type FindingSource, type Money, type ResourceReport, } from "./finding.js";
21
+ export { AZURE_SOURCE_VALUES } from "./schema.js";
21
22
  export * from "./types.js";
22
23
  import type { AzureConfig } from "./azure/types.js";
23
24
  /**
@@ -1 +1 @@
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,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAChF,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAG7E,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAC;AAChD,eAAO,MAAM,KAAK,oBAAc,CAAC;AAGjC,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,eAAe,EACpB,0BAA0B,EAC1B,KAAK,aAAa,EAClB,KAAK,KAAK,EACV,KAAK,cAAc,GACpB,MAAM,cAAc,CAAC;AAEtB,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"}
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,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAChF,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAG7E,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAC;AAChD,eAAO,MAAM,KAAK,oBAAc,CAAC;AAGjC,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,eAAe,EACpB,0BAA0B,EAC1B,KAAK,aAAa,EAClB,KAAK,KAAK,EACV,KAAK,cAAc,GACpB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAElD,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
@@ -18,6 +18,7 @@ import * as azureModule from "./azure/index.js";
18
18
  export const azure = azureModule;
19
19
  // Phase 0: unified Finding model and analyzer plugin layer
20
20
  export { findingsFromAnalysisResult, } from "./finding.js";
21
+ export { AZURE_SOURCE_VALUES } from "./schema.js";
21
22
  export * from "./types.js";
22
23
  import { loadAzureConfig } from "./azure/config.js";
23
24
  /**
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAIL,sBAAsB,GACvB,MAAM,4BAA4B,CAAC;AAMpC,sBAAsB;AACtB,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAC;AAChD,MAAM,CAAC,MAAM,KAAK,GAAG,WAAW,CAAC;AAEjC,2DAA2D;AAC3D,OAAO,EAGL,0BAA0B,GAI3B,MAAM,cAAc,CAAC;AAEtB,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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAIL,sBAAsB,GACvB,MAAM,4BAA4B,CAAC;AAMpC,sBAAsB;AACtB,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAC;AAChD,MAAM,CAAC,MAAM,KAAK,GAAG,WAAW,CAAC;AAEjC,2DAA2D;AAC3D,OAAO,EAGL,0BAA0B,GAI3B,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAElD,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
@@ -80,17 +80,22 @@ export declare const ThresholdsSchema: z.ZodObject<{
80
80
  networkInBytesPerDay: number;
81
81
  } | undefined>>;
82
82
  }, z.core.$strict>;
83
+ export declare const AZURE_SOURCE_VALUES: readonly ["advisor", "custom"];
83
84
  /**
84
85
  * Single Zod schema representing the entire YAML configuration file.
85
86
  * Use `ConfigSchema.parse(rawYaml)` to validate and apply defaults.
86
87
  */
87
88
  export declare const ConfigSchema: z.ZodObject<{
88
89
  azure: z.ZodObject<{
90
+ azqrReportPath: z.ZodOptional<z.ZodString>;
89
91
  concurrency: z.ZodOptional<z.ZodNumber>;
90
92
  preferredLocation: z.ZodDefault<z.ZodString>;
91
- sources: z.ZodDefault<z.ZodArray<z.ZodEnum<{
93
+ sources: z.ZodDefault<z.ZodTuple<[z.ZodEnum<{
94
+ advisor: "advisor";
92
95
  custom: "custom";
96
+ }>], z.ZodEnum<{
93
97
  advisor: "advisor";
98
+ custom: "custom";
94
99
  }>>>;
95
100
  subscriptionIds: z.ZodArray<z.ZodString>;
96
101
  thresholds: z.ZodPipe<z.ZodOptional<z.ZodObject<{
@@ -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;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"}
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;AAIZ,eAAO,MAAM,mBAAmB,gCAAiC,CAAC;AAwClE;;;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
@@ -79,8 +79,16 @@ export const ThresholdsSchema = z
79
79
  })
80
80
  .strict();
81
81
  // ── top-level config schema ──────────────────────────────────────────────────
82
+ export const AZURE_SOURCE_VALUES = ["advisor", "custom"];
83
+ const AzureSourceSchema = z.enum(AZURE_SOURCE_VALUES);
82
84
  const AzureSectionSchema = z
83
85
  .object({
86
+ /**
87
+ * Optional path to an AZQR (`azqr scan --json`) report. When set, its
88
+ * FinOps-relevant impacted resources are merged into the analysis. The
89
+ * `--azqr-report` CLI flag, when provided, overrides this value.
90
+ */
91
+ azqrReportPath: z.string().optional(),
84
92
  /**
85
93
  * Maximum number of resources analyzed in parallel within a single
86
94
  * subscription. Defaults to 8 when not provided.
@@ -93,9 +101,9 @@ const AzureSectionSchema = z
93
101
  * Azure Advisor recommendations, or `["custom"]` to skip Advisor.
94
102
  */
95
103
  sources: z
96
- .array(z.enum(["advisor", "custom"]))
97
- .nonempty()
98
- .default(["advisor", "custom"]),
104
+ .tuple([AzureSourceSchema])
105
+ .rest(AzureSourceSchema)
106
+ .default([...AZURE_SOURCE_VALUES]),
99
107
  subscriptionIds: z
100
108
  .array(z.string())
101
109
  .min(1, "Config file must contain at least one entry in 'azure.subscriptionIds'"),