@spottoai/types-package 1.0.2-dev1138.2 → 1.0.2-dev1138.5
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 +9 -0
- package/dist/accounts/accounts.d.ts +77 -4
- package/dist/accounts/accounts.d.ts.map +1 -1
- package/dist/accounts/accounts.js +2 -1
- package/dist/accounts/accounts.js.map +1 -1
- package/dist/accounts/azureManualOnboarding.d.ts +114 -0
- package/dist/accounts/azureManualOnboarding.d.ts.map +1 -0
- package/dist/accounts/azureManualOnboarding.js +12 -0
- package/dist/accounts/azureManualOnboarding.js.map +1 -0
- package/dist/accounts/azureSpSetup.d.ts +128 -31
- package/dist/accounts/azureSpSetup.d.ts.map +1 -1
- package/dist/accounts/azureSpSetup.js +15 -0
- package/dist/accounts/azureSpSetup.js.map +1 -1
- package/dist/accounts/azureSpSetupDurable.d.ts +8 -0
- package/dist/accounts/azureSpSetupDurable.d.ts.map +1 -1
- package/dist/accounts/azureSpSetupDurable.js +28 -0
- package/dist/accounts/azureSpSetupDurable.js.map +1 -1
- package/dist/accounts/index.d.ts +1 -0
- package/dist/accounts/index.d.ts.map +1 -1
- package/dist/accounts/index.js +1 -0
- package/dist/accounts/index.js.map +1 -1
- package/dist/accounts/readPermissions.d.ts +1 -1
- package/dist/accounts/readPermissions.d.ts.map +1 -1
- package/dist/accounts/readPermissions.js.map +1 -1
- package/dist/accounts/validation.d.ts +24 -0
- package/dist/accounts/validation.d.ts.map +1 -1
- package/dist/ai/index.d.ts +14 -1
- package/dist/ai/index.d.ts.map +1 -1
- package/dist/ai/index.js +12 -0
- package/dist/ai/index.js.map +1 -1
- package/dist/aws/commitments-planning.d.ts +5 -0
- package/dist/aws/commitments-planning.d.ts.map +1 -1
- package/dist/aws/commitments-planning.js +19 -0
- package/dist/aws/commitments-planning.js.map +1 -1
- package/dist/aws/estates.d.ts +1 -1
- package/dist/aws/estates.d.ts.map +1 -1
- package/dist/aws/estates.js +7 -1
- package/dist/aws/estates.js.map +1 -1
- package/dist/aws/index.d.ts +5 -0
- package/dist/aws/index.d.ts.map +1 -1
- package/dist/aws/index.js +5 -0
- package/dist/aws/index.js.map +1 -1
- package/dist/aws/onboarding.d.ts +3 -0
- package/dist/aws/onboarding.d.ts.map +1 -1
- package/dist/aws/organizationCommitments.d.ts +260 -0
- package/dist/aws/organizationCommitments.d.ts.map +1 -0
- package/dist/aws/organizationCommitments.js +53 -0
- package/dist/aws/organizationCommitments.js.map +1 -0
- package/dist/aws/organizationCommitmentsApiValidation.d.ts +21 -0
- package/dist/aws/organizationCommitmentsApiValidation.d.ts.map +1 -0
- package/dist/aws/organizationCommitmentsApiValidation.js +226 -0
- package/dist/aws/organizationCommitmentsApiValidation.js.map +1 -0
- package/dist/aws/organizationCommitmentsValidation.d.ts +12 -0
- package/dist/aws/organizationCommitmentsValidation.d.ts.map +1 -0
- package/dist/aws/organizationCommitmentsValidation.js +228 -0
- package/dist/aws/organizationCommitmentsValidation.js.map +1 -0
- package/dist/aws/portalOrganizationCommitmentsPlanningPublicArtifactValidation.d.ts +9 -0
- package/dist/aws/portalOrganizationCommitmentsPlanningPublicArtifactValidation.d.ts.map +1 -0
- package/dist/aws/portalOrganizationCommitmentsPlanningPublicArtifactValidation.js +407 -0
- package/dist/aws/portalOrganizationCommitmentsPlanningPublicArtifactValidation.js.map +1 -0
- package/dist/aws/portalOrganizationCommitmentsPlanningPublicArtifacts.d.ts +17 -0
- package/dist/aws/portalOrganizationCommitmentsPlanningPublicArtifacts.d.ts.map +1 -0
- package/dist/aws/portalOrganizationCommitmentsPlanningPublicArtifacts.js +6 -0
- package/dist/aws/portalOrganizationCommitmentsPlanningPublicArtifacts.js.map +1 -0
- package/dist/aws/requests.d.ts +13 -2
- package/dist/aws/requests.d.ts.map +1 -1
- package/dist/aws/requests.js +1 -1
- package/dist/aws/requests.js.map +1 -1
- package/dist/azure/commitmentsPlanning.d.ts +2 -2
- package/dist/azure/commitmentsPlanning.d.ts.map +1 -1
- package/dist/azure/financialAnalytics.d.ts +68 -2
- package/dist/azure/financialAnalytics.d.ts.map +1 -1
- package/dist/azure/financialAnalyticsDelivery.d.ts +2 -0
- package/dist/azure/financialAnalyticsDelivery.d.ts.map +1 -1
- package/dist/azure/financialAnalyticsDelivery.js +4 -1
- package/dist/azure/financialAnalyticsDelivery.js.map +1 -1
- package/dist/azure/financialAnalyticsDisplayKernel.d.ts +12 -0
- package/dist/azure/financialAnalyticsDisplayKernel.d.ts.map +1 -0
- package/dist/azure/financialAnalyticsDisplayKernel.js +144 -0
- package/dist/azure/financialAnalyticsDisplayKernel.js.map +1 -0
- package/dist/azure/financialAnalyticsValidation.d.ts.map +1 -1
- package/dist/azure/financialAnalyticsValidation.js +61 -7
- package/dist/azure/financialAnalyticsValidation.js.map +1 -1
- package/dist/azure/financialAuthorityProjectionKernel.d.ts +7 -0
- package/dist/azure/financialAuthorityProjectionKernel.d.ts.map +1 -0
- package/dist/azure/financialAuthorityProjectionKernel.js +165 -0
- package/dist/azure/financialAuthorityProjectionKernel.js.map +1 -0
- package/dist/azure/financialAuthorityView.d.ts +4 -0
- package/dist/azure/financialAuthorityView.d.ts.map +1 -1
- package/dist/azure/financialAuthorityView.js.map +1 -1
- package/dist/azure/financialAuthorityViewValidation.d.ts +3 -1
- package/dist/azure/financialAuthorityViewValidation.d.ts.map +1 -1
- package/dist/azure/financialAuthorityViewValidation.js +206 -2
- package/dist/azure/financialAuthorityViewValidation.js.map +1 -1
- package/dist/azure/financialChargeComposition.d.ts +81 -0
- package/dist/azure/financialChargeComposition.d.ts.map +1 -0
- package/dist/azure/financialChargeComposition.js +41 -0
- package/dist/azure/financialChargeComposition.js.map +1 -0
- package/dist/azure/financialChargeCompositionKernel.d.ts +3 -0
- package/dist/azure/financialChargeCompositionKernel.d.ts.map +1 -0
- package/dist/azure/financialChargeCompositionKernel.js +53 -0
- package/dist/azure/financialChargeCompositionKernel.js.map +1 -0
- package/dist/azure/financialChargeCompositionValidation.d.ts +8 -0
- package/dist/azure/financialChargeCompositionValidation.d.ts.map +1 -0
- package/dist/azure/financialChargeCompositionValidation.js +164 -0
- package/dist/azure/financialChargeCompositionValidation.js.map +1 -0
- package/dist/azure/financialDataflow.d.ts +8 -1
- package/dist/azure/financialDataflow.d.ts.map +1 -1
- package/dist/azure/financialDataflow.js.map +1 -1
- package/dist/azure/financialDataflowKernel.d.ts +2 -0
- package/dist/azure/financialDataflowKernel.d.ts.map +1 -1
- package/dist/azure/financialDataflowKernel.js +60 -7
- package/dist/azure/financialDataflowKernel.js.map +1 -1
- package/dist/azure/financialDataflowValidation.d.ts +1 -1
- package/dist/azure/financialDataflowValidation.d.ts.map +1 -1
- package/dist/azure/financialDataflowValidation.js +146 -28
- package/dist/azure/financialDataflowValidation.js.map +1 -1
- package/dist/azure/financialPolicy.d.ts +3 -0
- package/dist/azure/financialPolicy.d.ts.map +1 -1
- package/dist/azure/financialPolicy.js.map +1 -1
- package/dist/azure/financialPolicyValidation.d.ts.map +1 -1
- package/dist/azure/financialPolicyValidation.js +5 -0
- package/dist/azure/financialPolicyValidation.js.map +1 -1
- package/dist/azure/financialProjection.d.ts +11 -1
- package/dist/azure/financialProjection.d.ts.map +1 -1
- package/dist/azure/financialProjection.js.map +1 -1
- package/dist/azure/financialProjectionValidation.d.ts.map +1 -1
- package/dist/azure/financialProjectionValidation.js +43 -3
- package/dist/azure/financialProjectionValidation.js.map +1 -1
- package/dist/azure/financialSavingsAuthority.d.ts +25 -4
- package/dist/azure/financialSavingsAuthority.d.ts.map +1 -1
- package/dist/azure/financialSavingsAuthorityValidation.d.ts +6 -2
- package/dist/azure/financialSavingsAuthorityValidation.d.ts.map +1 -1
- package/dist/azure/financialSavingsAuthorityValidation.js +126 -2
- package/dist/azure/financialSavingsAuthorityValidation.js.map +1 -1
- package/dist/azure/financialSavingsCoordinateValidation.d.ts.map +1 -1
- package/dist/azure/financialSavingsCoordinateValidation.js +62 -19
- package/dist/azure/financialSavingsCoordinateValidation.js.map +1 -1
- package/dist/azure/financialSavingsSurfaceProjection.d.ts +58 -3
- package/dist/azure/financialSavingsSurfaceProjection.d.ts.map +1 -1
- package/dist/azure/financialSavingsSurfaceProjection.js +3 -1
- package/dist/azure/financialSavingsSurfaceProjection.js.map +1 -1
- package/dist/azure/financialSavingsSurfaceProjectionKernel.d.ts +14 -0
- package/dist/azure/financialSavingsSurfaceProjectionKernel.d.ts.map +1 -0
- package/dist/azure/financialSavingsSurfaceProjectionKernel.js +326 -0
- package/dist/azure/financialSavingsSurfaceProjectionKernel.js.map +1 -0
- package/dist/azure/financialSavingsSurfaceProjectionValidation.d.ts.map +1 -1
- package/dist/azure/financialSavingsSurfaceProjectionValidation.js +126 -12
- package/dist/azure/financialSavingsSurfaceProjectionValidation.js.map +1 -1
- package/dist/azure/financialScope.d.ts +6 -0
- package/dist/azure/financialScope.d.ts.map +1 -1
- package/dist/azure/financialScope.js +6 -0
- package/dist/azure/financialScope.js.map +1 -1
- package/dist/azure/governance.d.ts +145 -0
- package/dist/azure/governance.d.ts.map +1 -1
- package/dist/azure/governance.js +2 -1
- package/dist/azure/governance.js.map +1 -1
- package/dist/azure/metrics.d.ts +2 -2
- package/dist/azure/metrics.d.ts.map +1 -1
- package/dist/azure/payloads.d.ts +19 -0
- package/dist/azure/payloads.d.ts.map +1 -1
- package/dist/azure/prices.d.ts +16 -3
- package/dist/azure/prices.d.ts.map +1 -1
- package/dist/azure/recommendationState.d.ts +1 -1
- package/dist/azure/recommendationState.d.ts.map +1 -1
- package/dist/azure/recommendations.d.ts +15 -5
- package/dist/azure/recommendations.d.ts.map +1 -1
- package/dist/azure/reports.d.ts +3 -2
- package/dist/azure/reports.d.ts.map +1 -1
- package/dist/azure/reports.js.map +1 -1
- package/dist/azure/resources.d.ts +12 -0
- package/dist/azure/resources.d.ts.map +1 -1
- package/dist/azure/serviceRetirement.d.ts +33 -2
- package/dist/azure/serviceRetirement.d.ts.map +1 -1
- package/dist/azure/subscriptions.d.ts +13 -2
- package/dist/azure/subscriptions.d.ts.map +1 -1
- package/dist/azure/views.d.ts +26 -3
- package/dist/azure/views.d.ts.map +1 -1
- package/dist/azure/views.js.map +1 -1
- package/dist/azure/vmPatchManagement.d.ts +109 -0
- package/dist/azure/vmPatchManagement.d.ts.map +1 -0
- package/dist/azure/vmPatchManagement.js +7 -0
- package/dist/azure/vmPatchManagement.js.map +1 -0
- package/dist/common/provider.d.ts +53 -0
- package/dist/common/provider.d.ts.map +1 -1
- package/dist/common/provider.js.map +1 -1
- package/dist/environment/canonicalization.d.ts +12 -0
- package/dist/environment/canonicalization.d.ts.map +1 -0
- package/dist/environment/canonicalization.js +40 -0
- package/dist/environment/canonicalization.js.map +1 -0
- package/dist/environment/contracts.d.ts +196 -0
- package/dist/environment/contracts.d.ts.map +1 -0
- package/dist/environment/contracts.js +21 -0
- package/dist/environment/contracts.js.map +1 -0
- package/dist/environment/index.d.ts +5 -0
- package/dist/environment/index.d.ts.map +1 -0
- package/dist/environment/index.js +21 -0
- package/dist/environment/index.js.map +1 -0
- package/dist/environment/internal.d.ts +21 -0
- package/dist/environment/internal.d.ts.map +1 -0
- package/dist/environment/internal.js +113 -0
- package/dist/environment/internal.js.map +1 -0
- package/dist/environment/references.d.ts +18 -0
- package/dist/environment/references.d.ts.map +1 -0
- package/dist/environment/references.js +206 -0
- package/dist/environment/references.js.map +1 -0
- package/dist/environment/validation.d.ts +28 -0
- package/dist/environment/validation.d.ts.map +1 -0
- package/dist/environment/validation.js +320 -0
- package/dist/environment/validation.js.map +1 -0
- package/dist/esm/accounts/accounts.js +1 -0
- package/dist/esm/accounts/azureManualOnboarding.js +8 -0
- package/dist/esm/accounts/azureSpSetup.js +14 -1
- package/dist/esm/accounts/azureSpSetupDurable.js +27 -1
- package/dist/esm/accounts/index.js +1 -0
- package/dist/esm/ai/index.js +10 -1
- package/dist/esm/aws/commitments-planning.js +5 -0
- package/dist/esm/aws/estates.js +7 -1
- package/dist/esm/aws/index.js +5 -0
- package/dist/esm/aws/organizationCommitments.js +48 -0
- package/dist/esm/aws/organizationCommitmentsApiValidation.js +220 -0
- package/dist/esm/aws/organizationCommitmentsValidation.js +224 -0
- package/dist/esm/aws/portalOrganizationCommitmentsPlanningPublicArtifactValidation.js +402 -0
- package/dist/esm/aws/portalOrganizationCommitmentsPlanningPublicArtifacts.js +2 -0
- package/dist/esm/aws/requests.js +1 -1
- package/dist/esm/azure/financialAnalyticsDelivery.js +2 -0
- package/dist/esm/azure/financialAnalyticsDisplayKernel.js +139 -0
- package/dist/esm/azure/financialAnalyticsValidation.js +61 -7
- package/dist/esm/azure/financialAuthorityProjectionKernel.js +159 -0
- package/dist/esm/azure/financialAuthorityViewValidation.js +204 -1
- package/dist/esm/azure/financialChargeComposition.js +36 -0
- package/dist/esm/azure/financialChargeCompositionKernel.js +48 -0
- package/dist/esm/azure/financialChargeCompositionValidation.js +155 -0
- package/dist/esm/azure/financialDataflowKernel.js +60 -7
- package/dist/esm/azure/financialDataflowValidation.js +146 -28
- package/dist/esm/azure/financialPolicyValidation.js +5 -0
- package/dist/esm/azure/financialProjectionValidation.js +43 -3
- package/dist/esm/azure/financialSavingsAuthorityValidation.js +124 -2
- package/dist/esm/azure/financialSavingsCoordinateValidation.js +62 -19
- package/dist/esm/azure/financialSavingsSurfaceProjection.js +2 -0
- package/dist/esm/azure/financialSavingsSurfaceProjectionKernel.js +318 -0
- package/dist/esm/azure/financialSavingsSurfaceProjectionValidation.js +126 -12
- package/dist/esm/azure/financialScope.js +6 -0
- package/dist/esm/azure/governance.js +1 -0
- package/dist/esm/azure/vmPatchManagement.js +3 -0
- package/dist/esm/entries/commitments-planning.js +2 -2
- package/dist/esm/entries/environment.js +3 -0
- package/dist/esm/environment/canonicalization.js +34 -0
- package/dist/esm/environment/contracts.js +17 -0
- package/dist/esm/environment/index.js +4 -0
- package/dist/esm/environment/internal.js +97 -0
- package/dist/esm/environment/references.js +194 -0
- package/dist/esm/environment/validation.js +303 -0
- package/dist/esm/index.js +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/portfolio/portfolioOperations.d.ts +1 -1
- package/dist/portfolio/portfolioOperations.d.ts.map +1 -1
- package/package.json +15 -3
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Phase-one environment-memory contract constants shared by producers and consumers. */
|
|
2
|
+
export const ENVIRONMENT_CONTRACT_LIMITS_V1 = Object.freeze({
|
|
3
|
+
completedPointerBytes: 16 * 1024,
|
|
4
|
+
projectionBytes: 64 * 1024,
|
|
5
|
+
environmentIndexBytes: 4 * 1024,
|
|
6
|
+
costPillarBytes: 8 * 1024,
|
|
7
|
+
boundedListItems: 50,
|
|
8
|
+
customerStringScalars: 4096,
|
|
9
|
+
safeLabelScalars: 512,
|
|
10
|
+
scopeIdentifierScalars: 2048,
|
|
11
|
+
environmentRunIdAsciiCharacters: 128,
|
|
12
|
+
sourceIdentityScalars: 256,
|
|
13
|
+
logicalReferencePayloadBytes: 4 * 1024,
|
|
14
|
+
validatedContainerDepth: 8,
|
|
15
|
+
});
|
|
16
|
+
export const ENVIRONMENT_DOCUMENT_NAMES_V1 = ['projection.json', 'environment-index.md', 'pillars/cost.md'];
|
|
17
|
+
export const ENVIRONMENT_ARTIFACT_KINDS_V1 = ['subscription-summary', 'subscription-resources', 'subscription-recommendations'];
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { ENVIRONMENT_CONTRACT_LIMITS_V1 } from './contracts.js';
|
|
2
|
+
export const SHA256_PATTERN = /^[a-f0-9]{64}$/u;
|
|
3
|
+
export const ENVIRONMENT_RUN_ID_PATTERN = /^[A-Za-z0-9._-]+$/u;
|
|
4
|
+
export const DECIMAL_PATTERN = /^(?:0|[1-9]\d*)(?:\.\d+)?$/u;
|
|
5
|
+
export const CURRENCY_PATTERN = /^[A-Z]{3}$/u;
|
|
6
|
+
export const PROTOTYPE_KEYS = new Set(['__proto__', 'prototype', 'constructor']);
|
|
7
|
+
export const hasControlCharacter = (value) => {
|
|
8
|
+
for (const character of value) {
|
|
9
|
+
const codePoint = character.codePointAt(0);
|
|
10
|
+
if (codePoint <= 0x1f || (codePoint >= 0x7f && codePoint <= 0x9f))
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
return false;
|
|
14
|
+
};
|
|
15
|
+
export const isRecord = (value) => {
|
|
16
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
17
|
+
return false;
|
|
18
|
+
const prototype = Object.getPrototypeOf(value);
|
|
19
|
+
return prototype === Object.prototype || prototype === null;
|
|
20
|
+
};
|
|
21
|
+
export const hasExactKeys = (value, required, optional = []) => {
|
|
22
|
+
const allowed = new Set([...required, ...optional]);
|
|
23
|
+
const keys = Object.keys(value);
|
|
24
|
+
return required.every(key => Object.prototype.hasOwnProperty.call(value, key)) && keys.every(key => allowed.has(key) && !PROTOTYPE_KEYS.has(key));
|
|
25
|
+
};
|
|
26
|
+
const scalarLength = (value) => {
|
|
27
|
+
let count = 0;
|
|
28
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
29
|
+
const codeUnit = value.charCodeAt(index);
|
|
30
|
+
if (codeUnit >= 0xd800 && codeUnit <= 0xdbff) {
|
|
31
|
+
const trailing = value.charCodeAt(index + 1);
|
|
32
|
+
if (!(trailing >= 0xdc00 && trailing <= 0xdfff))
|
|
33
|
+
return null;
|
|
34
|
+
index += 1;
|
|
35
|
+
}
|
|
36
|
+
else if (codeUnit >= 0xdc00 && codeUnit <= 0xdfff) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
count += 1;
|
|
40
|
+
}
|
|
41
|
+
return count;
|
|
42
|
+
};
|
|
43
|
+
export const isBoundedString = (value, maximumScalars, options = {}) => {
|
|
44
|
+
if (typeof value !== 'string')
|
|
45
|
+
return false;
|
|
46
|
+
if (options.trimmed && (value.length === 0 || value.trim() !== value))
|
|
47
|
+
return false;
|
|
48
|
+
if (options.controls && hasControlCharacter(value))
|
|
49
|
+
return false;
|
|
50
|
+
const length = scalarLength(value);
|
|
51
|
+
return length !== null && length <= maximumScalars;
|
|
52
|
+
};
|
|
53
|
+
export const isCustomerString = (value) => isBoundedString(value, ENVIRONMENT_CONTRACT_LIMITS_V1.customerStringScalars, { controls: true });
|
|
54
|
+
export const isSafeLabel = (value) => isBoundedString(value, ENVIRONMENT_CONTRACT_LIMITS_V1.safeLabelScalars, { trimmed: true, controls: true });
|
|
55
|
+
export const isScopeIdentifier = (value) => isBoundedString(value, ENVIRONMENT_CONTRACT_LIMITS_V1.scopeIdentifierScalars, { trimmed: true, controls: true });
|
|
56
|
+
export const isSourceIdentity = (value) => isBoundedString(value, ENVIRONMENT_CONTRACT_LIMITS_V1.sourceIdentityScalars, { trimmed: true, controls: true });
|
|
57
|
+
export const isNonNegativeInteger = (value) => Number.isSafeInteger(value) && value >= 0;
|
|
58
|
+
export const isCanonicalUtcTimestamp = (value) => {
|
|
59
|
+
if (typeof value !== 'string' || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/u.test(value))
|
|
60
|
+
return false;
|
|
61
|
+
const milliseconds = Date.parse(value);
|
|
62
|
+
return Number.isFinite(milliseconds) && new Date(milliseconds).toISOString() === value;
|
|
63
|
+
};
|
|
64
|
+
export const utf8ByteLength = (value) => {
|
|
65
|
+
let bytes = 0;
|
|
66
|
+
for (const character of value) {
|
|
67
|
+
const codePoint = character.codePointAt(0);
|
|
68
|
+
if (codePoint <= 0x7f)
|
|
69
|
+
bytes += 1;
|
|
70
|
+
else if (codePoint <= 0x7ff)
|
|
71
|
+
bytes += 2;
|
|
72
|
+
else if (codePoint <= 0xffff)
|
|
73
|
+
bytes += 3;
|
|
74
|
+
else
|
|
75
|
+
bytes += 4;
|
|
76
|
+
}
|
|
77
|
+
return bytes;
|
|
78
|
+
};
|
|
79
|
+
export const hasSafeContainerShape = (value, depth = 0, seen = new Set()) => {
|
|
80
|
+
if (depth > ENVIRONMENT_CONTRACT_LIMITS_V1.validatedContainerDepth)
|
|
81
|
+
return false;
|
|
82
|
+
if (typeof value !== 'object' || value === null)
|
|
83
|
+
return true;
|
|
84
|
+
if (seen.has(value))
|
|
85
|
+
return false;
|
|
86
|
+
seen.add(value);
|
|
87
|
+
if (Array.isArray(value)) {
|
|
88
|
+
const valid = value.length <= ENVIRONMENT_CONTRACT_LIMITS_V1.boundedListItems && value.every(item => hasSafeContainerShape(item, depth + 1, seen));
|
|
89
|
+
seen.delete(value);
|
|
90
|
+
return valid;
|
|
91
|
+
}
|
|
92
|
+
if (!isRecord(value))
|
|
93
|
+
return false;
|
|
94
|
+
const valid = Object.keys(value).every(key => !PROTOTYPE_KEYS.has(key) && hasSafeContainerShape(value[key], depth + 1, seen));
|
|
95
|
+
seen.delete(value);
|
|
96
|
+
return valid;
|
|
97
|
+
};
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { ENVIRONMENT_ARTIFACT_KINDS_V1, ENVIRONMENT_CONTRACT_LIMITS_V1, } from './contracts.js';
|
|
2
|
+
import { hasControlCharacter, isBoundedString, utf8ByteLength } from './internal.js';
|
|
3
|
+
const BASE64URL_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
|
|
4
|
+
const ARTIFACT_PREFIX = 'spotto://artifact/v1/';
|
|
5
|
+
const RESOURCE_PREFIX = 'spotto://resource/v1/';
|
|
6
|
+
const ARTIFACT_KINDS = new Set(ENVIRONMENT_ARTIFACT_KINDS_V1);
|
|
7
|
+
const isCanonicalScopeQualifiedSubject = (value) => {
|
|
8
|
+
try {
|
|
9
|
+
const parsed = JSON.parse(value);
|
|
10
|
+
return (Array.isArray(parsed) &&
|
|
11
|
+
parsed.length === 4 &&
|
|
12
|
+
parsed[0] === 'azure-subscription' &&
|
|
13
|
+
parsed
|
|
14
|
+
.slice(1)
|
|
15
|
+
.every(item => isBoundedString(item, ENVIRONMENT_CONTRACT_LIMITS_V1.scopeIdentifierScalars, { trimmed: true, controls: true })) &&
|
|
16
|
+
JSON.stringify(parsed) === value);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
const encodeUtf8 = (value) => {
|
|
23
|
+
const bytes = [];
|
|
24
|
+
for (const character of value) {
|
|
25
|
+
const codePoint = character.codePointAt(0);
|
|
26
|
+
if (codePoint <= 0x7f)
|
|
27
|
+
bytes.push(codePoint);
|
|
28
|
+
else if (codePoint <= 0x7ff)
|
|
29
|
+
bytes.push(0xc0 | (codePoint >> 6), 0x80 | (codePoint & 0x3f));
|
|
30
|
+
else if (codePoint <= 0xffff)
|
|
31
|
+
bytes.push(0xe0 | (codePoint >> 12), 0x80 | ((codePoint >> 6) & 0x3f), 0x80 | (codePoint & 0x3f));
|
|
32
|
+
else
|
|
33
|
+
bytes.push(0xf0 | (codePoint >> 18), 0x80 | ((codePoint >> 12) & 0x3f), 0x80 | ((codePoint >> 6) & 0x3f), 0x80 | (codePoint & 0x3f));
|
|
34
|
+
}
|
|
35
|
+
return bytes;
|
|
36
|
+
};
|
|
37
|
+
const encodeBase64Url = (bytes) => {
|
|
38
|
+
let output = '';
|
|
39
|
+
for (let index = 0; index < bytes.length; index += 3) {
|
|
40
|
+
const first = bytes[index];
|
|
41
|
+
const second = bytes[index + 1];
|
|
42
|
+
const third = bytes[index + 2];
|
|
43
|
+
output += BASE64URL_ALPHABET[first >> 2];
|
|
44
|
+
output += BASE64URL_ALPHABET[((first & 0x03) << 4) | ((second ?? 0) >> 4)];
|
|
45
|
+
if (second !== undefined)
|
|
46
|
+
output += BASE64URL_ALPHABET[((second & 0x0f) << 2) | ((third ?? 0) >> 6)];
|
|
47
|
+
if (third !== undefined)
|
|
48
|
+
output += BASE64URL_ALPHABET[third & 0x3f];
|
|
49
|
+
}
|
|
50
|
+
return output;
|
|
51
|
+
};
|
|
52
|
+
const decodeBase64Url = (value) => {
|
|
53
|
+
if (!/^[A-Za-z0-9_-]+$/u.test(value) || value.length % 4 === 1)
|
|
54
|
+
return null;
|
|
55
|
+
const bytes = [];
|
|
56
|
+
for (let index = 0; index < value.length; index += 4) {
|
|
57
|
+
const first = BASE64URL_ALPHABET.indexOf(value[index]);
|
|
58
|
+
const second = BASE64URL_ALPHABET.indexOf(value[index + 1]);
|
|
59
|
+
const thirdCharacter = value[index + 2];
|
|
60
|
+
const fourthCharacter = value[index + 3];
|
|
61
|
+
const third = thirdCharacter === undefined ? 0 : BASE64URL_ALPHABET.indexOf(thirdCharacter);
|
|
62
|
+
const fourth = fourthCharacter === undefined ? 0 : BASE64URL_ALPHABET.indexOf(fourthCharacter);
|
|
63
|
+
if (first < 0 || second < 0 || third < 0 || fourth < 0)
|
|
64
|
+
return null;
|
|
65
|
+
bytes.push((first << 2) | (second >> 4));
|
|
66
|
+
if (thirdCharacter !== undefined)
|
|
67
|
+
bytes.push(((second & 0x0f) << 4) | (third >> 2));
|
|
68
|
+
if (fourthCharacter !== undefined)
|
|
69
|
+
bytes.push(((third & 0x03) << 6) | fourth);
|
|
70
|
+
}
|
|
71
|
+
return encodeBase64Url(bytes) === value ? bytes : null;
|
|
72
|
+
};
|
|
73
|
+
const decodeUtf8 = (bytes) => {
|
|
74
|
+
let output = '';
|
|
75
|
+
for (let index = 0; index < bytes.length;) {
|
|
76
|
+
const first = bytes[index];
|
|
77
|
+
let codePoint;
|
|
78
|
+
let continuationCount;
|
|
79
|
+
if (first <= 0x7f) {
|
|
80
|
+
codePoint = first;
|
|
81
|
+
continuationCount = 0;
|
|
82
|
+
}
|
|
83
|
+
else if (first >= 0xc2 && first <= 0xdf) {
|
|
84
|
+
codePoint = first & 0x1f;
|
|
85
|
+
continuationCount = 1;
|
|
86
|
+
}
|
|
87
|
+
else if (first >= 0xe0 && first <= 0xef) {
|
|
88
|
+
codePoint = first & 0x0f;
|
|
89
|
+
continuationCount = 2;
|
|
90
|
+
}
|
|
91
|
+
else if (first >= 0xf0 && first <= 0xf4) {
|
|
92
|
+
codePoint = first & 0x07;
|
|
93
|
+
continuationCount = 3;
|
|
94
|
+
}
|
|
95
|
+
else
|
|
96
|
+
return null;
|
|
97
|
+
if (index + continuationCount >= bytes.length)
|
|
98
|
+
return null;
|
|
99
|
+
for (let offset = 1; offset <= continuationCount; offset += 1) {
|
|
100
|
+
const next = bytes[index + offset];
|
|
101
|
+
if ((next & 0xc0) !== 0x80)
|
|
102
|
+
return null;
|
|
103
|
+
codePoint = (codePoint << 6) | (next & 0x3f);
|
|
104
|
+
}
|
|
105
|
+
if ((continuationCount === 2 && codePoint < 0x800) ||
|
|
106
|
+
(continuationCount === 3 && codePoint < 0x10000) ||
|
|
107
|
+
(codePoint >= 0xd800 && codePoint <= 0xdfff) ||
|
|
108
|
+
codePoint > 0x10ffff) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
output += String.fromCodePoint(codePoint);
|
|
112
|
+
index += continuationCount + 1;
|
|
113
|
+
}
|
|
114
|
+
return output;
|
|
115
|
+
};
|
|
116
|
+
const decodePayload = (value) => {
|
|
117
|
+
const bytes = decodeBase64Url(value);
|
|
118
|
+
if (bytes === null || bytes.length > ENVIRONMENT_CONTRACT_LIMITS_V1.logicalReferencePayloadBytes)
|
|
119
|
+
return null;
|
|
120
|
+
const decoded = decodeUtf8(bytes);
|
|
121
|
+
if (decoded === null || encodeBase64Url(encodeUtf8(decoded)) !== value)
|
|
122
|
+
return null;
|
|
123
|
+
return decoded;
|
|
124
|
+
};
|
|
125
|
+
const assertReferencePayload = (value, label, maximumScalars) => {
|
|
126
|
+
if (!isBoundedString(value, maximumScalars, { trimmed: true, controls: true })) {
|
|
127
|
+
throw new TypeError(`${label} must be a non-empty, trimmed, bounded Unicode string without control characters.`);
|
|
128
|
+
}
|
|
129
|
+
if (utf8ByteLength(value) > ENVIRONMENT_CONTRACT_LIMITS_V1.logicalReferencePayloadBytes) {
|
|
130
|
+
throw new RangeError(`${label} exceeds the V1 logical-reference payload limit.`);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
const isCanonicalAzureResourceId = (value) => {
|
|
134
|
+
if (!/^\/subscriptions\/[^/]+\/(?:resourceGroups\/[^/]+\/)?providers\/[^/]+\/[^/]+\/[^/]+(?:\/[^/]+\/[^/]+)*$/iu.test(value) ||
|
|
135
|
+
value.includes('\\') ||
|
|
136
|
+
value.includes('?') ||
|
|
137
|
+
value.includes('#') ||
|
|
138
|
+
value.includes('%') ||
|
|
139
|
+
hasControlCharacter(value)) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
return value.split('/').every(segment => segment !== '.' && segment !== '..');
|
|
143
|
+
};
|
|
144
|
+
/** Builds an opaque logical artifact reference. The decoded subject is never a storage path. */
|
|
145
|
+
export const buildEnvironmentLogicalArtifactReferenceV1 = (artifactKind, canonicalScopeQualifiedSubject) => {
|
|
146
|
+
if (!ARTIFACT_KINDS.has(artifactKind))
|
|
147
|
+
throw new TypeError('Unsupported V1 environment artifact kind.');
|
|
148
|
+
assertReferencePayload(canonicalScopeQualifiedSubject, 'Artifact subject', ENVIRONMENT_CONTRACT_LIMITS_V1.customerStringScalars);
|
|
149
|
+
if (!isCanonicalScopeQualifiedSubject(canonicalScopeQualifiedSubject)) {
|
|
150
|
+
throw new TypeError('Artifact subject is not a canonical V1 scope-qualified subject.');
|
|
151
|
+
}
|
|
152
|
+
return `${ARTIFACT_PREFIX}${artifactKind}/${encodeBase64Url(encodeUtf8(canonicalScopeQualifiedSubject))}`;
|
|
153
|
+
};
|
|
154
|
+
/** Parses a V1 logical artifact reference into its allowlisted kind and opaque subject. */
|
|
155
|
+
export const parseEnvironmentLogicalArtifactReferenceV1 = (value) => {
|
|
156
|
+
if (typeof value !== 'string' || !value.startsWith(ARTIFACT_PREFIX))
|
|
157
|
+
return null;
|
|
158
|
+
const remainder = value.slice(ARTIFACT_PREFIX.length);
|
|
159
|
+
const separator = remainder.indexOf('/');
|
|
160
|
+
if (separator <= 0 || remainder.indexOf('/', separator + 1) >= 0)
|
|
161
|
+
return null;
|
|
162
|
+
const artifactKind = remainder.slice(0, separator);
|
|
163
|
+
if (!ARTIFACT_KINDS.has(artifactKind))
|
|
164
|
+
return null;
|
|
165
|
+
const subject = decodePayload(remainder.slice(separator + 1));
|
|
166
|
+
if (subject === null || !isCanonicalScopeQualifiedSubject(subject)) {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
return { kind: 'artifact', artifactKind: artifactKind, subject };
|
|
170
|
+
};
|
|
171
|
+
/** Builds an opaque logical Azure resource reference from a canonical resource ID. */
|
|
172
|
+
export const buildEnvironmentLogicalResourceReferenceV1 = (canonicalAzureResourceId) => {
|
|
173
|
+
assertReferencePayload(canonicalAzureResourceId, 'Azure resource ID', ENVIRONMENT_CONTRACT_LIMITS_V1.scopeIdentifierScalars);
|
|
174
|
+
if (!isCanonicalAzureResourceId(canonicalAzureResourceId))
|
|
175
|
+
throw new TypeError('Azure resource ID is not canonical or safe.');
|
|
176
|
+
return `${RESOURCE_PREFIX}${encodeBase64Url(encodeUtf8(canonicalAzureResourceId))}`;
|
|
177
|
+
};
|
|
178
|
+
/** Parses a V1 logical resource reference into a canonical Azure resource ID. */
|
|
179
|
+
export const parseEnvironmentLogicalResourceReferenceV1 = (value) => {
|
|
180
|
+
if (typeof value !== 'string' || !value.startsWith(RESOURCE_PREFIX))
|
|
181
|
+
return null;
|
|
182
|
+
const resourceId = decodePayload(value.slice(RESOURCE_PREFIX.length));
|
|
183
|
+
if (resourceId === null || !isCanonicalAzureResourceId(resourceId))
|
|
184
|
+
return null;
|
|
185
|
+
return { kind: 'resource', resourceId };
|
|
186
|
+
};
|
|
187
|
+
/** Parses either closed V1 logical evidence-reference kind. */
|
|
188
|
+
export const parseEnvironmentLogicalEvidenceReferenceV1 = (value) => parseEnvironmentLogicalArtifactReferenceV1(value) ?? parseEnvironmentLogicalResourceReferenceV1(value);
|
|
189
|
+
/** Returns true when a value is a canonical V1 logical artifact reference. */
|
|
190
|
+
export const isEnvironmentLogicalArtifactReferenceV1 = (value) => parseEnvironmentLogicalArtifactReferenceV1(value) !== null;
|
|
191
|
+
/** Returns true when a value is a canonical V1 logical resource reference. */
|
|
192
|
+
export const isEnvironmentLogicalResourceReferenceV1 = (value) => parseEnvironmentLogicalResourceReferenceV1(value) !== null;
|
|
193
|
+
/** Returns true when a value is one of the closed V1 logical evidence-reference kinds. */
|
|
194
|
+
export const isEnvironmentLogicalEvidenceReferenceV1 = (value) => parseEnvironmentLogicalEvidenceReferenceV1(value) !== null;
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import { ENVIRONMENT_ARTIFACT_KINDS_V1, ENVIRONMENT_CONTRACT_LIMITS_V1, ENVIRONMENT_DOCUMENT_NAMES_V1, } from './contracts.js';
|
|
2
|
+
import { CURRENCY_PATTERN, DECIMAL_PATTERN, ENVIRONMENT_RUN_ID_PATTERN, SHA256_PATTERN, hasExactKeys, hasSafeContainerShape, isBoundedString, isCanonicalUtcTimestamp, isCustomerString, isNonNegativeInteger, isRecord, isSafeLabel, isScopeIdentifier, isSourceIdentity, utf8ByteLength, } from './internal.js';
|
|
3
|
+
import { isEnvironmentLogicalEvidenceReferenceV1, isEnvironmentLogicalResourceReferenceV1 } from './references.js';
|
|
4
|
+
const DOCUMENT_NAMES = new Set(ENVIRONMENT_DOCUMENT_NAMES_V1);
|
|
5
|
+
const ARTIFACT_KINDS = new Set(ENVIRONMENT_ARTIFACT_KINDS_V1);
|
|
6
|
+
const MONEY_BASES = new Set(['billed', 'amortized']);
|
|
7
|
+
const MONEY_PROVENANCE = new Set([
|
|
8
|
+
'subscription-summary',
|
|
9
|
+
'subscription-resources',
|
|
10
|
+
'cost-savings-summary',
|
|
11
|
+
'savings-aggregate',
|
|
12
|
+
'recommendation',
|
|
13
|
+
]);
|
|
14
|
+
const SAVINGS_ADDITIVITY = new Set(['additive', 'scenario-non-additive']);
|
|
15
|
+
const CHANGE_DIRECTIONS = new Set(['increase', 'decrease', 'unchanged', 'unknown']);
|
|
16
|
+
const scopesEqual = (left, right) => left.kind === right.kind && left.tenantId === right.tenantId && left.companyId === right.companyId && left.subscriptionId === right.subscriptionId;
|
|
17
|
+
/** Validates a bounded, local Portal route suitable for client-visible evidence. */
|
|
18
|
+
export const isEnvironmentPortalRouteV1 = (value) => isBoundedString(value, ENVIRONMENT_CONTRACT_LIMITS_V1.customerStringScalars, { trimmed: true, controls: true }) &&
|
|
19
|
+
value.startsWith('/') &&
|
|
20
|
+
!value.startsWith('//') &&
|
|
21
|
+
!value.includes('\\') &&
|
|
22
|
+
!value.includes('://') &&
|
|
23
|
+
!value.includes('?') &&
|
|
24
|
+
!value.includes('#') &&
|
|
25
|
+
!value.includes('%') &&
|
|
26
|
+
value.split('/').every(segment => segment !== '.' && segment !== '..');
|
|
27
|
+
const isGeneralKey = (value) => isBoundedString(value, ENVIRONMENT_CONTRACT_LIMITS_V1.safeLabelScalars, { trimmed: true, controls: true });
|
|
28
|
+
const isReferenceArray = (value) => Array.isArray(value) &&
|
|
29
|
+
value.length <= ENVIRONMENT_CONTRACT_LIMITS_V1.boundedListItems &&
|
|
30
|
+
value.every(isEnvironmentLogicalEvidenceReferenceV1) &&
|
|
31
|
+
new Set(value).size === value.length;
|
|
32
|
+
/** Validates the closed phase-one Azure subscription scope. */
|
|
33
|
+
export const isEnvironmentScopeV1 = (value) => isRecord(value) &&
|
|
34
|
+
hasExactKeys(value, ['kind', 'tenantId', 'companyId', 'subscriptionId']) &&
|
|
35
|
+
value.kind === 'azure-subscription' &&
|
|
36
|
+
isScopeIdentifier(value.tenantId) &&
|
|
37
|
+
isScopeIdentifier(value.companyId) &&
|
|
38
|
+
isScopeIdentifier(value.subscriptionId);
|
|
39
|
+
/** Validates the client-safe identity of one authoritative source generation. */
|
|
40
|
+
export const isEnvironmentSourceGenerationV1 = (value) => isRecord(value) &&
|
|
41
|
+
hasExactKeys(value, [
|
|
42
|
+
'viewSetSchemaVersion',
|
|
43
|
+
'publicationId',
|
|
44
|
+
'portalRunId',
|
|
45
|
+
'pluginRunId',
|
|
46
|
+
'economicsGenerationId',
|
|
47
|
+
'economicsFingerprint',
|
|
48
|
+
'completedAt',
|
|
49
|
+
]) &&
|
|
50
|
+
value.viewSetSchemaVersion === 1 &&
|
|
51
|
+
isSourceIdentity(value.publicationId) &&
|
|
52
|
+
isSourceIdentity(value.portalRunId) &&
|
|
53
|
+
isSourceIdentity(value.pluginRunId) &&
|
|
54
|
+
isSourceIdentity(value.economicsGenerationId) &&
|
|
55
|
+
isSourceIdentity(value.economicsFingerprint) &&
|
|
56
|
+
isCanonicalUtcTimestamp(value.completedAt);
|
|
57
|
+
/** Validates a byte-preserving binding to an authoritative CompletedAzureViewSetV1. */
|
|
58
|
+
export const isEnvironmentSourceBindingV1 = (value) => isRecord(value) &&
|
|
59
|
+
hasExactKeys(value, [
|
|
60
|
+
'kind',
|
|
61
|
+
'viewSetSchemaVersion',
|
|
62
|
+
'scope',
|
|
63
|
+
'publicationId',
|
|
64
|
+
'portalRunId',
|
|
65
|
+
'pluginRunId',
|
|
66
|
+
'economicsGenerationId',
|
|
67
|
+
'economicsFingerprint',
|
|
68
|
+
'completedAt',
|
|
69
|
+
]) &&
|
|
70
|
+
value.kind === 'azure-subscription-view-set' &&
|
|
71
|
+
value.viewSetSchemaVersion === 1 &&
|
|
72
|
+
isEnvironmentScopeV1(value.scope) &&
|
|
73
|
+
isEnvironmentSourceGenerationV1({
|
|
74
|
+
viewSetSchemaVersion: value.viewSetSchemaVersion,
|
|
75
|
+
publicationId: value.publicationId,
|
|
76
|
+
portalRunId: value.portalRunId,
|
|
77
|
+
pluginRunId: value.pluginRunId,
|
|
78
|
+
economicsGenerationId: value.economicsGenerationId,
|
|
79
|
+
economicsFingerprint: value.economicsFingerprint,
|
|
80
|
+
completedAt: value.completedAt,
|
|
81
|
+
});
|
|
82
|
+
/** Validates a storage-safe environment run identity independently from source identities. */
|
|
83
|
+
export const isEnvironmentRunIdV1 = (value) => typeof value === 'string' &&
|
|
84
|
+
value.length > 0 &&
|
|
85
|
+
value.length <= ENVIRONMENT_CONTRACT_LIMITS_V1.environmentRunIdAsciiCharacters &&
|
|
86
|
+
value !== '.' &&
|
|
87
|
+
value !== '..' &&
|
|
88
|
+
ENVIRONMENT_RUN_ID_PATTERN.test(value);
|
|
89
|
+
/** Validates canonical decimal money with explicit currency, basis, period, and provenance. */
|
|
90
|
+
export const isEnvironmentMoneyValueV1 = (value) => isRecord(value) &&
|
|
91
|
+
hasExactKeys(value, ['amount', 'currencyCode', 'basis', 'period', 'provenance'], ['savingsAdditivity']) &&
|
|
92
|
+
typeof value.amount === 'string' &&
|
|
93
|
+
DECIMAL_PATTERN.test(value.amount) &&
|
|
94
|
+
typeof value.currencyCode === 'string' &&
|
|
95
|
+
CURRENCY_PATTERN.test(value.currencyCode) &&
|
|
96
|
+
typeof value.basis === 'string' &&
|
|
97
|
+
MONEY_BASES.has(value.basis) &&
|
|
98
|
+
isBoundedString(value.period, ENVIRONMENT_CONTRACT_LIMITS_V1.safeLabelScalars, { trimmed: true, controls: true }) &&
|
|
99
|
+
typeof value.provenance === 'string' &&
|
|
100
|
+
MONEY_PROVENANCE.has(value.provenance) &&
|
|
101
|
+
(value.savingsAdditivity === undefined || (typeof value.savingsAdditivity === 'string' && SAVINGS_ADDITIVITY.has(value.savingsAdditivity)));
|
|
102
|
+
const isObservedMoney = (value) => isEnvironmentMoneyValueV1(value) && value.savingsAdditivity === undefined;
|
|
103
|
+
const isSavingsMoney = (value) => isEnvironmentMoneyValueV1(value) && value.savingsAdditivity !== undefined;
|
|
104
|
+
/** Validates the closed coverage-state union and state-specific freshness rules. */
|
|
105
|
+
export const isEnvironmentCoverageStateV1 = (value) => {
|
|
106
|
+
if (!isRecord(value) || typeof value.status !== 'string')
|
|
107
|
+
return false;
|
|
108
|
+
const hasValidFreshness = (value.observedAt === undefined || isCanonicalUtcTimestamp(value.observedAt)) &&
|
|
109
|
+
(value.completeThrough === undefined || isCanonicalUtcTimestamp(value.completeThrough)) &&
|
|
110
|
+
(value.observedAt === undefined ||
|
|
111
|
+
value.completeThrough === undefined ||
|
|
112
|
+
Date.parse(value.completeThrough) <= Date.parse(value.observedAt));
|
|
113
|
+
if (value.status === 'complete') {
|
|
114
|
+
return hasExactKeys(value, ['status'], ['observedAt', 'completeThrough']) && hasValidFreshness;
|
|
115
|
+
}
|
|
116
|
+
if (value.status === 'partial') {
|
|
117
|
+
return (hasExactKeys(value, ['status', 'reason'], ['observedAt', 'completeThrough']) &&
|
|
118
|
+
isCustomerString(value.reason) &&
|
|
119
|
+
value.reason.length > 0 &&
|
|
120
|
+
hasValidFreshness);
|
|
121
|
+
}
|
|
122
|
+
if (value.status === 'stale') {
|
|
123
|
+
return (hasExactKeys(value, ['status', 'reason', 'observedAt'], ['completeThrough']) &&
|
|
124
|
+
isCustomerString(value.reason) &&
|
|
125
|
+
value.reason.length > 0 &&
|
|
126
|
+
hasValidFreshness);
|
|
127
|
+
}
|
|
128
|
+
if (value.status === 'unavailable' || value.status === 'not-collected') {
|
|
129
|
+
return hasExactKeys(value, ['status', 'reason']) && isCustomerString(value.reason) && value.reason.length > 0;
|
|
130
|
+
}
|
|
131
|
+
return false;
|
|
132
|
+
};
|
|
133
|
+
const isBoundedList = (value, itemValidator) => {
|
|
134
|
+
if (!isRecord(value) || !hasExactKeys(value, ['items', 'totalCount', 'includedCount', 'truncated'], ['continuationReference']))
|
|
135
|
+
return false;
|
|
136
|
+
if (!Array.isArray(value.items) || value.items.length > ENVIRONMENT_CONTRACT_LIMITS_V1.boundedListItems || !value.items.every(itemValidator))
|
|
137
|
+
return false;
|
|
138
|
+
if (!isNonNegativeInteger(value.totalCount) || !isNonNegativeInteger(value.includedCount))
|
|
139
|
+
return false;
|
|
140
|
+
if (value.includedCount !== value.items.length || value.totalCount < value.includedCount || typeof value.truncated !== 'boolean')
|
|
141
|
+
return false;
|
|
142
|
+
if (value.truncated !== value.totalCount > value.includedCount)
|
|
143
|
+
return false;
|
|
144
|
+
if (value.continuationReference !== undefined && !isEnvironmentLogicalEvidenceReferenceV1(value.continuationReference))
|
|
145
|
+
return false;
|
|
146
|
+
return !value.truncated || value.continuationReference !== undefined;
|
|
147
|
+
};
|
|
148
|
+
const isCostRollup = (value) => isRecord(value) &&
|
|
149
|
+
hasExactKeys(value, ['key', 'safeLabel', 'resourceCount', 'sourceReferences'], ['observedCost', 'potentialSavings']) &&
|
|
150
|
+
isGeneralKey(value.key) &&
|
|
151
|
+
isSafeLabel(value.safeLabel) &&
|
|
152
|
+
isNonNegativeInteger(value.resourceCount) &&
|
|
153
|
+
(value.observedCost === undefined || isObservedMoney(value.observedCost)) &&
|
|
154
|
+
(value.potentialSavings === undefined || isSavingsMoney(value.potentialSavings)) &&
|
|
155
|
+
isReferenceArray(value.sourceReferences);
|
|
156
|
+
const isCostDriver = (value) => isRecord(value) &&
|
|
157
|
+
hasExactKeys(value, ['key', 'safeLabel', 'sourceReferences'], ['description', 'observedCost', 'portalRoute', 'resourceReference']) &&
|
|
158
|
+
isGeneralKey(value.key) &&
|
|
159
|
+
isSafeLabel(value.safeLabel) &&
|
|
160
|
+
(value.description === undefined || isCustomerString(value.description)) &&
|
|
161
|
+
(value.observedCost === undefined || isObservedMoney(value.observedCost)) &&
|
|
162
|
+
(value.portalRoute === undefined || isEnvironmentPortalRouteV1(value.portalRoute)) &&
|
|
163
|
+
(value.resourceReference === undefined || isEnvironmentLogicalResourceReferenceV1(value.resourceReference)) &&
|
|
164
|
+
isReferenceArray(value.sourceReferences);
|
|
165
|
+
const isRecommendation = (value) => isRecord(value) &&
|
|
166
|
+
hasExactKeys(value, ['recommendationId', 'safeLabel', 'portalRoute', 'sourceReferences'], ['description', 'potentialSavings']) &&
|
|
167
|
+
isScopeIdentifier(value.recommendationId) &&
|
|
168
|
+
isSafeLabel(value.safeLabel) &&
|
|
169
|
+
isEnvironmentPortalRouteV1(value.portalRoute) &&
|
|
170
|
+
(value.description === undefined || isCustomerString(value.description)) &&
|
|
171
|
+
(value.potentialSavings === undefined || isSavingsMoney(value.potentialSavings)) &&
|
|
172
|
+
isReferenceArray(value.sourceReferences);
|
|
173
|
+
const isCostChange = (value) => isRecord(value) &&
|
|
174
|
+
hasExactKeys(value, ['key', 'safeLabel', 'description', 'direction', 'sourceReferences'], ['amount']) &&
|
|
175
|
+
isGeneralKey(value.key) &&
|
|
176
|
+
isSafeLabel(value.safeLabel) &&
|
|
177
|
+
isCustomerString(value.description) &&
|
|
178
|
+
typeof value.direction === 'string' &&
|
|
179
|
+
CHANGE_DIRECTIONS.has(value.direction) &&
|
|
180
|
+
(value.amount === undefined || isObservedMoney(value.amount)) &&
|
|
181
|
+
isReferenceArray(value.sourceReferences);
|
|
182
|
+
const isWarning = (value) => isRecord(value) &&
|
|
183
|
+
hasExactKeys(value, ['code', 'safeLabel', 'sourceReferences'], ['detail']) &&
|
|
184
|
+
isGeneralKey(value.code) &&
|
|
185
|
+
isSafeLabel(value.safeLabel) &&
|
|
186
|
+
(value.detail === undefined || isCustomerString(value.detail)) &&
|
|
187
|
+
isReferenceArray(value.sourceReferences);
|
|
188
|
+
const isSourceCoverage = (value) => isRecord(value) &&
|
|
189
|
+
hasExactKeys(value, ['subscriptionSummary', 'resources', 'recommendations', 'costs', 'savings']) &&
|
|
190
|
+
isEnvironmentCoverageStateV1(value.subscriptionSummary) &&
|
|
191
|
+
isEnvironmentCoverageStateV1(value.resources) &&
|
|
192
|
+
isEnvironmentCoverageStateV1(value.recommendations) &&
|
|
193
|
+
isEnvironmentCoverageStateV1(value.costs) &&
|
|
194
|
+
isEnvironmentCoverageStateV1(value.savings);
|
|
195
|
+
const isCostSummary = (value) => isRecord(value) &&
|
|
196
|
+
hasExactKeys(value, [], ['observedCost', 'potentialSavings', 'resourceCount', 'recommendationCount']) &&
|
|
197
|
+
(value.observedCost === undefined || isObservedMoney(value.observedCost)) &&
|
|
198
|
+
(value.potentialSavings === undefined || isSavingsMoney(value.potentialSavings)) &&
|
|
199
|
+
(value.resourceCount === undefined || isNonNegativeInteger(value.resourceCount)) &&
|
|
200
|
+
(value.recommendationCount === undefined || isNonNegativeInteger(value.recommendationCount));
|
|
201
|
+
/** Validates the strict, bounded phase-one subscription-cost projection. */
|
|
202
|
+
export const isEnvironmentSubscriptionCostProjectionV1 = (value) => {
|
|
203
|
+
if (!hasSafeContainerShape(value) || !isRecord(value))
|
|
204
|
+
return false;
|
|
205
|
+
if (!hasExactKeys(value, [
|
|
206
|
+
'schemaVersion',
|
|
207
|
+
'scope',
|
|
208
|
+
'sourceBinding',
|
|
209
|
+
'generatedAt',
|
|
210
|
+
'subscription',
|
|
211
|
+
'sourceCoverage',
|
|
212
|
+
'costSummary',
|
|
213
|
+
'serviceFamilyRollups',
|
|
214
|
+
'estateCostRollups',
|
|
215
|
+
'costDrivers',
|
|
216
|
+
'recommendations',
|
|
217
|
+
'changes',
|
|
218
|
+
'warnings',
|
|
219
|
+
'sourceReferences',
|
|
220
|
+
]) ||
|
|
221
|
+
value.schemaVersion !== 1 ||
|
|
222
|
+
!isEnvironmentScopeV1(value.scope) ||
|
|
223
|
+
!isEnvironmentSourceBindingV1(value.sourceBinding) ||
|
|
224
|
+
!scopesEqual(value.scope, value.sourceBinding.scope) ||
|
|
225
|
+
!isCanonicalUtcTimestamp(value.generatedAt) ||
|
|
226
|
+
Date.parse(value.generatedAt) < Date.parse(value.sourceBinding.completedAt) ||
|
|
227
|
+
!isRecord(value.subscription) ||
|
|
228
|
+
!hasExactKeys(value.subscription, ['safeLabel', 'portalRoute']) ||
|
|
229
|
+
!isSafeLabel(value.subscription.safeLabel) ||
|
|
230
|
+
!isEnvironmentPortalRouteV1(value.subscription.portalRoute) ||
|
|
231
|
+
!isSourceCoverage(value.sourceCoverage) ||
|
|
232
|
+
!isCostSummary(value.costSummary) ||
|
|
233
|
+
!isBoundedList(value.serviceFamilyRollups, isCostRollup) ||
|
|
234
|
+
!isBoundedList(value.estateCostRollups, isCostRollup) ||
|
|
235
|
+
!isBoundedList(value.costDrivers, isCostDriver) ||
|
|
236
|
+
!isBoundedList(value.recommendations, isRecommendation) ||
|
|
237
|
+
!isBoundedList(value.changes, isCostChange) ||
|
|
238
|
+
!isBoundedList(value.warnings, isWarning) ||
|
|
239
|
+
!isReferenceArray(value.sourceReferences)) {
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
return utf8ByteLength(JSON.stringify(value)) <= ENVIRONMENT_CONTRACT_LIMITS_V1.projectionBytes;
|
|
243
|
+
};
|
|
244
|
+
const descriptorByteLimit = (name) => {
|
|
245
|
+
if (name === 'projection.json')
|
|
246
|
+
return ENVIRONMENT_CONTRACT_LIMITS_V1.projectionBytes;
|
|
247
|
+
if (name === 'environment-index.md')
|
|
248
|
+
return ENVIRONMENT_CONTRACT_LIMITS_V1.environmentIndexBytes;
|
|
249
|
+
return ENVIRONMENT_CONTRACT_LIMITS_V1.costPillarBytes;
|
|
250
|
+
};
|
|
251
|
+
/** Validates one descriptor from the exact V1 three-document allowlist. */
|
|
252
|
+
export const isEnvironmentDocumentDescriptorV1 = (value) => {
|
|
253
|
+
if (!isRecord(value) || !hasExactKeys(value, ['name', 'mediaType', 'byteCount', 'contentSha256', 'approximateTokenCount']))
|
|
254
|
+
return false;
|
|
255
|
+
if (typeof value.name !== 'string' || !DOCUMENT_NAMES.has(value.name))
|
|
256
|
+
return false;
|
|
257
|
+
const expectedMediaType = value.name === 'projection.json' ? 'application/json' : 'text/markdown; charset=utf-8';
|
|
258
|
+
return (value.mediaType === expectedMediaType &&
|
|
259
|
+
isNonNegativeInteger(value.byteCount) &&
|
|
260
|
+
value.byteCount > 0 &&
|
|
261
|
+
value.byteCount <= descriptorByteLimit(value.name) &&
|
|
262
|
+
typeof value.contentSha256 === 'string' &&
|
|
263
|
+
SHA256_PATTERN.test(value.contentSha256) &&
|
|
264
|
+
isNonNegativeInteger(value.approximateTokenCount));
|
|
265
|
+
};
|
|
266
|
+
/** Validates that descriptors contain each allowlisted V1 document exactly once. */
|
|
267
|
+
export const isEnvironmentDocumentDescriptorSetV1 = (value) => Array.isArray(value) &&
|
|
268
|
+
value.length === ENVIRONMENT_DOCUMENT_NAMES_V1.length &&
|
|
269
|
+
value.every(isEnvironmentDocumentDescriptorV1) &&
|
|
270
|
+
new Set(value.map(descriptor => descriptor.name)).size === ENVIRONMENT_DOCUMENT_NAMES_V1.length &&
|
|
271
|
+
ENVIRONMENT_DOCUMENT_NAMES_V1.every(name => value.some(descriptor => descriptor.name === name));
|
|
272
|
+
/** Validates an atomically visible completed environment-generation pointer. */
|
|
273
|
+
export const isEnvironmentCompiledGenerationPointerV1 = (value) => {
|
|
274
|
+
if (!hasSafeContainerShape(value) || !isRecord(value))
|
|
275
|
+
return false;
|
|
276
|
+
if (!hasExactKeys(value, ['schemaVersion', 'status', 'environmentRunId', 'scope', 'sourceBinding', 'treeDigestSha256', 'fileCount', 'generatedAt']) ||
|
|
277
|
+
value.schemaVersion !== 1 ||
|
|
278
|
+
value.status !== 'completed' ||
|
|
279
|
+
!isEnvironmentRunIdV1(value.environmentRunId) ||
|
|
280
|
+
!isEnvironmentScopeV1(value.scope) ||
|
|
281
|
+
!isEnvironmentSourceBindingV1(value.sourceBinding) ||
|
|
282
|
+
!scopesEqual(value.scope, value.sourceBinding.scope) ||
|
|
283
|
+
typeof value.treeDigestSha256 !== 'string' ||
|
|
284
|
+
!SHA256_PATTERN.test(value.treeDigestSha256) ||
|
|
285
|
+
value.fileCount !== 3 ||
|
|
286
|
+
!isCanonicalUtcTimestamp(value.generatedAt) ||
|
|
287
|
+
Date.parse(value.generatedAt) < Date.parse(value.sourceBinding.completedAt)) {
|
|
288
|
+
return false;
|
|
289
|
+
}
|
|
290
|
+
const sourceIdentities = [
|
|
291
|
+
value.sourceBinding.publicationId,
|
|
292
|
+
value.sourceBinding.portalRunId,
|
|
293
|
+
value.sourceBinding.pluginRunId,
|
|
294
|
+
value.sourceBinding.economicsGenerationId,
|
|
295
|
+
value.sourceBinding.economicsFingerprint,
|
|
296
|
+
];
|
|
297
|
+
return (!sourceIdentities.includes(value.environmentRunId) &&
|
|
298
|
+
utf8ByteLength(JSON.stringify(value)) <= ENVIRONMENT_CONTRACT_LIMITS_V1.completedPointerBytes);
|
|
299
|
+
};
|
|
300
|
+
/** Validates the artifact kind without widening the closed V1 union. */
|
|
301
|
+
export const isEnvironmentArtifactKindV1 = (value) => typeof value === 'string' && ARTIFACT_KINDS.has(value);
|
|
302
|
+
/** Validates the V1 safe-label bound used by customer-visible evidence metadata. */
|
|
303
|
+
export const isEnvironmentSafeLabelV1 = (value) => isSafeLabel(value);
|
package/dist/esm/index.js
CHANGED
|
@@ -44,6 +44,7 @@ export * from './azure/storage-inventory.js';
|
|
|
44
44
|
export * from './azure/governance.js';
|
|
45
45
|
export * from './azure/publicIpAddresses.js';
|
|
46
46
|
export * from './azure/workManagement.js';
|
|
47
|
+
export * from './azure/vmPatchManagement.js';
|
|
47
48
|
export * from './aws/index.js';
|
|
48
49
|
export * from './accounts/index.js';
|
|
49
50
|
export * from './assessments/index.js';
|
|
@@ -54,6 +55,7 @@ export * from './company/index.js';
|
|
|
54
55
|
export * from './customProperties/index.js';
|
|
55
56
|
export * from './scheduler/index.js';
|
|
56
57
|
export * from './ai/index.js';
|
|
58
|
+
export * from './environment/index.js';
|
|
57
59
|
export * from './events/index.js';
|
|
58
60
|
export * from './integrations/index.js';
|
|
59
61
|
export * from './identity/index.js';
|
package/dist/index.d.ts
CHANGED
|
@@ -44,6 +44,7 @@ export * from './azure/storage-inventory';
|
|
|
44
44
|
export * from './azure/governance';
|
|
45
45
|
export * from './azure/publicIpAddresses';
|
|
46
46
|
export * from './azure/workManagement';
|
|
47
|
+
export * from './azure/vmPatchManagement';
|
|
47
48
|
export * from './aws';
|
|
48
49
|
export * from './accounts';
|
|
49
50
|
export * from './assessments';
|
|
@@ -54,6 +55,7 @@ export * from './company';
|
|
|
54
55
|
export * from './customProperties';
|
|
55
56
|
export * from './scheduler';
|
|
56
57
|
export * from './ai';
|
|
58
|
+
export * from './environment';
|
|
57
59
|
export * from './events';
|
|
58
60
|
export * from './integrations';
|
|
59
61
|
export * from './identity';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,cAAc,gBAAgB,CAAC;AAC/B,cAAc,yBAAyB,CAAC;AACxC,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC;AAChC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mCAAmC,CAAC;AAClD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,yCAAyC,CAAC;AACxD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,yBAAyB,CAAC;AACxC,cAAc,wBAAwB,CAAC;AACvC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,yCAAyC,CAAC;AACxD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oBAAoB,CAAC;AACnC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AACvC,cAAc,OAAO,CAAC;AACtB,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,2BAA2B,CAAC;AAC1C,cAAc,WAAW,CAAC;AAC1B,cAAc,oBAAoB,CAAC;AACnC,cAAc,aAAa,CAAC;AAC5B,cAAc,MAAM,CAAC;AACrB,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,4BAA4B,CAAC;AAC3C,cAAc,QAAQ,CAAC;AACvB,cAAc,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,cAAc,gBAAgB,CAAC;AAC/B,cAAc,yBAAyB,CAAC;AACxC,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC;AAChC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mCAAmC,CAAC;AAClD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,yCAAyC,CAAC;AACxD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,yBAAyB,CAAC;AACxC,cAAc,wBAAwB,CAAC;AACvC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,yCAAyC,CAAC;AACxD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oBAAoB,CAAC;AACnC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AACvC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,OAAO,CAAC;AACtB,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,2BAA2B,CAAC;AAC1C,cAAc,WAAW,CAAC;AAC1B,cAAc,oBAAoB,CAAC;AACnC,cAAc,aAAa,CAAC;AAC5B,cAAc,MAAM,CAAC;AACrB,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,4BAA4B,CAAC;AAC3C,cAAc,QAAQ,CAAC;AACvB,cAAc,aAAa,CAAC"}
|