@spottoai/types-package 1.0.2-beta.427 → 1.0.2-beta.429

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/dist/azure/dataProtection.d.ts +40 -0
  2. package/dist/azure/dataProtection.d.ts.map +1 -1
  3. package/dist/azure/metrics.d.ts +2 -27
  4. package/dist/azure/metrics.d.ts.map +1 -1
  5. package/dist/azure/payloads.d.ts +7 -7
  6. package/dist/azure/payloads.d.ts.map +1 -1
  7. package/dist/azure/payloads.js +22 -23
  8. package/dist/azure/payloads.js.map +1 -1
  9. package/dist/azure/reportEvidence.d.ts +7 -5
  10. package/dist/azure/reportEvidence.d.ts.map +1 -1
  11. package/dist/azure/reportEvidence.js.map +1 -1
  12. package/dist/azure/reportEvidenceValidation.d.ts.map +1 -1
  13. package/dist/azure/reportEvidenceValidation.js +5 -1
  14. package/dist/azure/reportEvidenceValidation.js.map +1 -1
  15. package/dist/azure/reportEvidenceValidationHelpers.d.ts +3 -16
  16. package/dist/azure/reportEvidenceValidationHelpers.d.ts.map +1 -1
  17. package/dist/azure/reportEvidenceValidationHelpers.js +79 -95
  18. package/dist/azure/reportEvidenceValidationHelpers.js.map +1 -1
  19. package/dist/azure/resilienceFacts.d.ts +100 -0
  20. package/dist/azure/resilienceFacts.d.ts.map +1 -0
  21. package/dist/azure/resilienceFacts.js +20 -0
  22. package/dist/azure/resilienceFacts.js.map +1 -0
  23. package/dist/azure/resilienceFactsValidation.d.ts +4 -0
  24. package/dist/azure/resilienceFactsValidation.d.ts.map +1 -0
  25. package/dist/azure/resilienceFactsValidation.js +70 -0
  26. package/dist/azure/resilienceFactsValidation.js.map +1 -0
  27. package/dist/azure/views.d.ts +7 -0
  28. package/dist/azure/views.d.ts.map +1 -1
  29. package/dist/azure/views.js.map +1 -1
  30. package/dist/common/boundedRows.d.ts +7 -0
  31. package/dist/common/boundedRows.d.ts.map +1 -0
  32. package/dist/{scheduler/scheduler.js → common/boundedRows.js} +1 -1
  33. package/dist/common/boundedRows.js.map +1 -0
  34. package/dist/common/metricStats.d.ts +29 -0
  35. package/dist/common/metricStats.d.ts.map +1 -0
  36. package/dist/{scheduler/bastionSchedule.js → common/metricStats.js} +1 -1
  37. package/dist/common/metricStats.js.map +1 -0
  38. package/dist/common/utilizationStories.d.ts +516 -0
  39. package/dist/common/utilizationStories.d.ts.map +1 -0
  40. package/dist/common/utilizationStories.js +21 -0
  41. package/dist/common/utilizationStories.js.map +1 -0
  42. package/dist/common/utilizationStoriesValidation.d.ts +48 -0
  43. package/dist/common/utilizationStoriesValidation.d.ts.map +1 -0
  44. package/dist/common/utilizationStoriesValidation.js +552 -0
  45. package/dist/common/utilizationStoriesValidation.js.map +1 -0
  46. package/dist/common/validationHelpers.d.ts +18 -0
  47. package/dist/common/validationHelpers.d.ts.map +1 -0
  48. package/dist/common/validationHelpers.js +36 -0
  49. package/dist/common/validationHelpers.js.map +1 -0
  50. package/dist/esm/azure/payloads.js +19 -20
  51. package/dist/esm/azure/reportEvidenceValidation.js +5 -1
  52. package/dist/esm/azure/reportEvidenceValidationHelpers.js +2 -18
  53. package/dist/esm/azure/resilienceFacts.js +16 -0
  54. package/dist/esm/azure/resilienceFactsValidation.js +64 -0
  55. package/dist/esm/common/utilizationStories.js +17 -0
  56. package/dist/esm/common/utilizationStoriesValidation.js +514 -0
  57. package/dist/esm/common/validationHelpers.js +18 -0
  58. package/dist/esm/index.js +4 -3
  59. package/dist/esm/scheduler/index.js +2 -1
  60. package/dist/esm/scheduler/resourceStrategyCapabilityValidation.js +4 -20
  61. package/dist/esm/scheduler/resourceStrategyContracts.js +4 -2
  62. package/dist/esm/scheduler/resourceStrategyScheduleValidation.js +109 -4
  63. package/dist/esm/scheduler/schedulerContracts.js +1 -0
  64. package/dist/esm/scheduler/schedulerValidation.js +363 -0
  65. package/dist/index.d.ts +4 -3
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +4 -3
  68. package/dist/index.js.map +1 -1
  69. package/dist/scheduler/index.d.ts +2 -1
  70. package/dist/scheduler/index.d.ts.map +1 -1
  71. package/dist/scheduler/index.js +2 -1
  72. package/dist/scheduler/index.js.map +1 -1
  73. package/dist/scheduler/resourceStrategyCapabilityValidation.d.ts.map +1 -1
  74. package/dist/scheduler/resourceStrategyCapabilityValidation.js +4 -20
  75. package/dist/scheduler/resourceStrategyCapabilityValidation.js.map +1 -1
  76. package/dist/scheduler/resourceStrategyContracts.d.ts +26 -9
  77. package/dist/scheduler/resourceStrategyContracts.d.ts.map +1 -1
  78. package/dist/scheduler/resourceStrategyContracts.js +4 -2
  79. package/dist/scheduler/resourceStrategyContracts.js.map +1 -1
  80. package/dist/scheduler/resourceStrategyScheduleValidation.d.ts +3 -1
  81. package/dist/scheduler/resourceStrategyScheduleValidation.d.ts.map +1 -1
  82. package/dist/scheduler/resourceStrategyScheduleValidation.js +110 -4
  83. package/dist/scheduler/resourceStrategyScheduleValidation.js.map +1 -1
  84. package/dist/scheduler/schedulerContracts.d.ts +178 -0
  85. package/dist/scheduler/schedulerContracts.d.ts.map +1 -0
  86. package/dist/scheduler/schedulerContracts.js +3 -0
  87. package/dist/scheduler/schedulerContracts.js.map +1 -0
  88. package/dist/scheduler/schedulerValidation.d.ts +14 -0
  89. package/dist/scheduler/schedulerValidation.d.ts.map +1 -0
  90. package/dist/scheduler/schedulerValidation.js +378 -0
  91. package/dist/scheduler/schedulerValidation.js.map +1 -0
  92. package/package.json +5 -3
  93. package/dist/esm/scheduler/bastionScheduleProfile.js +0 -206
  94. package/dist/esm/scheduler/bastionScheduleValidation.js +0 -399
  95. package/dist/scheduler/bastionSchedule.d.ts +0 -113
  96. package/dist/scheduler/bastionSchedule.d.ts.map +0 -1
  97. package/dist/scheduler/bastionSchedule.js.map +0 -1
  98. package/dist/scheduler/bastionScheduleProfile.d.ts +0 -24
  99. package/dist/scheduler/bastionScheduleProfile.d.ts.map +0 -1
  100. package/dist/scheduler/bastionScheduleProfile.js +0 -210
  101. package/dist/scheduler/bastionScheduleProfile.js.map +0 -1
  102. package/dist/scheduler/bastionScheduleValidation.d.ts +0 -10
  103. package/dist/scheduler/bastionScheduleValidation.d.ts.map +0 -1
  104. package/dist/scheduler/bastionScheduleValidation.js +0 -410
  105. package/dist/scheduler/bastionScheduleValidation.js.map +0 -1
  106. package/dist/scheduler/scheduler.d.ts +0 -267
  107. package/dist/scheduler/scheduler.d.ts.map +0 -1
  108. package/dist/scheduler/scheduler.js.map +0 -1
  109. /package/dist/esm/{scheduler/bastionSchedule.js → common/boundedRows.js} +0 -0
  110. /package/dist/esm/{scheduler/scheduler.js → common/metricStats.js} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validationHelpers.d.ts","sourceRoot":"","sources":["../../src/common/validationHelpers.ts"],"names":[],"mappings":"AAAA,qFAAqF;AACrF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEvD,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEjD,eAAO,MAAM,QAAQ,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,UAAkF,CAAC;AACtI,eAAO,MAAM,QAAQ,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MAA8D,CAAC;AAClH,eAAO,MAAM,gBAAgB,GAAI,OAAO,OAAO,KAAG,OAAiD,CAAC;AACpG,eAAO,MAAM,cAAc,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MAA6D,CAAC;AACvH,eAAO,MAAM,sBAAsB,GAAI,OAAO,OAAO,KAAG,OAAuD,CAAC;AAChH,eAAO,MAAM,iBAAiB,GAAI,OAAO,OAAO,KAAG,OAA4D,CAAC;AAChH,eAAO,MAAM,OAAO,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MAAwE,CAAC;AAC3H,eAAO,MAAM,UAAU,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MAA+D,CAAC;AACrH,eAAO,MAAM,aAAa,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MAAM,EAAmD,CAAC;AAClH,eAAO,MAAM,aAAa,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAA2D,CAAC;AACzI,eAAO,MAAM,UAAU,GAAI,OAAO,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAG,MAAyE,CAAC;AACrI,eAAO,MAAM,kBAAkB,GAAI,OAAO,UAAU,EAAE,MAAM,SAAS,MAAM,EAAE,KAAG,OAA0D,CAAC;AAC3I,eAAO,MAAM,kBAAkB,GAAI,OAAO,UAAU,EAAE,MAAM,SAAS,MAAM,EAAE,KAAG,OAAgE,CAAC;AAEjJ,eAAO,MAAM,aAAa,GAAI,CAAC,EAAE,OAAO,OAAO,EAAE,OAAO,MAAM,EAAE,OAAO,CAAC,GAAG,EAAE,OAAO,KAAK,GAAG,IAAI,CAAC,KAAG,KAAK,IAAI,iBAAiB,CAAC,CAAC,CAG/H,CAAC"}
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isBoundedRows = exports.hasOptionalNumbers = exports.hasOptionalStrings = exports.countTotal = exports.isCountRecord = exports.isStringArray = exports.isDateTime = exports.isCount = exports.isOptionalBoolean = exports.isOptionalFiniteNumber = exports.isFiniteNumber = exports.isOptionalString = exports.isString = exports.isRecord = void 0;
4
+ const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
5
+ exports.isRecord = isRecord;
6
+ const isString = (value) => typeof value === 'string' && value.trim().length > 0;
7
+ exports.isString = isString;
8
+ const isOptionalString = (value) => value === undefined || (0, exports.isString)(value);
9
+ exports.isOptionalString = isOptionalString;
10
+ const isFiniteNumber = (value) => typeof value === 'number' && Number.isFinite(value);
11
+ exports.isFiniteNumber = isFiniteNumber;
12
+ const isOptionalFiniteNumber = (value) => value === undefined || (0, exports.isFiniteNumber)(value);
13
+ exports.isOptionalFiniteNumber = isOptionalFiniteNumber;
14
+ const isOptionalBoolean = (value) => value === undefined || typeof value === 'boolean';
15
+ exports.isOptionalBoolean = isOptionalBoolean;
16
+ const isCount = (value) => (0, exports.isFiniteNumber)(value) && Number.isInteger(value) && value >= 0;
17
+ exports.isCount = isCount;
18
+ const isDateTime = (value) => (0, exports.isString)(value) && Number.isFinite(Date.parse(value));
19
+ exports.isDateTime = isDateTime;
20
+ const isStringArray = (value) => Array.isArray(value) && value.every(exports.isString);
21
+ exports.isStringArray = isStringArray;
22
+ const isCountRecord = (value) => (0, exports.isRecord)(value) && Object.values(value).every(exports.isCount);
23
+ exports.isCountRecord = isCountRecord;
24
+ const countTotal = (value) => Object.values(value).reduce((total, count) => total + count, 0);
25
+ exports.countTotal = countTotal;
26
+ const hasOptionalStrings = (value, keys) => keys.every(key => (0, exports.isOptionalString)(value[key]));
27
+ exports.hasOptionalStrings = hasOptionalStrings;
28
+ const hasOptionalNumbers = (value, keys) => keys.every(key => (0, exports.isOptionalFiniteNumber)(value[key]));
29
+ exports.hasOptionalNumbers = hasOptionalNumbers;
30
+ const isBoundedRows = (value, limit, isRow) => {
31
+ if (!(0, exports.isRecord)(value) || !(0, exports.isCount)(value.totalCount) || !(0, exports.isCount)(value.omittedCount) || !Array.isArray(value.rows))
32
+ return false;
33
+ return value.rows.length <= limit && value.rows.every(isRow) && value.totalCount === value.rows.length + value.omittedCount;
34
+ };
35
+ exports.isBoundedRows = isBoundedRows;
36
+ //# sourceMappingURL=validationHelpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validationHelpers.js","sourceRoot":"","sources":["../../src/common/validationHelpers.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
- const RESOURCE_SCHEDULER_TICK_PREFIX_V1 = 'resource-scheduler:tick:';
2
- const RESOURCE_SCHEDULER_TICK_MAX_ID_LENGTH_V1 = 128;
3
- const RESOURCE_SCHEDULER_TICK_KEYS_V1 = [
1
+ const SCHEDULER_TICK_PREFIX_V1 = 'scheduler:tick:';
2
+ const SCHEDULER_TICK_MAX_ID_LENGTH_V1 = 128;
3
+ const SCHEDULER_TICK_KEYS_V1 = [
4
4
  'schemaVersion',
5
5
  'entity',
6
6
  'action',
@@ -19,34 +19,33 @@ const isCanonicalUtcTimestampMilliseconds = (value) => {
19
19
  const parsed = Date.parse(value);
20
20
  return Number.isFinite(parsed) && new Date(parsed).toISOString() === value;
21
21
  };
22
- const hasExactResourceSchedulerTickKeysV1 = (value) => {
22
+ const hasExactSchedulerTickKeysV1 = (value) => {
23
23
  const keys = Object.keys(value);
24
- return (keys.length === RESOURCE_SCHEDULER_TICK_KEYS_V1.length &&
25
- RESOURCE_SCHEDULER_TICK_KEYS_V1.every(key => Object.prototype.hasOwnProperty.call(value, key)));
24
+ return keys.length === SCHEDULER_TICK_KEYS_V1.length && SCHEDULER_TICK_KEYS_V1.every(key => Object.prototype.hasOwnProperty.call(value, key));
26
25
  };
27
- const normalizeResourceSchedulerTickInstantV1 = (scheduledAt) => {
26
+ const normalizeSchedulerTickInstantV1 = (scheduledAt) => {
28
27
  if (typeof scheduledAt === 'string') {
29
28
  if (!isCanonicalUtcTimestampMilliseconds(scheduledAt)) {
30
- throw new TypeError('Resource scheduler tick time must be a canonical UTC timestamp with milliseconds');
29
+ throw new TypeError('Scheduler tick time must be a canonical UTC timestamp with milliseconds');
31
30
  }
32
31
  return scheduledAt;
33
32
  }
34
33
  const milliseconds = scheduledAt instanceof Date ? scheduledAt.getTime() : scheduledAt;
35
34
  if (!Number.isFinite(milliseconds))
36
- throw new TypeError('Resource scheduler tick time must be finite');
35
+ throw new TypeError('Scheduler tick time must be finite');
37
36
  const normalized = new Date(milliseconds).toISOString();
38
37
  if (!isCanonicalUtcTimestampMilliseconds(normalized)) {
39
- throw new TypeError('Resource scheduler tick time must use a four-digit UTC year');
38
+ throw new TypeError('Scheduler tick time must use a four-digit UTC year');
40
39
  }
41
40
  return normalized;
42
41
  };
43
42
  /** Creates the deterministic internal queue envelope for one cron occurrence. */
44
- export const createResourceSchedulerTickRequestMessageV1 = (scheduledAt) => {
45
- const scheduledAtUtc = normalizeResourceSchedulerTickInstantV1(scheduledAt);
46
- const tickId = `${RESOURCE_SCHEDULER_TICK_PREFIX_V1}${scheduledAtUtc}`;
43
+ export const createSchedulerTickRequestMessageV1 = (scheduledAt) => {
44
+ const scheduledAtUtc = normalizeSchedulerTickInstantV1(scheduledAt);
45
+ const tickId = `${SCHEDULER_TICK_PREFIX_V1}${scheduledAtUtc}`;
47
46
  return {
48
47
  schemaVersion: 1,
49
- entity: 'resource-scheduler',
48
+ entity: 'scheduler',
50
49
  action: 'tick',
51
50
  companyId: '*',
52
51
  cloudAccountId: '*',
@@ -57,25 +56,25 @@ export const createResourceSchedulerTickRequestMessageV1 = (scheduledAt) => {
57
56
  correlationId: tickId,
58
57
  };
59
58
  };
60
- /** Exact validator for the internal resource-scheduler tick queue boundary. */
61
- export const isResourceSchedulerTickRequestMessageV1 = (value) => {
59
+ /** Exact validator for the internal scheduler tick queue boundary. */
60
+ export const isSchedulerTickRequestMessageV1 = (value) => {
62
61
  try {
63
62
  if (typeof value !== 'object' || value === null || Array.isArray(value))
64
63
  return false;
65
64
  const candidate = value;
66
- if (!hasExactResourceSchedulerTickKeysV1(candidate) || !isCanonicalUtcTimestampMilliseconds(candidate['scheduledAtUtc'])) {
65
+ if (!hasExactSchedulerTickKeysV1(candidate) || !isCanonicalUtcTimestampMilliseconds(candidate['scheduledAtUtc'])) {
67
66
  return false;
68
67
  }
69
- const expectedTickId = `${RESOURCE_SCHEDULER_TICK_PREFIX_V1}${candidate['scheduledAtUtc']}`;
68
+ const expectedTickId = `${SCHEDULER_TICK_PREFIX_V1}${candidate['scheduledAtUtc']}`;
70
69
  return (candidate['schemaVersion'] === 1 &&
71
- candidate['entity'] === 'resource-scheduler' &&
70
+ candidate['entity'] === 'scheduler' &&
72
71
  candidate['action'] === 'tick' &&
73
72
  candidate['companyId'] === '*' &&
74
73
  candidate['cloudAccountId'] === '*' &&
75
74
  candidate['tenantId'] === '*' &&
76
75
  candidate['clientId'] === '*' &&
77
76
  typeof candidate['tickId'] === 'string' &&
78
- candidate['tickId'].length <= RESOURCE_SCHEDULER_TICK_MAX_ID_LENGTH_V1 &&
77
+ candidate['tickId'].length <= SCHEDULER_TICK_MAX_ID_LENGTH_V1 &&
79
78
  candidate['tickId'] === expectedTickId &&
80
79
  candidate['correlationId'] === expectedTickId);
81
80
  }
@@ -3,6 +3,7 @@ import { isReportDailySpend } from './reportDailySpendValidation.js';
3
3
  import { isReportSavingsBasis, isReportSpendProjection } from './reportSpendValidation.js';
4
4
  import { REPORT_EVIDENCE_LIMITS, } from './reportEvidence.js';
5
5
  import { countTotal, hasOptionalNumbers, hasOptionalStrings, hasRequiredRecords, isBoundedRows, isCommitmentExpirySummary, isCostSummary, isCount, isCountRecord, isDateTime, isEvidenceReference, isFiniteNumber, isOptionalBoolean, isOptionalFiniteNumber, isOptionalString, isProjectionRows, isRecommendationSummary, isRecord, isResourceSummary, isRetirementSummary, isSourceFileStatus, isString, isStringArray, isTagCoverage, } from './reportEvidenceValidationHelpers.js';
6
+ import { isReportingStories, isStoryFingerprintRows } from '../common/utilizationStoriesValidation.js';
6
7
  export const isReportSecureScoreEvidence = (value) => {
7
8
  if (!isRecord(value) || !['available', 'unavailable', 'stale'].includes(value.status))
8
9
  return false;
@@ -260,6 +261,8 @@ const isReportingProjection = (value) => {
260
261
  return false;
261
262
  if (value.spend !== undefined && !isReportSpendProjection(value.spend))
262
263
  return false;
264
+ if (value.stories !== undefined && !isReportingStories(value.stories))
265
+ return false;
263
266
  if (value.costChangePeriods !== undefined &&
264
267
  (!isBoundedRows(value.costChangePeriods, REPORT_EVIDENCE_LIMITS.costChangePeriods, isCostChangePeriod) ||
265
268
  new Set(value.costChangePeriods.rows.map(period => period.period)).size !== value.costChangePeriods.rows.length)) {
@@ -448,7 +451,8 @@ export const isSubscriptionReportHistory = (value) => {
448
451
  !isDateTime(period.sourceGeneratedAt) ||
449
452
  !isHistoryMetrics(period.metrics) ||
450
453
  !isBoundedRows(period.recommendations, REPORT_EVIDENCE_LIMITS.historyRecommendations, isRecommendationFingerprint) ||
451
- (period.comparisonIdentities !== undefined && !isHistoryComparisonIdentities(period.comparisonIdentities))) {
454
+ (period.comparisonIdentities !== undefined && !isHistoryComparisonIdentities(period.comparisonIdentities)) ||
455
+ (period.stories !== undefined && !isStoryFingerprintRows(period.stories))) {
452
456
  return false;
453
457
  }
454
458
  if (period.recommendations.omittedCount === 0 &&
@@ -1,22 +1,6 @@
1
1
  import { REPORT_EVIDENCE_LIMITS } from './reportEvidence.js';
2
- export const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
3
- export const isString = (value) => typeof value === 'string' && value.trim().length > 0;
4
- export const isOptionalString = (value) => value === undefined || isString(value);
5
- export const isFiniteNumber = (value) => typeof value === 'number' && Number.isFinite(value);
6
- export const isOptionalFiniteNumber = (value) => value === undefined || isFiniteNumber(value);
7
- export const isOptionalBoolean = (value) => value === undefined || typeof value === 'boolean';
8
- export const isCount = (value) => isFiniteNumber(value) && Number.isInteger(value) && value >= 0;
9
- export const isDateTime = (value) => isString(value) && Number.isFinite(Date.parse(value));
10
- export const isStringArray = (value) => Array.isArray(value) && value.every(isString);
11
- export const isCountRecord = (value) => isRecord(value) && Object.values(value).every(isCount);
12
- export const countTotal = (value) => Object.values(value).reduce((total, count) => total + count, 0);
13
- export const hasOptionalStrings = (value, keys) => keys.every(key => isOptionalString(value[key]));
14
- export const hasOptionalNumbers = (value, keys) => keys.every(key => isOptionalFiniteNumber(value[key]));
15
- export const isBoundedRows = (value, limit, isRow) => {
16
- if (!isRecord(value) || !isCount(value.totalCount) || !isCount(value.omittedCount) || !Array.isArray(value.rows))
17
- return false;
18
- return value.rows.length <= limit && value.rows.every(isRow) && value.totalCount === value.rows.length + value.omittedCount;
19
- };
2
+ import { isBoundedRows, isCount, isCountRecord, isFiniteNumber, isOptionalFiniteNumber, isOptionalString, isRecord, isString, isStringArray, hasOptionalNumbers, hasOptionalStrings, } from '../common/validationHelpers.js';
3
+ export { isRecord, isString, isOptionalString, isFiniteNumber, isOptionalFiniteNumber, isOptionalBoolean, isCount, isDateTime, isStringArray, isCountRecord, countTotal, hasOptionalStrings, hasOptionalNumbers, isBoundedRows, } from '../common/validationHelpers.js';
20
4
  export const isProjectionRows = (value, limit = REPORT_EVIDENCE_LIMITS.detailRows) => isBoundedRows(value, limit, isRecord);
21
5
  export const hasRequiredRecords = (value, keys) => keys.every(key => isRecord(value[key]));
22
6
  export const isSourceFileStatus = (value) => isRecord(value) &&
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Resilience facts: the small per-resource property reads the resilience story needs that no other collection
3
+ * carries — storage blob-service data-protection settings and lifecycle management, Azure SQL database backup
4
+ * retention and failover groups. Collected in the reliability component beside the data-protection posture and
5
+ * published as `azure-portal/subscriptions/{id}/resilience-facts.json`. Consumed by the cloud-engine
6
+ * `ProtectionProfileBuilder` (`data-protection:<selector>` capability sources) and available to the resilience story page.
7
+ *
8
+ * Spec: `Specs/reporting/utilization-stories-types-package.md` (parent: core `specs/reporting/utilization-stories.md`).
9
+ * Additive: consumers tolerate unknown fields. Field semantics: `undefined` = not read (the request failed, was not
10
+ * applicable or was skipped) and resolves to an `unknown` capability; `null` = read and absent/disabled. A failed
11
+ * read must never be presented as "disabled".
12
+ */
13
+ export const RESILIENCE_FACTS_SCHEMA_VERSION = 1;
14
+ export const RESILIENCE_FACTS_SOURCE = 'AzureResilienceFacts';
15
+ export const RESILIENCE_FACTS_PORTAL_FILE = 'resilience-facts.json';
16
+ export const RESILIENCE_FACTS_RAW_FILE = 'data-protection/resilience-facts.json.gz';
@@ -0,0 +1,64 @@
1
+ import { isCount, isDateTime, isFiniteNumber, isRecord, isString, isStringArray } from '../common/validationHelpers.js';
2
+ import { RESILIENCE_FACTS_SCHEMA_VERSION, RESILIENCE_FACTS_SOURCE, } from './resilienceFacts.js';
3
+ const STATUSES = new Set(['collected', 'partial', 'failed', 'not-applicable']);
4
+ const isNullableBoolean = (value) => value === null || typeof value === 'boolean';
5
+ const isNullableNumber = (value) => value === null || isFiniteNumber(value);
6
+ const isOptionalNullableNumber = (value) => value === undefined || isNullableNumber(value);
7
+ const isOptionalNullableString = (value) => value === undefined || value === null || isString(value);
8
+ const isRetention = (value) => value === undefined || value === null || (isRecord(value) && typeof value.enabled === 'boolean' && isOptionalNullableNumber(value.days));
9
+ const isOptionalNullableBoolean = (value) => value === undefined || isNullableBoolean(value);
10
+ const isStorageFacts = (value) => isRecord(value) &&
11
+ isRetention(value.blobSoftDelete) &&
12
+ isRetention(value.containerSoftDelete) &&
13
+ isOptionalNullableBoolean(value.versioning) &&
14
+ isRetention(value.changeFeed) &&
15
+ isRetention(value.pointInTimeRestore) &&
16
+ isOptionalNullableString(value.kind) &&
17
+ (value.managementPolicy === undefined ||
18
+ value.managementPolicy === null ||
19
+ (isRecord(value.managementPolicy) &&
20
+ typeof value.managementPolicy.present === 'boolean' &&
21
+ (value.managementPolicy.ruleCount === undefined || isCount(value.managementPolicy.ruleCount))));
22
+ const isSqlFacts = (value) => isRecord(value) &&
23
+ isOptionalNullableNumber(value.shortTermRetentionDays) &&
24
+ isOptionalNullableNumber(value.diffBackupIntervalHours) &&
25
+ (value.longTermRetention === undefined ||
26
+ value.longTermRetention === null ||
27
+ (isRecord(value.longTermRetention) &&
28
+ typeof value.longTermRetention.enabled === 'boolean' &&
29
+ isOptionalNullableString(value.longTermRetention.weekly) &&
30
+ isOptionalNullableString(value.longTermRetention.monthly) &&
31
+ isOptionalNullableString(value.longTermRetention.yearly) &&
32
+ isOptionalNullableNumber(value.longTermRetention.weekOfYear))) &&
33
+ (value.failoverGroup === undefined ||
34
+ value.failoverGroup === null ||
35
+ (isRecord(value.failoverGroup) &&
36
+ isString(value.failoverGroup.id) &&
37
+ isString(value.failoverGroup.name) &&
38
+ isStringArray(value.failoverGroup.partnerServers) &&
39
+ isOptionalNullableString(value.failoverGroup.readWriteFailoverPolicy) &&
40
+ isOptionalNullableString(value.failoverGroup.role))) &&
41
+ isOptionalNullableString(value.backupStorageRedundancy);
42
+ export const isResilienceFactsItem = (value) => isRecord(value) &&
43
+ isString(value.resourceId) &&
44
+ isString(value.resourceType) &&
45
+ isString(value.status) &&
46
+ STATUSES.has(value.status) &&
47
+ isDateTime(value.observedAt) &&
48
+ isCount(value.requestCount) &&
49
+ (value.errors === undefined || isStringArray(value.errors)) &&
50
+ (value.storage === undefined || isStorageFacts(value.storage)) &&
51
+ (value.sql === undefined || isSqlFacts(value.sql));
52
+ export const isResilienceFactsProjection = (value) => isRecord(value) &&
53
+ value.schemaVersion === RESILIENCE_FACTS_SCHEMA_VERSION &&
54
+ value.source === RESILIENCE_FACTS_SOURCE &&
55
+ isString(value.subscriptionId) &&
56
+ (value.tenantId === undefined || isString(value.tenantId)) &&
57
+ isDateTime(value.generatedAt) &&
58
+ isRecord(value.summary) &&
59
+ (value.summary.notApplicable === undefined || isCount(value.summary.notApplicable)) &&
60
+ ['storageAccounts', 'sqlDatabases', 'sqlServers', 'collected', 'partial', 'failed', 'requestCount'].every(key => isCount(value.summary[key])) &&
61
+ Array.isArray(value.items) &&
62
+ value.items.every(isResilienceFactsItem) &&
63
+ Array.isArray(value.issues) &&
64
+ value.issues.every(issue => isRecord(issue) && (issue.severity === 'warning' || issue.severity === 'error') && isString(issue.code) && isString(issue.message));
@@ -0,0 +1,17 @@
1
+ export const STORY_KEYS = [
2
+ 'oversized-resources',
3
+ 'right-sku',
4
+ 'schedule-candidates',
5
+ 'resilience-recovery',
6
+ 'hybrid-benefit',
7
+ 'commitments',
8
+ ];
9
+ export const STORY_LIMITS = {
10
+ sectionRows: 1000,
11
+ sampleRows: 50,
12
+ windowDays: 30,
13
+ weeklyGridDays: 7,
14
+ weeklyGridHours: 24,
15
+ /** Story fingerprints retained per monthly history period (all stories together). */
16
+ historyFingerprints: 2000,
17
+ };