@spottoai/types-package 1.0.2-beta.354 → 1.0.2-beta.357
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 +37 -0
- package/dist/aws/commitments-planning.d.ts +3 -0
- package/dist/aws/commitments-planning.d.ts.map +1 -0
- package/dist/aws/commitments-planning.js +6 -0
- package/dist/aws/commitments-planning.js.map +1 -0
- package/dist/aws/relationships.d.ts +4 -0
- package/dist/aws/relationships.d.ts.map +1 -0
- package/dist/aws/relationships.js +6 -0
- package/dist/aws/relationships.js.map +1 -0
- package/dist/azure/billingArtifactCanonicalization.d.ts +28 -0
- package/dist/azure/billingArtifactCanonicalization.d.ts.map +1 -0
- package/dist/azure/billingArtifactCanonicalization.js +291 -0
- package/dist/azure/billingArtifactCanonicalization.js.map +1 -0
- package/dist/azure/billingArtifactEvidence.d.ts +39 -0
- package/dist/azure/billingArtifactEvidence.d.ts.map +1 -0
- package/dist/azure/billingArtifactEvidence.js +29 -0
- package/dist/azure/billingArtifactEvidence.js.map +1 -0
- package/dist/azure/billingArtifactGeneration.d.ts +114 -0
- package/dist/azure/billingArtifactGeneration.d.ts.map +1 -0
- package/dist/azure/billingArtifactGeneration.js +238 -0
- package/dist/azure/billingArtifactGeneration.js.map +1 -0
- package/dist/azure/billingPlots.d.ts +22 -0
- package/dist/azure/billingPlots.d.ts.map +1 -1
- package/dist/azure/billingPlots.js +186 -0
- package/dist/azure/billingPlots.js.map +1 -1
- package/dist/azure/views.d.ts +62 -1
- package/dist/azure/views.d.ts.map +1 -1
- package/dist/azure/views.js +152 -1
- package/dist/azure/views.js.map +1 -1
- package/dist/common/artifactControlData.d.ts +12 -0
- package/dist/common/artifactControlData.d.ts.map +1 -0
- package/dist/common/artifactControlData.js +102 -0
- package/dist/common/artifactControlData.js.map +1 -0
- package/dist/common/artifactEvidence.d.ts +144 -0
- package/dist/common/artifactEvidence.d.ts.map +1 -0
- package/dist/common/artifactEvidence.js +181 -0
- package/dist/common/artifactEvidence.js.map +1 -0
- package/dist/common/artifactEvidenceValidation.d.ts +16 -0
- package/dist/common/artifactEvidenceValidation.d.ts.map +1 -0
- package/dist/common/artifactEvidenceValidation.js +31 -0
- package/dist/common/artifactEvidenceValidation.js.map +1 -0
- package/dist/common/artifactGeneration.d.ts +4 -2
- package/dist/common/artifactGeneration.d.ts.map +1 -1
- package/dist/common/artifactGeneration.js.map +1 -1
- package/dist/common/capabilityPassport.d.ts +6 -11
- package/dist/common/capabilityPassport.d.ts.map +1 -1
- package/dist/common/capabilityPassport.js +2 -10
- package/dist/common/capabilityPassport.js.map +1 -1
- package/dist/esm/accounts/accounts.js +115 -0
- package/dist/esm/accounts/azureSpSetup.js +1 -0
- package/dist/esm/accounts/azureSpSetupDurable.js +1 -0
- package/dist/esm/accounts/index.js +5 -0
- package/dist/esm/accounts/readPermissions.js +198 -0
- package/dist/esm/accounts/validation.js +1 -0
- package/dist/esm/accounts/writePermissions.js +43 -0
- package/dist/esm/ai/index.js +2 -0
- package/dist/esm/assessments/artifact.js +1 -0
- package/dist/esm/assessments/catalogue.js +12 -0
- package/dist/esm/assessments/common.js +1 -0
- package/dist/esm/assessments/evidence.js +1 -0
- package/dist/esm/assessments/export.js +1 -0
- package/dist/esm/assessments/index.js +10 -0
- package/dist/esm/assessments/queue.js +2 -0
- package/dist/esm/assessments/review.js +1 -0
- package/dist/esm/assessments/run.js +1 -0
- package/dist/esm/assessments/sow.js +1 -0
- package/dist/esm/assessments/status.js +23 -0
- package/dist/esm/aws/commitments-planning.js +1 -0
- package/dist/esm/aws/commitmentsPlanningValidation.js +121 -0
- package/dist/esm/aws/estates.js +21 -0
- package/dist/esm/aws/index.js +18 -0
- package/dist/esm/aws/onboarding.js +1 -0
- package/dist/esm/aws/pluginPublicArtifactBodyValidation.js +217 -0
- package/dist/esm/aws/pluginPublicArtifactEvidence.js +1 -0
- package/dist/esm/aws/pluginPublicArtifactManifestValidation.js +255 -0
- package/dist/esm/aws/pluginPublicArtifactSectionValidation.js +464 -0
- package/dist/esm/aws/pluginPublicArtifactValidation.js +2 -0
- package/dist/esm/aws/pluginPublicArtifactValidationHelpers.js +216 -0
- package/dist/esm/aws/pluginPublicArtifacts.js +109 -0
- package/dist/esm/aws/portalCommitmentsPlanningPublicArtifactValidation.js +509 -0
- package/dist/esm/aws/portalCommitmentsPlanningPublicArtifacts.js +1 -0
- package/dist/esm/aws/portalPublicArtifactCurrentValidation.js +328 -0
- package/dist/esm/aws/portalPublicArtifactEvidenceValidation.js +294 -0
- package/dist/esm/aws/portalPublicArtifactHistoryAiValidation.js +368 -0
- package/dist/esm/aws/portalPublicArtifactHistoryTypes.js +40 -0
- package/dist/esm/aws/portalPublicArtifactNestedEvidence.js +1 -0
- package/dist/esm/aws/portalPublicArtifactNestedEvidenceValidation.js +397 -0
- package/dist/esm/aws/portalPublicArtifactValidation.js +2 -0
- package/dist/esm/aws/portalPublicArtifactValidationCommon.js +139 -0
- package/dist/esm/aws/portalPublicArtifacts.js +73 -0
- package/dist/esm/aws/portalRelationshipPublicArtifactValidation.js +334 -0
- package/dist/esm/aws/portalRelationshipPublicArtifacts.js +59 -0
- package/dist/esm/aws/portalRelationshipResourceNodeTypes.js +1 -0
- package/dist/esm/aws/portalRelationshipResourceNodeValidation.js +368 -0
- package/dist/esm/aws/publicArtifacts.js +13 -0
- package/dist/esm/aws/relationships.js +1 -0
- package/dist/esm/aws/requests.js +19 -0
- package/dist/esm/azure/activityLogs.js +2 -0
- package/dist/esm/azure/advisorScore.js +1 -0
- package/dist/esm/azure/benefits.js +1 -0
- package/dist/esm/azure/billingArtifactCanonicalization.js +281 -0
- package/dist/esm/azure/billingArtifactEvidence.js +23 -0
- package/dist/esm/azure/billingArtifactGeneration.js +229 -0
- package/dist/esm/azure/billingGeneration.js +1 -0
- package/dist/esm/azure/billingPlots.js +187 -0
- package/dist/esm/azure/budgets.js +1 -0
- package/dist/esm/azure/commitmentsPlanning.js +1 -0
- package/dist/esm/azure/common.js +1 -0
- package/dist/esm/azure/configs.js +1 -0
- package/dist/esm/azure/costComposition.js +1 -0
- package/dist/esm/azure/dataProtection.js +5 -0
- package/dist/esm/azure/governance.js +14 -0
- package/dist/esm/azure/licensing.js +1 -0
- package/dist/esm/azure/metrics.js +1 -0
- package/dist/esm/azure/payloads.js +1 -0
- package/dist/esm/azure/portalArtifacts.js +1 -0
- package/dist/esm/azure/prices.js +1 -0
- package/dist/esm/azure/publicIpAddresses.js +2 -0
- package/dist/esm/azure/recommendationAudit.js +16 -0
- package/dist/esm/azure/recommendationState.js +1 -0
- package/dist/esm/azure/recommendationTracks.js +40 -0
- package/dist/esm/azure/recommendationWorkflow.js +13 -0
- package/dist/esm/azure/recommendations.js +10 -0
- package/dist/esm/azure/regulatoryCompliance.js +6 -0
- package/dist/esm/azure/relationships.js +2 -0
- package/dist/esm/azure/reportingTemplates.js +1 -0
- package/dist/esm/azure/reports.js +12 -0
- package/dist/esm/azure/resourceHealth.js +12 -0
- package/dist/esm/azure/resourceOptimization.js +1 -0
- package/dist/esm/azure/resources.js +1 -0
- package/dist/esm/azure/reviewChecklist.js +1 -0
- package/dist/esm/azure/secureScore.js +1 -0
- package/dist/esm/azure/security.js +1 -0
- package/dist/esm/azure/serviceRetirement.js +1 -0
- package/dist/esm/azure/serviceRetirementState.js +1 -0
- package/dist/esm/azure/serviceRetirementWorkManagement.js +1 -0
- package/dist/esm/azure/storage-inventory.js +1 -0
- package/dist/esm/azure/subscriptions.js +1 -0
- package/dist/esm/azure/views.js +181 -0
- package/dist/esm/azure/workManagement.js +4 -0
- package/dist/esm/common/artifactControlData.js +95 -0
- package/dist/esm/common/artifactEvidence.js +173 -0
- package/dist/esm/common/artifactEvidenceValidation.js +24 -0
- package/dist/esm/common/artifactGeneration.js +1 -0
- package/dist/esm/common/capabilityPassport.js +206 -0
- package/dist/esm/common/index.js +7 -0
- package/dist/esm/common/pagination.js +1 -0
- package/dist/esm/common/provider.js +11 -0
- package/dist/esm/common/providerSyncProgress.js +1 -0
- package/dist/esm/common/relationships.js +1 -0
- package/dist/esm/common/syncProgress.js +1 -0
- package/dist/esm/company/billingAccount.js +1 -0
- package/dist/esm/company/company.js +1 -0
- package/dist/esm/company/companyHierarchy.js +1 -0
- package/dist/esm/company/companyNotes.js +38 -0
- package/dist/esm/company/customProperties.js +1 -0
- package/dist/esm/company/index.js +6 -0
- package/dist/esm/company/notification.js +43 -0
- package/dist/esm/company/survey.js +1 -0
- package/dist/esm/customProperties/customProperties.js +1 -0
- package/dist/esm/customProperties/index.js +1 -0
- package/dist/esm/entries/aws.js +3 -0
- package/dist/esm/entries/commitments-planning.js +3 -0
- package/dist/esm/entries/provider.js +3 -0
- package/dist/esm/entries/recommendations.js +3 -0
- package/dist/esm/entries/relationships.js +3 -0
- package/dist/esm/entries/root.js +3 -0
- package/dist/esm/events/actionGroups.js +1 -0
- package/dist/esm/events/alertDefinitionRun.js +1 -0
- package/dist/esm/events/baseAlert.js +1 -0
- package/dist/esm/events/cost.js +1 -0
- package/dist/esm/events/costAnomalies.js +1 -0
- package/dist/esm/events/costViews.js +1 -0
- package/dist/esm/events/eventStatus.js +1 -0
- package/dist/esm/events/index.js +8 -0
- package/dist/esm/events/quickAlerts.js +2 -0
- package/dist/esm/features-and-permissions/access.js +1 -0
- package/dist/esm/features-and-permissions/index.js +1 -0
- package/dist/esm/feedbacks/feedback.js +1 -0
- package/dist/esm/identity/apikey.js +1 -0
- package/dist/esm/identity/index.js +1 -0
- package/dist/esm/index.js +57 -0
- package/dist/esm/index.vite.js +48 -0
- package/dist/esm/integrations/autotask.js +1 -0
- package/dist/esm/integrations/azureDevOps.js +1 -0
- package/dist/esm/integrations/github.js +1 -0
- package/dist/esm/integrations/halo.js +1 -0
- package/dist/esm/integrations/index.js +5 -0
- package/dist/esm/integrations/integrationTickets.js +1 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/scheduler/index.js +1 -0
- package/dist/esm/scheduler/scheduler.js +1 -0
- package/dist/esm/tags/index.js +1 -0
- package/dist/esm/tags/tags.js +6 -0
- package/dist/esm/unknown/services.js +1 -0
- package/dist/esm/users/companyUser.js +2 -0
- package/dist/esm/users/index.js +2 -0
- package/dist/esm/users/user.js +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/package.json +25 -7
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Billing cost analysis types for Azure cost visualization.
|
|
3
|
+
*/
|
|
4
|
+
import { isArtifactOwnershipBinding, } from '../common/artifactEvidence.js';
|
|
5
|
+
import { containsForbiddenArtifactControlData } from '../common/artifactControlData.js';
|
|
6
|
+
import { isArtifactRevisionVector } from '../common/artifactEvidenceValidation.js';
|
|
7
|
+
import { isBillingCompletedArtifactPublicationDecision, isBillingPartialArtifactPublicationDecision, } from './billingArtifactEvidence.js';
|
|
8
|
+
const BILLING_DOCUMENT_STATES = new Set(['current', 'stale', 'partial', 'fallback', 'complete-empty']);
|
|
9
|
+
const SHA256_PATTERN = /^[a-f0-9]{64}$/;
|
|
10
|
+
const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
11
|
+
const isNonEmptyString = (value) => typeof value === 'string' && value.trim() === value && value.length > 0;
|
|
12
|
+
const isFiniteNumber = (value) => typeof value === 'number' && Number.isFinite(value);
|
|
13
|
+
const isOptionalFiniteNumber = (value) => value === undefined || isFiniteNumber(value);
|
|
14
|
+
const isNullableFiniteNumber = (value) => value === null || isFiniteNumber(value);
|
|
15
|
+
const isNonNegativeInteger = (value) => Number.isSafeInteger(value) && Number(value) >= 0;
|
|
16
|
+
const isPositiveInteger = (value) => Number.isSafeInteger(value) && Number(value) > 0;
|
|
17
|
+
const isStringArray = (value) => Array.isArray(value) && value.every(isNonEmptyString);
|
|
18
|
+
const hasControlCharacters = (value) => Array.from(value).some(character => character.charCodeAt(0) < 32 || character.charCodeAt(0) === 127);
|
|
19
|
+
const isPathSegment = (value) => isNonEmptyString(value) && !/[\\/?#%]/.test(value) && !hasControlCharacters(value) && value !== '.' && value !== '..';
|
|
20
|
+
const publicationDecisionReferencesDigest = (value, digest) => isRecord(value) && Array.isArray(value.dependencies) && value.dependencies.some(dependency => isRecord(dependency) && dependency.digest === digest);
|
|
21
|
+
const isTrend = (value) => isRecord(value) && isNonEmptyString(value.method) && isFiniteNumber(value.slope) && isFiniteNumber(value.intercept);
|
|
22
|
+
const isDailyPoint = (value) => isRecord(value) &&
|
|
23
|
+
isNonEmptyString(value.date) &&
|
|
24
|
+
isFiniteNumber(value.timestamp) &&
|
|
25
|
+
isFiniteNumber(value.cost) &&
|
|
26
|
+
typeof value.isAnomaly === 'boolean' &&
|
|
27
|
+
isNonNegativeInteger(value.anomalyVotes) &&
|
|
28
|
+
isOptionalFiniteNumber(value.trendCost) &&
|
|
29
|
+
(value.anomalyMethods === undefined || isStringArray(value.anomalyMethods));
|
|
30
|
+
const isForecastPoint = (value) => isRecord(value) &&
|
|
31
|
+
isNonEmptyString(value.date) &&
|
|
32
|
+
isFiniteNumber(value.timestamp) &&
|
|
33
|
+
isFiniteNumber(value.cost) &&
|
|
34
|
+
isOptionalFiniteNumber(value.trendCost);
|
|
35
|
+
const isMonthlyPoint = (value) => isRecord(value) &&
|
|
36
|
+
/^\d{4}-\d{2}$/.test(String(value.month)) &&
|
|
37
|
+
isFiniteNumber(value.startDate) &&
|
|
38
|
+
isFiniteNumber(value.endDate) &&
|
|
39
|
+
isFiniteNumber(value.cost) &&
|
|
40
|
+
isFiniteNumber(value.averageDailyCost) &&
|
|
41
|
+
isNonNegativeInteger(value.anomalyCount) &&
|
|
42
|
+
isOptionalFiniteNumber(value.trendCost) &&
|
|
43
|
+
(value.anomalyDates === undefined || (Array.isArray(value.anomalyDates) && value.anomalyDates.every(isFiniteNumber)));
|
|
44
|
+
const hasCommonChartViewFields = (value) => isFiniteNumber(value.startDate) &&
|
|
45
|
+
isFiniteNumber(value.endDate) &&
|
|
46
|
+
isFiniteNumber(value.averageDailyCost) &&
|
|
47
|
+
isFiniteNumber(value.totalCost) &&
|
|
48
|
+
(value.trend === undefined || isTrend(value.trend));
|
|
49
|
+
const isChartView = (value) => {
|
|
50
|
+
if (!isRecord(value) || (value.trend !== undefined && !isTrend(value.trend)))
|
|
51
|
+
return false;
|
|
52
|
+
if (value.forecastMethod !== undefined) {
|
|
53
|
+
return (value.aggregation === 'daily' &&
|
|
54
|
+
isNonEmptyString(value.forecastMethod) &&
|
|
55
|
+
[value.startDate, value.endDate, value.actualTotalCost, value.forecastRemaining, value.forecastMonthTotal].every(isFiniteNumber) &&
|
|
56
|
+
Array.isArray(value.actualPoints) &&
|
|
57
|
+
value.actualPoints.every(isDailyPoint) &&
|
|
58
|
+
Array.isArray(value.forecastPoints) &&
|
|
59
|
+
value.forecastPoints.every(isForecastPoint) &&
|
|
60
|
+
Array.isArray(value.fittedPoints) &&
|
|
61
|
+
value.fittedPoints.every(isForecastPoint));
|
|
62
|
+
}
|
|
63
|
+
if (!hasCommonChartViewFields(value) || !Array.isArray(value.points))
|
|
64
|
+
return false;
|
|
65
|
+
if (value.aggregation === 'daily')
|
|
66
|
+
return value.points.every(isDailyPoint);
|
|
67
|
+
if (value.aggregation === 'monthly')
|
|
68
|
+
return value.points.every(isMonthlyPoint);
|
|
69
|
+
return false;
|
|
70
|
+
};
|
|
71
|
+
const isChartData = (value) => {
|
|
72
|
+
if (!isRecord(value) || !isPositiveInteger(value.schemaVersion) || !isNonEmptyString(value.source))
|
|
73
|
+
return false;
|
|
74
|
+
if (!isRecord(value.dataWindow) ||
|
|
75
|
+
!isFiniteNumber(value.dataWindow.startDate) ||
|
|
76
|
+
!isFiniteNumber(value.dataWindow.endDate) ||
|
|
77
|
+
!isNonNegativeInteger(value.dataWindow.pointCount)) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
if (!isRecord(value.views) || !Object.values(value.views).every(view => view === undefined || isChartView(view)))
|
|
81
|
+
return false;
|
|
82
|
+
if (!isRecord(value.detectors) || !isFiniteNumber(value.detectors.threshold) || !Array.isArray(value.detectors.methods))
|
|
83
|
+
return false;
|
|
84
|
+
return value.detectors.methods.every(method => isRecord(method) &&
|
|
85
|
+
isNonEmptyString(method.name) &&
|
|
86
|
+
(method.status === undefined || isNonEmptyString(method.status)) &&
|
|
87
|
+
(method.error === undefined || method.error === null || typeof method.error === 'string') &&
|
|
88
|
+
Array.isArray(method.triggeredDates) &&
|
|
89
|
+
method.triggeredDates.every(isFiniteNumber));
|
|
90
|
+
};
|
|
91
|
+
const isDriverResource = (value) => isRecord(value) &&
|
|
92
|
+
isNonEmptyString(value.name) &&
|
|
93
|
+
(value.resourceScope === undefined || isNonEmptyString(value.resourceScope)) &&
|
|
94
|
+
(value.resourceId === undefined || isNonEmptyString(value.resourceId)) &&
|
|
95
|
+
(value.isSubscriptionLevel === undefined || typeof value.isSubscriptionLevel === 'boolean') &&
|
|
96
|
+
isFiniteNumber(value.cost) &&
|
|
97
|
+
isNullableFiniteNumber(value.baseline) &&
|
|
98
|
+
isFiniteNumber(value.delta) &&
|
|
99
|
+
isNullableFiniteNumber(value.percentChange) &&
|
|
100
|
+
typeof value.isNew === 'boolean' &&
|
|
101
|
+
isNonEmptyString(value.summary);
|
|
102
|
+
const isAnomaly = (value) => {
|
|
103
|
+
if (!isRecord(value) || !isFiniteNumber(value.date) || !isNonEmptyString(value.summary) || !isNonEmptyString(value.confidence))
|
|
104
|
+
return false;
|
|
105
|
+
if (!isStringArray(value.notes) || !isRecord(value.impact) || !Array.isArray(value.drivers))
|
|
106
|
+
return false;
|
|
107
|
+
const impact = value.impact;
|
|
108
|
+
const impactRequired = ['cost', 'delta', 'monthToDateCost'];
|
|
109
|
+
const impactNullable = [
|
|
110
|
+
'baseline7Day',
|
|
111
|
+
'baseline30Day',
|
|
112
|
+
'percentChange',
|
|
113
|
+
'previousDayCost',
|
|
114
|
+
'previousDayDelta',
|
|
115
|
+
'monthToDateBaseline',
|
|
116
|
+
'monthToDateDelta',
|
|
117
|
+
'monthToDatePercentChange',
|
|
118
|
+
];
|
|
119
|
+
if (!impactRequired.every(field => isFiniteNumber(impact[field])) || !impactNullable.every(field => isNullableFiniteNumber(impact[field]))) {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
return value.drivers.every(driver => isRecord(driver) &&
|
|
123
|
+
isNonEmptyString(driver.type) &&
|
|
124
|
+
isNonEmptyString(driver.name) &&
|
|
125
|
+
isNonEmptyString(driver.summary) &&
|
|
126
|
+
isFiniteNumber(driver.cost) &&
|
|
127
|
+
isFiniteNumber(driver.delta) &&
|
|
128
|
+
isNullableFiniteNumber(driver.baseline) &&
|
|
129
|
+
isNullableFiniteNumber(driver.percentChange) &&
|
|
130
|
+
isFiniteNumber(driver.shareOfImpactPercent) &&
|
|
131
|
+
typeof driver.isNew === 'boolean' &&
|
|
132
|
+
Array.isArray(driver.resources) &&
|
|
133
|
+
driver.resources.every(isDriverResource));
|
|
134
|
+
};
|
|
135
|
+
const hasValidMetadataEvidenceState = (state, evidence, billingGenerationId, inputManifestDigest, chartData, anomalies) => {
|
|
136
|
+
if (state === 'partial')
|
|
137
|
+
return isBillingPartialArtifactPublicationDecision(evidence, billingGenerationId, inputManifestDigest);
|
|
138
|
+
if (!isBillingCompletedArtifactPublicationDecision(evidence, billingGenerationId, inputManifestDigest))
|
|
139
|
+
return false;
|
|
140
|
+
if (state !== 'complete-empty')
|
|
141
|
+
return true;
|
|
142
|
+
const billingHistory = evidence.dependencies.find(dependency => dependency.name === 'billing-history');
|
|
143
|
+
const dataWindow = chartData.dataWindow;
|
|
144
|
+
const views = chartData.views;
|
|
145
|
+
return (billingHistory?.emptyEvidence === 'complete-empty' &&
|
|
146
|
+
billingHistory.acceptedRowCount === 0 &&
|
|
147
|
+
isNonEmptyString(billingHistory.emptyProofRef) &&
|
|
148
|
+
isRecord(dataWindow) &&
|
|
149
|
+
dataWindow.pointCount === 0 &&
|
|
150
|
+
isRecord(views) &&
|
|
151
|
+
Object.values(views).every(view => view === undefined) &&
|
|
152
|
+
anomalies.length === 0);
|
|
153
|
+
};
|
|
154
|
+
/** Dependency-free validator for customer-readable V2 billing metadata. */
|
|
155
|
+
export const isBillingCostAnalysisMetadataV2 = (value) => {
|
|
156
|
+
if (!isRecord(value) || containsForbiddenArtifactControlData(value) || value.schemaVersion !== 2)
|
|
157
|
+
return false;
|
|
158
|
+
if (Object.prototype.hasOwnProperty.call(value, 'outputManifestDigest'))
|
|
159
|
+
return false;
|
|
160
|
+
if (!isPathSegment(value.subscriptionId) || !isPathSegment(value.billingGenerationId))
|
|
161
|
+
return false;
|
|
162
|
+
if (!isArtifactOwnershipBinding(value.ownership) || value.ownership.provider !== 'azure' || value.ownership.accountId !== value.subscriptionId)
|
|
163
|
+
return false;
|
|
164
|
+
if (!isArtifactRevisionVector(value.revision) || value.ownership.ownershipEpochRevision !== value.revision.ownershipEpochRevision) {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
if (typeof value.artifactState !== 'string' || !BILLING_DOCUMENT_STATES.has(value.artifactState))
|
|
168
|
+
return false;
|
|
169
|
+
if (typeof value.inputManifestDigest !== 'string' || !SHA256_PATTERN.test(value.inputManifestDigest))
|
|
170
|
+
return false;
|
|
171
|
+
if (typeof value.outputBindingDigest !== 'string' || !SHA256_PATTERN.test(value.outputBindingDigest))
|
|
172
|
+
return false;
|
|
173
|
+
if (value.outputBindingDigest === value.inputManifestDigest ||
|
|
174
|
+
publicationDecisionReferencesDigest(value.artifactEvidence, value.outputBindingDigest)) {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
if (!isChartData(value.chartData) || !Array.isArray(value.anomalies) || !value.anomalies.every(isAnomaly))
|
|
178
|
+
return false;
|
|
179
|
+
if (!hasValidMetadataEvidenceState(value.artifactState, value.artifactEvidence, value.billingGenerationId, value.inputManifestDigest, value.chartData, value.anomalies)) {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
if (!isNonEmptyString(value.currencyCode) || !isNonEmptyString(value.currencySymbol))
|
|
183
|
+
return false;
|
|
184
|
+
if (value.forecastMethod !== undefined && !isNonEmptyString(value.forecastMethod))
|
|
185
|
+
return false;
|
|
186
|
+
return [value.forecastMonthTotal, value.forecastRemaining, value.forecastPeriodEnd].every(isOptionalFiniteNumber);
|
|
187
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export const DATA_PROTECTION_SCHEMA_VERSION = '2026-06-24';
|
|
2
|
+
export const DATA_PROTECTION_SOURCE = 'Spotto.DataProtectionPosture';
|
|
3
|
+
export const DATA_PROTECTION_PORTAL_FILE = 'data-protection.json.gz';
|
|
4
|
+
export const DATA_PROTECTION_BACKUP_COST_DIAGNOSTICS_FILE = 'data-protection/backup-costs.json.gz';
|
|
5
|
+
export const DATA_PROTECTION_BILLING_SOURCE_FILE = 'history/billing/cost-last-30-days.json';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const GOVERNANCE_REPORT_SCHEMA_VERSION = '2026-05-01.slim-v1';
|
|
2
|
+
export const GOVERNANCE_GRAPH_SCHEMA_VERSION = '2026-05-02.graph-v1';
|
|
3
|
+
export const GOVERNANCE_ACCESS_SCHEMA_VERSION = '2026-05-14.identity-access-v2';
|
|
4
|
+
export const TENANT_GOVERNANCE_REPORT_SCHEMA_VERSION = '2026-05-02.tenant-slim-v1';
|
|
5
|
+
export const TENANT_GOVERNANCE_GRAPH_SCHEMA_VERSION = '2026-05-02.tenant-graph-v1';
|
|
6
|
+
export const GLOBAL_ADMIN_RAW_SCHEMA_VERSION = '2026-06-24.global-admin-raw-v1';
|
|
7
|
+
export const PRIVILEGED_ROLE_SUMMARY_SCHEMA_VERSION = '2026-06-24.privileged-role-summary-v1';
|
|
8
|
+
export const TENANT_GOVERNANCE_ACCESS_SCHEMA_VERSION = '2026-06-24.tenant-governance-access-v1';
|
|
9
|
+
export const GOVERNANCE_REPORT_PORTAL_FILE = 'governance.json.gz';
|
|
10
|
+
export const GOVERNANCE_GRAPH_PORTAL_FILE = 'governance-graph.json.gz';
|
|
11
|
+
export const GOVERNANCE_ACCESS_PORTAL_FILE = 'governance-access.json.gz';
|
|
12
|
+
export const TENANT_GOVERNANCE_REPORT_PORTAL_FILE = 'governance.json.gz';
|
|
13
|
+
export const TENANT_GOVERNANCE_GRAPH_PORTAL_FILE = 'governance-graph.json.gz';
|
|
14
|
+
export const TENANT_GOVERNANCE_ACCESS_PORTAL_FILE = 'governance-access.json.gz';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const AZURE_PORTAL_ARTIFACT_SCHEMA_VERSION = 1;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export const RecommendationAuditRowKinds = {
|
|
2
|
+
resourceTarget: 'resource-view:target',
|
|
3
|
+
subscriptionTarget: 'scope-view:target',
|
|
4
|
+
resourceProviderScopeFeed: 'resource-view:providerScope-feed',
|
|
5
|
+
subscriptionProviderScopeFeed: 'scope-view:providerScope-feed',
|
|
6
|
+
};
|
|
7
|
+
export const RecommendationWorkflowAuditEventTypes = {
|
|
8
|
+
assigned: 'WorkflowAssigned',
|
|
9
|
+
laneChanged: 'WorkflowLaneChanged',
|
|
10
|
+
blocked: 'WorkflowBlocked',
|
|
11
|
+
unblocked: 'WorkflowUnblocked',
|
|
12
|
+
returnedToSuggestions: 'ReturnedToSuggestions',
|
|
13
|
+
repaired: 'WorkflowRepaired',
|
|
14
|
+
sourceChanged: 'WorkflowSourceChanged',
|
|
15
|
+
};
|
|
16
|
+
export const RECOMMENDATION_WORKFLOW_AUDIT_DETAILS_SCHEMA_VERSION = 1;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export const SystemTrackIds = {
|
|
2
|
+
resourceHygiene: 'resource-hygiene',
|
|
3
|
+
capacityRightsizing: 'capacity-rightsizing',
|
|
4
|
+
securityPosture: 'security-posture',
|
|
5
|
+
patchingLifecycle: 'patching-lifecycle',
|
|
6
|
+
commitmentsLicensing: 'commitments-licensing',
|
|
7
|
+
backupRecovery: 'backup-recovery',
|
|
8
|
+
perimeterExposure: 'perimeter-exposure',
|
|
9
|
+
identityPrivilege: 'identity-privilege',
|
|
10
|
+
governanceOwnership: 'governance-ownership',
|
|
11
|
+
spendExceptions: 'spend-exceptions',
|
|
12
|
+
};
|
|
13
|
+
export const RecommendationFocusModes = {
|
|
14
|
+
all: 'all',
|
|
15
|
+
finops: 'finops',
|
|
16
|
+
securityGovernance: 'securityGovernance',
|
|
17
|
+
operationalHealth: 'operationalHealth',
|
|
18
|
+
optimizationDelivery: 'optimizationDelivery',
|
|
19
|
+
serviceDelivery: 'serviceDelivery',
|
|
20
|
+
};
|
|
21
|
+
export const RecommendationTrackFacets = {
|
|
22
|
+
orphaned: 'orphaned',
|
|
23
|
+
unattached: 'unattached',
|
|
24
|
+
idle: 'idle',
|
|
25
|
+
underutilized: 'underutilized',
|
|
26
|
+
unprotected: 'unprotected',
|
|
27
|
+
staleBackup: 'stale-backup',
|
|
28
|
+
expiring: 'expiring',
|
|
29
|
+
unpatched: 'unpatched',
|
|
30
|
+
unsupported: 'unsupported',
|
|
31
|
+
publicIngress: 'public-ingress',
|
|
32
|
+
overprivileged: 'overprivileged',
|
|
33
|
+
missingOwnership: 'missing-ownership',
|
|
34
|
+
missingTags: 'missing-tags',
|
|
35
|
+
costAnomaly: 'cost-anomaly',
|
|
36
|
+
};
|
|
37
|
+
export const SystemTrackCardGranularities = {
|
|
38
|
+
recommendation: 'recommendation',
|
|
39
|
+
resource: 'resource',
|
|
40
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const RecommendationWorkflowLanes = {
|
|
2
|
+
prioritized: 'prioritized',
|
|
3
|
+
todo: 'todo',
|
|
4
|
+
inProgress: 'in-progress',
|
|
5
|
+
blocked: 'blocked',
|
|
6
|
+
completed: 'completed',
|
|
7
|
+
};
|
|
8
|
+
export const RecommendationWorkflowSelectionModes = {
|
|
9
|
+
allAffected: 'all-affected',
|
|
10
|
+
selectedSubset: 'selected-subset',
|
|
11
|
+
subscriptionWide: 'subscription-wide',
|
|
12
|
+
};
|
|
13
|
+
export const RECOMMENDATION_WORKFLOW_REPAIR_PAGE_LIMIT = 250;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export var RecommendationCategory;
|
|
2
|
+
(function (RecommendationCategory) {
|
|
3
|
+
RecommendationCategory["Cost"] = "Cost";
|
|
4
|
+
RecommendationCategory["Performance"] = "Performance";
|
|
5
|
+
RecommendationCategory["Reliability"] = "Reliability";
|
|
6
|
+
RecommendationCategory["Security"] = "Security";
|
|
7
|
+
RecommendationCategory["Compliance"] = "Compliance";
|
|
8
|
+
RecommendationCategory["OperationalExcellence"] = "OperationalExcellence";
|
|
9
|
+
RecommendationCategory["OperationalExcellenceAlternative"] = "Operational Excellence";
|
|
10
|
+
})(RecommendationCategory || (RecommendationCategory = {}));
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export const REGULATORY_COMPLIANCE_REPORT_SCHEMA_VERSION = '2026-07-29.regulatory-compliance-v1';
|
|
2
|
+
export const REGULATORY_COMPLIANCE_ASSIGNMENT_DETAIL_SCHEMA_VERSION = '2026-07-29.regulatory-compliance-assignment-v1';
|
|
3
|
+
export const POLICY_EXEMPTION_COMMAND_SCHEMA_VERSION = '2026-07-29.policy-exemption-command-v1';
|
|
4
|
+
export const REGULATORY_COMPLIANCE_PORTAL_FILE = 'regulatory-compliance.json.gz';
|
|
5
|
+
export const REGULATORY_COMPLIANCE_ASSIGNMENT_DETAIL_DIRECTORY = 'regulatory-compliance/assignments';
|
|
6
|
+
export const REGULATORY_COMPLIANCE_NOTE_TARGET_TYPES = ['assignment', 'control', 'policy'];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const REPORTING_TEMPLATE_REPORT_TYPES = ['sdm', 'sow', 'review-checklist', 'architecture-assessment'];
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Specific reason types for cost changes */
|
|
2
|
+
export var ChangeReasonType;
|
|
3
|
+
(function (ChangeReasonType) {
|
|
4
|
+
ChangeReasonType["NEW_RESOURCE"] = "new_resource";
|
|
5
|
+
ChangeReasonType["REMOVED_RESOURCE"] = "removed_resource";
|
|
6
|
+
ChangeReasonType["QUANTITY_INCREASE"] = "quantity_increase";
|
|
7
|
+
ChangeReasonType["QUANTITY_DECREASE"] = "quantity_decrease";
|
|
8
|
+
ChangeReasonType["RATE_CHANGE"] = "rate_change";
|
|
9
|
+
ChangeReasonType["SKU_CHANGE"] = "sku_change";
|
|
10
|
+
ChangeReasonType["NEW_METER"] = "new_meter";
|
|
11
|
+
ChangeReasonType["REMOVED_METER"] = "removed_meter";
|
|
12
|
+
})(ChangeReasonType || (ChangeReasonType = {}));
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const RESOURCE_HEALTH_EVENTS_FILE_NAME = 'microsoft.resourcehealth-events.json';
|
|
2
|
+
export const RESOURCE_HEALTH_EVENTS_IMPACTED_RESOURCES_FILE_NAME = 'microsoft.resourcehealth-events-impactedresources.json';
|
|
3
|
+
export const RESOURCE_HEALTH_AVAILABILITY_STATUSES_FILE_NAME = 'microsoft.resourcehealth-availabilitystatuses.json';
|
|
4
|
+
export const RESOURCE_HEALTH_EVENTS_PORTAL_FILE_NAME = 'health-events.json';
|
|
5
|
+
export const RESOURCE_HEALTH_AVAILABILITY_STATUSES_PORTAL_FILE_NAME = 'health-availability-statuses.json';
|
|
6
|
+
export const RESOURCE_HEALTH_INDEX_FILE_NAME = 'history/resource-health/index.json';
|
|
7
|
+
export const AZURE_RESOURCE_HEALTH_EVENTS_SOURCE = 'Microsoft.ResourceHealth/events';
|
|
8
|
+
export const AZURE_RESOURCE_HEALTH_IMPACTED_RESOURCES_SOURCE = 'Microsoft.ResourceHealth/events/impactedResources';
|
|
9
|
+
export const AZURE_RESOURCE_HEALTH_AVAILABILITY_STATUSES_SOURCE = 'Microsoft.ResourceHealth/availabilityStatuses';
|
|
10
|
+
export const RESOURCE_HEALTH_EVENTS_SOURCE = AZURE_RESOURCE_HEALTH_EVENTS_SOURCE;
|
|
11
|
+
export const RESOURCE_HEALTH_EVENTS_IMPACTED_RESOURCES_SOURCE = AZURE_RESOURCE_HEALTH_IMPACTED_RESOURCES_SOURCE;
|
|
12
|
+
export const RESOURCE_HEALTH_AVAILABILITY_STATUSES_SOURCE = AZURE_RESOURCE_HEALTH_AVAILABILITY_STATUSES_SOURCE;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { allowedArtifactReferenceField, containsForbiddenArtifactControlData, } from '../common/artifactControlData.js';
|
|
2
|
+
import { isArtifactOwnershipBinding, isArtifactPublicationDecision, isEnforceableArtifactOwnershipBinding, } from '../common/artifactEvidence.js';
|
|
3
|
+
import { isArtifactRevisionVector, isStrictLogicalArtifactReference } from '../common/artifactEvidenceValidation.js';
|
|
4
|
+
const isNonEmptyString = (value) => typeof value === 'string' && value.trim().length > 0;
|
|
5
|
+
const isCanonicalIsoTimestamp = (value) => {
|
|
6
|
+
if (!isNonEmptyString(value))
|
|
7
|
+
return false;
|
|
8
|
+
const timestamp = Date.parse(value);
|
|
9
|
+
return Number.isFinite(timestamp) && new Date(timestamp).toISOString() === value;
|
|
10
|
+
};
|
|
11
|
+
const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
12
|
+
const isLogicalManifestPath = (value) => {
|
|
13
|
+
if (!isNonEmptyString(value))
|
|
14
|
+
return false;
|
|
15
|
+
if (value.startsWith('/') || value.includes('://') || value.includes('?') || value.includes('#') || value.includes('\\'))
|
|
16
|
+
return false;
|
|
17
|
+
const segments = value.split('/');
|
|
18
|
+
return segments.length >= 2 && segments.every(segment => segment.length > 0 && segment !== '.' && segment !== '..');
|
|
19
|
+
};
|
|
20
|
+
const isViewSetSurfaceReference = (value) => isRecord(value) && isNonEmptyString(value.runId) && isLogicalManifestPath(value.manifestPath) && isCanonicalIsoTimestamp(value.completedAt);
|
|
21
|
+
/** Dependency-free rejection boundary for customer-readable cross-surface pointers. */
|
|
22
|
+
export const isCompletedAzureViewSetV1 = (value) => {
|
|
23
|
+
if (!isRecord(value))
|
|
24
|
+
return false;
|
|
25
|
+
if (value.schemaVersion !== 1 || value.status !== 'completed')
|
|
26
|
+
return false;
|
|
27
|
+
if (!isNonEmptyString(value.subscriptionId) || !isNonEmptyString(value.publicationId) || !isCanonicalIsoTimestamp(value.completedAt)) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
if (!isViewSetSurfaceReference(value.portal) || !isViewSetSurfaceReference(value.plugin))
|
|
31
|
+
return false;
|
|
32
|
+
if (!isRecord(value.economics))
|
|
33
|
+
return false;
|
|
34
|
+
return isNonEmptyString(value.economics.generationId) && isNonEmptyString(value.economics.fingerprint);
|
|
35
|
+
};
|
|
36
|
+
const VIEW_CONTENT_ENCODINGS = new Set(['identity', 'gzip']);
|
|
37
|
+
const SHA256_PATTERN = /^[a-f0-9]{64}$/;
|
|
38
|
+
const hasControlCharacters = (value) => Array.from(value).some(character => character.charCodeAt(0) < 32 || character.charCodeAt(0) === 127);
|
|
39
|
+
const isStrictNonEmptyString = (value) => typeof value === 'string' && value.trim() === value && value.length > 0 && !hasControlCharacters(value);
|
|
40
|
+
const isPositiveSafeInteger = (value) => Number.isSafeInteger(value) && Number(value) > 0;
|
|
41
|
+
const isNonNegativeSafeInteger = (value) => Number.isSafeInteger(value) && Number(value) >= 0;
|
|
42
|
+
const isSha256 = (value) => typeof value === 'string' && SHA256_PATTERN.test(value);
|
|
43
|
+
const allowedViewArtifactPaths = (artifacts) => Array.isArray(artifacts) ? artifacts.flatMap(artifact => allowedArtifactReferenceField(artifact, 'path')) : [];
|
|
44
|
+
const isSafePathSegment = (value) => isStrictNonEmptyString(value) && !/[\\/?#%]/.test(value) && value !== '.' && value !== '..';
|
|
45
|
+
const isStrictCanonicalIsoTimestamp = (value) => {
|
|
46
|
+
if (!isStrictNonEmptyString(value))
|
|
47
|
+
return false;
|
|
48
|
+
const timestamp = Date.parse(value);
|
|
49
|
+
return Number.isFinite(timestamp) && new Date(timestamp).toISOString() === value;
|
|
50
|
+
};
|
|
51
|
+
const isEnforceableAzureOwnershipBinding = (value) => isEnforceableArtifactOwnershipBinding(value) && value.provider === 'azure';
|
|
52
|
+
const hasMatchingViewOwnership = (subscriptionId, ownership, revision, enforceable) => {
|
|
53
|
+
if (!isSafePathSegment(subscriptionId) || !isArtifactRevisionVector(revision))
|
|
54
|
+
return false;
|
|
55
|
+
if (!isArtifactOwnershipBinding(ownership))
|
|
56
|
+
return false;
|
|
57
|
+
if (enforceable && !isEnforceableArtifactOwnershipBinding(ownership))
|
|
58
|
+
return false;
|
|
59
|
+
if (ownership.provider !== 'azure' || ownership.accountId !== subscriptionId)
|
|
60
|
+
return false;
|
|
61
|
+
return ownership.ownershipEpochRevision === revision.ownershipEpochRevision;
|
|
62
|
+
};
|
|
63
|
+
const isViewArtifactDescriptor = (value, runId) => isRecord(value) &&
|
|
64
|
+
isStrictLogicalArtifactReference(value.path) &&
|
|
65
|
+
value.path.startsWith(`runs/${runId}/`) &&
|
|
66
|
+
value.path.length > `runs/${runId}/`.length &&
|
|
67
|
+
isStrictNonEmptyString(value.name) &&
|
|
68
|
+
value.mediaType === 'application/json' &&
|
|
69
|
+
typeof value.contentEncoding === 'string' &&
|
|
70
|
+
VIEW_CONTENT_ENCODINGS.has(value.contentEncoding) &&
|
|
71
|
+
isNonNegativeSafeInteger(value.byteLength) &&
|
|
72
|
+
isSha256(value.sha256);
|
|
73
|
+
const hasRequiredViewDependencies = (decision) => {
|
|
74
|
+
const dependencies = new Map(decision.dependencies.map(dependency => [dependency.name, dependency]));
|
|
75
|
+
const billing = dependencies.get('billing');
|
|
76
|
+
const economics = dependencies.get('economics');
|
|
77
|
+
if (!billing || !economics)
|
|
78
|
+
return false;
|
|
79
|
+
for (const dependency of [billing, economics]) {
|
|
80
|
+
if (dependency.publication === 'completed' && (!isStrictNonEmptyString(dependency.generationId) || !isSha256(dependency.digest))) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const completedDependencies = new Set(decision.dependencies.filter(dependency => dependency.publication === 'completed').map(dependency => dependency.name));
|
|
85
|
+
return decision.claims.every(claim => claim.publication !== 'completed' || claim.requiredDependencies.every(dependency => completedDependencies.has(dependency)));
|
|
86
|
+
};
|
|
87
|
+
/** Validates an evidence-aware completed portal or plugin generation manifest. */
|
|
88
|
+
export const isCompletedViewManifestV3 = (value) => {
|
|
89
|
+
if (!isRecord(value) || containsForbiddenArtifactControlData(value, allowedViewArtifactPaths(value.artifacts)))
|
|
90
|
+
return false;
|
|
91
|
+
if (value.schemaVersion !== 3 || value.status !== 'completed')
|
|
92
|
+
return false;
|
|
93
|
+
if (!isSafePathSegment(value.runId) || !hasMatchingViewOwnership(value.subscriptionId, value.ownership, value.revision, false))
|
|
94
|
+
return false;
|
|
95
|
+
if (!isRecord(value.artifactGeneration) ||
|
|
96
|
+
value.artifactGeneration.runId !== value.runId ||
|
|
97
|
+
!isStrictCanonicalIsoTimestamp(value.artifactGeneration.generatedAt)) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
if (!Array.isArray(value.artifacts) || value.artifacts.length === 0)
|
|
101
|
+
return false;
|
|
102
|
+
if (!value.artifacts.every(artifact => isViewArtifactDescriptor(artifact, value.runId)))
|
|
103
|
+
return false;
|
|
104
|
+
if (new Set(value.artifacts.map(artifact => artifact.path)).size !== value.artifacts.length ||
|
|
105
|
+
new Set(value.artifacts.map(artifact => artifact.name)).size !== value.artifacts.length) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
if (!isPositiveSafeInteger(value.requestedArtifactCount) ||
|
|
109
|
+
value.requestedArtifactCount !== value.artifacts.length ||
|
|
110
|
+
!isNonNegativeSafeInteger(value.requestedResourceCount) ||
|
|
111
|
+
value.failedArtifactCount !== 0 ||
|
|
112
|
+
value.failedResourceCount !== 0 ||
|
|
113
|
+
!isSha256(value.compositeDependencyDigest) ||
|
|
114
|
+
!isStrictCanonicalIsoTimestamp(value.completedAt)) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
return isArtifactPublicationDecision(value.publicationDecision) && hasRequiredViewDependencies(value.publicationDecision);
|
|
118
|
+
};
|
|
119
|
+
const hasSameOwnership = (left, right) => left.provider === right.provider &&
|
|
120
|
+
left.tenantId === right.tenantId &&
|
|
121
|
+
left.companyId === right.companyId &&
|
|
122
|
+
left.cloudAccountId === right.cloudAccountId &&
|
|
123
|
+
left.accountId === right.accountId &&
|
|
124
|
+
left.ownershipEpochRevision === right.ownershipEpochRevision;
|
|
125
|
+
const hasSameRevision = (left, right) => left.ownershipEpochRevision === right.ownershipEpochRevision &&
|
|
126
|
+
left.sourceRevision === right.sourceRevision &&
|
|
127
|
+
left.policyRevision === right.policyRevision;
|
|
128
|
+
const isViewSetV2SurfaceReference = (value, surface, subscriptionId, ownership, revision, compositeDependencyDigest) => {
|
|
129
|
+
if (!isRecord(value) || !isSafePathSegment(value.runId))
|
|
130
|
+
return false;
|
|
131
|
+
const expectedManifestName = surface === 'portal' ? 'completed-view-manifest.json' : 'completed-plugin-generation.json';
|
|
132
|
+
if (value.manifestPath !== `runs/${value.runId}/${expectedManifestName}` || !isStrictLogicalArtifactReference(value.manifestPath))
|
|
133
|
+
return false;
|
|
134
|
+
if (!isEnforceableAzureOwnershipBinding(value.ownership) || !isArtifactRevisionVector(value.revision))
|
|
135
|
+
return false;
|
|
136
|
+
if (!hasMatchingViewOwnership(subscriptionId, value.ownership, value.revision, true))
|
|
137
|
+
return false;
|
|
138
|
+
if (!hasSameOwnership(ownership, value.ownership) || !hasSameRevision(revision, value.revision))
|
|
139
|
+
return false;
|
|
140
|
+
return (isSha256(value.manifestDigest) &&
|
|
141
|
+
value.compositeDependencyDigest === compositeDependencyDigest &&
|
|
142
|
+
isStrictCanonicalIsoTimestamp(value.completedAt));
|
|
143
|
+
};
|
|
144
|
+
const hasMatchingSurfaceDependency = (decision, name, surface) => {
|
|
145
|
+
const dependency = decision.dependencies.find(candidate => candidate.name === name);
|
|
146
|
+
return (dependency !== undefined &&
|
|
147
|
+
dependency.required &&
|
|
148
|
+
dependency.publication === 'completed' &&
|
|
149
|
+
dependency.generationId === surface.runId &&
|
|
150
|
+
dependency.digest === surface.manifestDigest);
|
|
151
|
+
};
|
|
152
|
+
/** Validates the promoted pointer for an evidence-enforced portal/plugin view pair. */
|
|
153
|
+
export const isCompletedAzureViewSetV2 = (value) => {
|
|
154
|
+
const allowedReferences = isRecord(value)
|
|
155
|
+
? [...allowedArtifactReferenceField(value.portal, 'manifestPath'), ...allowedArtifactReferenceField(value.plugin, 'manifestPath')]
|
|
156
|
+
: [];
|
|
157
|
+
if (!isRecord(value) || containsForbiddenArtifactControlData(value, allowedReferences))
|
|
158
|
+
return false;
|
|
159
|
+
if (value.schemaVersion !== 2 || value.status !== 'completed')
|
|
160
|
+
return false;
|
|
161
|
+
if (!isSafePathSegment(value.subscriptionId) ||
|
|
162
|
+
!isStrictNonEmptyString(value.publicationId) ||
|
|
163
|
+
!isEnforceableAzureOwnershipBinding(value.ownership) ||
|
|
164
|
+
!isArtifactRevisionVector(value.revision) ||
|
|
165
|
+
!hasMatchingViewOwnership(value.subscriptionId, value.ownership, value.revision, true)) {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
if (!isSha256(value.compositeDependencyDigest) || !isStrictCanonicalIsoTimestamp(value.completedAt))
|
|
169
|
+
return false;
|
|
170
|
+
if (!isViewSetV2SurfaceReference(value.portal, 'portal', value.subscriptionId, value.ownership, value.revision, value.compositeDependencyDigest) ||
|
|
171
|
+
!isViewSetV2SurfaceReference(value.plugin, 'plugin', value.subscriptionId, value.ownership, value.revision, value.compositeDependencyDigest)) {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
const laterSurfaceCompletedAt = Date.parse(value.portal.completedAt) >= Date.parse(value.plugin.completedAt) ? value.portal.completedAt : value.plugin.completedAt;
|
|
175
|
+
if (value.completedAt !== laterSurfaceCompletedAt)
|
|
176
|
+
return false;
|
|
177
|
+
return (isArtifactPublicationDecision(value.publicationDecision) &&
|
|
178
|
+
value.publicationDecision.publication === 'completed' &&
|
|
179
|
+
hasMatchingSurfaceDependency(value.publicationDecision, 'portal', value.portal) &&
|
|
180
|
+
hasMatchingSurfaceDependency(value.publicationDecision, 'plugin', value.plugin));
|
|
181
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export const WORK_ITEM_LANES = ['prioritized', 'todo', 'in-progress', 'completed'];
|
|
2
|
+
export const DEFAULT_WORK_BOARD_ID = 'kanban';
|
|
3
|
+
export const WORK_ITEM_EXTERNAL_TICKET_PROVIDERS = ['jira', 'halo', 'connectwise', 'autotask', 'azuredevops', 'github'];
|
|
4
|
+
export const WORK_ITEM_SHARE_CHANNELS = ['email', ...WORK_ITEM_EXTERNAL_TICKET_PROVIDERS];
|