@oneuptime/common 12.0.22 → 12.0.24

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 (345) hide show
  1. package/Models/DatabaseModels/DetectionRule.ts +55 -0
  2. package/Models/DatabaseModels/Index.ts +2 -0
  3. package/Models/DatabaseModels/NetworkDevice.ts +44 -0
  4. package/Models/DatabaseModels/NetworkDeviceAutoImportRule.ts +584 -0
  5. package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +105 -0
  6. package/Models/DatabaseModels/Project.ts +68 -0
  7. package/Models/DatabaseModels/User.ts +68 -0
  8. package/Server/API/TelemetryAPI.ts +29 -0
  9. package/Server/Infrastructure/Postgres/SchemaMigrations/1789100000000-AddNetworkDeviceAutoImportRule.ts +60 -0
  10. package/Server/Infrastructure/Postgres/SchemaMigrations/1789200000000-AddSysObjectIdPatternToAutoImportRule.ts +26 -0
  11. package/Server/Infrastructure/Postgres/SchemaMigrations/1789300000000-AddAutoApplyVendorHealthTemplate.ts +26 -0
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.ts +28 -0
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.ts +49 -0
  14. package/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.ts +33 -0
  15. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +12 -0
  16. package/Server/Middleware/GzipRequestBody.ts +311 -0
  17. package/Server/Middleware/MultipartFormData.ts +87 -3
  18. package/Server/Services/DetectionRuleService.ts +73 -6
  19. package/Server/Services/Index.ts +207 -0
  20. package/Server/Services/NetworkDeviceAutoImportRuleEngineService.ts +917 -0
  21. package/Server/Services/NetworkDeviceAutoImportRuleService.ts +181 -0
  22. package/Server/Services/NetworkDeviceDiscoveryScanService.ts +59 -0
  23. package/Server/Services/NetworkDeviceService.ts +148 -0
  24. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +65 -0
  25. package/Server/Services/OnCallDutyPolicyFeedService.ts +1 -1
  26. package/Server/Services/ProjectService.ts +29 -19
  27. package/Server/Services/SecurityEventService.ts +90 -13
  28. package/Server/Services/StatusPageResourceService.ts +223 -0
  29. package/Server/Services/TelemetryAttributeService.ts +17 -0
  30. package/Server/Services/UserService.ts +4 -6
  31. package/Server/Types/Markdown.ts +161 -67
  32. package/Server/Utils/AI/Toolbox/OnCallTools.ts +12 -4
  33. package/Server/Utils/AnalyticsDatabase/Statement.ts +13 -1
  34. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +297 -6
  35. package/Server/Utils/Attribution.ts +4 -0
  36. package/Server/Utils/Marketing/MarketingEventUtil.ts +45 -7
  37. package/Server/Utils/Monitor/NetworkInventoryUtil.ts +39 -1
  38. package/Server/Utils/SecurityEvent/DetectionRuleEvaluator.ts +55 -3
  39. package/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.ts +21 -0
  40. package/Server/Utils/StartServer.ts +12 -32
  41. package/Server/Utils/Telemetry/LlmCostBudgetEvaluator.ts +101 -4
  42. package/Server/Utils/Telemetry/LlmMetricSpend.ts +130 -0
  43. package/Server/Views/Partials/AnalyticsConsent.ejs +159 -102
  44. package/Server/Views/Partials/Head.ejs +2 -1
  45. package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +776 -38
  46. package/Tests/App/Dashboard/CorrelateFilterBuilder.test.tsx +359 -0
  47. package/Tests/App/Dashboard/CorrelateGraph.test.tsx +865 -0
  48. package/Tests/App/Dashboard/CorrelationGraph.test.ts +346 -0
  49. package/Tests/App/Dashboard/CriteriaFilterConditionDropdown.test.tsx +256 -0
  50. package/Tests/App/Dashboard/CriteriaFilterDefaults.test.ts +738 -0
  51. package/Tests/App/Dashboard/CriteriaFilterMonitorTypeChange.test.ts +1122 -0
  52. package/Tests/App/Dashboard/DiscoveryScanWizardValidation.test.tsx +283 -0
  53. package/Tests/App/Dashboard/ExternalStatusPageCriteriaFilter.test.ts +407 -0
  54. package/Tests/App/Dashboard/MetricSeriesInvestigateMenu.test.tsx +5 -14
  55. package/Tests/App/Dashboard/MonitorCriteriaActionToggles.test.tsx +426 -0
  56. package/Tests/App/Dashboard/MonitorStepsCriteriaAlignmentWiring.test.tsx +352 -0
  57. package/Tests/App/Dashboard/OnCallLayerCardOverrides.test.tsx +278 -0
  58. package/Tests/App/Dashboard/OnCallLayerShiftPreviewOverrides.test.ts +237 -0
  59. package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +2 -2
  60. package/Tests/App/Dashboard/OnCallSchedulePreviewOverrides.test.tsx +460 -0
  61. package/Tests/App/Dashboard/SecurityEventAttributeUtil.test.ts +171 -0
  62. package/Tests/App/Dashboard/SecurityEventCorrelation.test.ts +857 -0
  63. package/Tests/App/Dashboard/SecurityEventDetailObservables.test.tsx +194 -0
  64. package/Tests/App/Dashboard/SecurityEventsDetectionRulesPage.test.tsx +124 -0
  65. package/Tests/App/Dashboard/SecurityEventsMonitorStepForm.test.tsx +79 -19
  66. package/Tests/App/Dashboard/SecurityEventsTableColumns.test.tsx +382 -0
  67. package/Tests/App/StatusPage/BulkAddStatusPageMonitors.test.ts +216 -0
  68. package/Tests/App/StatusPage/BulkAddStatusPageMonitorsModal.test.tsx +590 -11
  69. package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +757 -0
  70. package/Tests/Models/DiscoveryScanNameColumn.test.ts +293 -0
  71. package/Tests/Server/API/SecurityEventAttributesAPI.test.ts +564 -0
  72. package/Tests/Server/Infrastructure/Postgres/SchemaMigrationsOrdering.test.ts +120 -4
  73. package/Tests/Server/Middleware/GzipRequestBody.test.ts +725 -0
  74. package/Tests/Server/Middleware/MultipartFormData.test.ts +298 -0
  75. package/Tests/Server/Services/AddDetectionRuleDistinctCountColumnsMigration.test.ts +311 -0
  76. package/Tests/Server/Services/AutoImportScanCredentialSelect.test.ts +205 -0
  77. package/Tests/Server/Services/DetectionRuleService.test.ts +134 -2
  78. package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +7 -1
  79. package/Tests/Server/Services/FeedRetentionConsistency.test.ts +131 -0
  80. package/Tests/Server/Services/NetworkDeviceAutoImportRuleEngineService.test.ts +933 -0
  81. package/Tests/Server/Services/NetworkDeviceDiscoveryScanService.test.ts +194 -0
  82. package/Tests/Server/Services/NetworkDeviceMonitorStatusStamp.test.ts +800 -0
  83. package/Tests/Server/Services/OnCallScheduleAttachRosterRefresh.test.ts +244 -0
  84. package/Tests/Server/Services/SecurityEventService.test.ts +376 -0
  85. package/Tests/Server/Services/StatusPageResourceDuplicates.test.ts +703 -0
  86. package/Tests/Server/Services/TelemetryAttributeServiceSecurityEvent.test.ts +256 -0
  87. package/Tests/Server/Types/Workflow/Components/ComponentRegistryParity.test.ts +417 -0
  88. package/Tests/Server/Utils/AnalyticsDatabase/StatementGeneratorWhereOperators.test.ts +677 -0
  89. package/Tests/Server/Utils/Marketing/MarketingEventUtil.test.ts +43 -0
  90. package/Tests/Server/Utils/Monitor/Criteria/ProfileMonitorCriteria.test.ts +15 -11
  91. package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +97 -1
  92. package/Tests/Server/Utils/SecurityEvent/DetectionRuleEvaluator.test.ts +369 -8
  93. package/Tests/Server/Utils/SecurityEvent/SigmaClickhouseCompiler.test.ts +35 -0
  94. package/Tests/Server/Utils/Telemetry/LlmCostBudgetEvaluator.test.ts +368 -4
  95. package/Tests/Server/Utils/Telemetry/LlmMetricSpend.test.ts +245 -0
  96. package/Tests/Server/Views/AnalyticsConsentPartial.test.ts +48 -5
  97. package/Tests/Types/Code/YamlSyntax.test.ts +396 -0
  98. package/Tests/Types/Monitor/MetricAndProfileDefaultCriteria.test.ts +957 -0
  99. package/Tests/Types/Monitor/MonitorCriteriaActionValidation.test.ts +598 -0
  100. package/Tests/Types/Monitor/MonitorCriteriaInstance.test.ts +49 -0
  101. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +6 -6
  102. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.test.ts +4 -4
  103. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +4 -3
  104. package/Tests/Types/Telemetry/LlmMetricConventions.test.ts +193 -0
  105. package/Tests/UI/Components/AiInvestigationSettingsCard.test.tsx +0 -12
  106. package/Tests/UI/Components/CardModelDetailEdit.test.tsx +1 -17
  107. package/Tests/UI/Components/CodeEditor.test.tsx +474 -10
  108. package/Tests/UI/Components/Detail/YamlField.test.tsx +105 -0
  109. package/Tests/UI/Components/EntityDropdownLabelsBulkAdd.test.tsx +319 -0
  110. package/Tests/UI/Components/Forms/FormFieldYaml.test.tsx +231 -0
  111. package/Tests/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.test.ts +1 -0
  112. package/Tests/UI/Components/Forms/ValidationYAML.test.ts +234 -0
  113. package/Tests/UI/Components/ModelTable/AttributeColumns.test.tsx +573 -0
  114. package/Tests/UI/Components/ModelTable/BaseModelTableAttributeColumns.test.tsx +824 -0
  115. package/Tests/UI/Components/ModelTable/ColumnCustomizationModalAddableColumns.test.tsx +707 -0
  116. package/Tests/UI/Components/SideOver.test.tsx +1 -1
  117. package/Tests/UI/Components/StatusPage/ResourceGridSelection.test.tsx +294 -0
  118. package/Tests/UI/Components/StatusPage/ResourceListSelection.test.tsx +436 -0
  119. package/Tests/UI/Components/TableBulkCsvExport.test.tsx +6 -2
  120. package/Tests/UI/Components/Toggle.test.tsx +25 -0
  121. package/Tests/UI/Components/YamlEditor.test.tsx +847 -0
  122. package/Tests/Utils/NetworkDevice/DeviceReachabilityUtil.test.ts +252 -0
  123. package/Tests/Utils/NetworkDiscovery/AutoImportRuleMatcher.test.ts +516 -0
  124. package/Tests/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.test.ts +272 -0
  125. package/Tests/Utils/NetworkDiscovery/DiscoveredHostUtil.test.ts +221 -0
  126. package/Tests/Utils/NetworkDiscovery/ScanNameUtil.test.ts +312 -0
  127. package/Tests/Utils/NetworkDiscovery/ScanTargetUtil.test.ts +164 -0
  128. package/Tests/Utils/StatusPage/ResourceExplorer.test.ts +468 -0
  129. package/Tests/Utils/Telemetry/LlmMetricQuery.test.ts +472 -0
  130. package/Types/Code/YamlSyntax.ts +396 -0
  131. package/Types/Exception/ExceptionCode.ts +1 -0
  132. package/Types/Exception/PayloadTooLargeException.ts +8 -0
  133. package/Types/Marketing/Attribution.ts +10 -0
  134. package/Types/Marketing/MarketingEvent.ts +4 -0
  135. package/Types/Monitor/MonitorCriteriaInstance.ts +116 -56
  136. package/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.ts +2 -1
  137. package/Types/NetworkAutomation/RuleRunResult.ts +84 -0
  138. package/Types/Permission.ts +47 -0
  139. package/Types/SecurityEvent/DetectionFindingConstants.ts +11 -0
  140. package/Types/Telemetry/LlmMetricConventions.ts +124 -0
  141. package/UI/Components/AutocompleteTextInput/AutocompleteTextInput.tsx +3 -0
  142. package/UI/Components/BulkUpdate/BulkUpdateForm.tsx +10 -1
  143. package/UI/Components/CodeEditor/CodeEditor.tsx +241 -79
  144. package/UI/Components/CodeEditor/YamlEditor.tsx +369 -0
  145. package/UI/Components/Detail/Detail.tsx +10 -0
  146. package/UI/Components/EntityDropdown/EntityDropdown.tsx +33 -0
  147. package/UI/Components/Forms/Fields/FormField.tsx +31 -0
  148. package/UI/Components/Forms/Types/Field.ts +8 -0
  149. package/UI/Components/Forms/Types/FormFieldSchemaType.ts +1 -0
  150. package/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.ts +2 -0
  151. package/UI/Components/Forms/Validation.ts +46 -0
  152. package/UI/Components/Input/Input.tsx +3 -0
  153. package/UI/Components/ModelTable/AttributeColumns.tsx +321 -0
  154. package/UI/Components/ModelTable/BaseModelTable.tsx +263 -14
  155. package/UI/Components/ModelTable/Column.ts +8 -0
  156. package/UI/Components/ModelTable/ColumnCustomizationModal.tsx +310 -0
  157. package/UI/Components/ModelTable/ColumnPreference.ts +13 -1
  158. package/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.tsx +2 -2
  159. package/UI/Components/SideOver/SideOver.tsx +5 -5
  160. package/UI/Components/StatusPage/ResourceGrid.tsx +82 -2
  161. package/UI/Components/StatusPage/ResourceList.tsx +197 -38
  162. package/UI/Components/Toggle/Toggle.tsx +9 -1
  163. package/UI/Components/Types/FieldType.ts +1 -0
  164. package/Utils/NetworkDevice/DeviceReachabilityUtil.ts +73 -1
  165. package/Utils/NetworkDiscovery/AutoImportRuleMatcher.ts +257 -0
  166. package/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.ts +183 -0
  167. package/Utils/NetworkDiscovery/DiscoveredHostUtil.ts +95 -0
  168. package/Utils/NetworkDiscovery/DiscoveryImportEligibility.ts +66 -0
  169. package/Utils/NetworkDiscovery/ScanNameUtil.ts +181 -0
  170. package/Utils/NetworkDiscovery/ScanTargetUtil.ts +82 -0
  171. package/Utils/StatusPage/ResourceExplorer.ts +258 -0
  172. package/Utils/Telemetry/LlmMetricQuery.ts +204 -0
  173. package/build/dist/Models/DatabaseModels/DetectionRule.js +57 -0
  174. package/build/dist/Models/DatabaseModels/DetectionRule.js.map +1 -1
  175. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  176. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  177. package/build/dist/Models/DatabaseModels/NetworkDevice.js +45 -0
  178. package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
  179. package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js +609 -0
  180. package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js.map +1 -0
  181. package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js +97 -0
  182. package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
  183. package/build/dist/Models/DatabaseModels/Project.js +76 -0
  184. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  185. package/build/dist/Models/DatabaseModels/User.js +76 -0
  186. package/build/dist/Models/DatabaseModels/User.js.map +1 -1
  187. package/build/dist/Server/API/TelemetryAPI.js +17 -0
  188. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  189. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789100000000-AddNetworkDeviceAutoImportRule.js +35 -0
  190. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789100000000-AddNetworkDeviceAutoImportRule.js.map +1 -0
  191. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789200000000-AddSysObjectIdPatternToAutoImportRule.js +19 -0
  192. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789200000000-AddSysObjectIdPatternToAutoImportRule.js.map +1 -0
  193. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789300000000-AddAutoApplyVendorHealthTemplate.js +19 -0
  194. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789300000000-AddAutoApplyVendorHealthTemplate.js.map +1 -0
  195. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.js +21 -0
  196. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.js.map +1 -0
  197. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.js +42 -0
  198. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.js.map +1 -0
  199. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.js +22 -0
  200. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.js.map +1 -0
  201. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +12 -0
  202. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  203. package/build/dist/Server/Middleware/GzipRequestBody.js +224 -0
  204. package/build/dist/Server/Middleware/GzipRequestBody.js.map +1 -0
  205. package/build/dist/Server/Middleware/MultipartFormData.js +71 -2
  206. package/build/dist/Server/Middleware/MultipartFormData.js.map +1 -1
  207. package/build/dist/Server/Services/DetectionRuleService.js +49 -4
  208. package/build/dist/Server/Services/DetectionRuleService.js.map +1 -1
  209. package/build/dist/Server/Services/Index.js +206 -0
  210. package/build/dist/Server/Services/Index.js.map +1 -1
  211. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js +711 -0
  212. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js.map +1 -0
  213. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js +155 -0
  214. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js.map +1 -0
  215. package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js +50 -0
  216. package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js.map +1 -1
  217. package/build/dist/Server/Services/NetworkDeviceService.js +124 -0
  218. package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
  219. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +68 -13
  220. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  221. package/build/dist/Server/Services/OnCallDutyPolicyFeedService.js +1 -1
  222. package/build/dist/Server/Services/OnCallDutyPolicyFeedService.js.map +1 -1
  223. package/build/dist/Server/Services/ProjectService.js +25 -45
  224. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  225. package/build/dist/Server/Services/SecurityEventService.js +57 -5
  226. package/build/dist/Server/Services/SecurityEventService.js.map +1 -1
  227. package/build/dist/Server/Services/StatusPageResourceService.js +140 -0
  228. package/build/dist/Server/Services/StatusPageResourceService.js.map +1 -1
  229. package/build/dist/Server/Services/TelemetryAttributeService.js +17 -0
  230. package/build/dist/Server/Services/TelemetryAttributeService.js.map +1 -1
  231. package/build/dist/Server/Services/UserService.js +2 -12
  232. package/build/dist/Server/Services/UserService.js.map +1 -1
  233. package/build/dist/Server/Types/Markdown.js +147 -61
  234. package/build/dist/Server/Types/Markdown.js.map +1 -1
  235. package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js +12 -4
  236. package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js.map +1 -1
  237. package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js +12 -1
  238. package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js.map +1 -1
  239. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +246 -9
  240. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
  241. package/build/dist/Server/Utils/Attribution.js.map +1 -1
  242. package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js +27 -7
  243. package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js.map +1 -1
  244. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +26 -0
  245. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
  246. package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js +42 -7
  247. package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js.map +1 -1
  248. package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js +18 -0
  249. package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js.map +1 -1
  250. package/build/dist/Server/Utils/StartServer.js +12 -21
  251. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  252. package/build/dist/Server/Utils/Telemetry/LlmCostBudgetEvaluator.js +76 -5
  253. package/build/dist/Server/Utils/Telemetry/LlmCostBudgetEvaluator.js.map +1 -1
  254. package/build/dist/Server/Utils/Telemetry/LlmMetricSpend.js +102 -0
  255. package/build/dist/Server/Utils/Telemetry/LlmMetricSpend.js.map +1 -0
  256. package/build/dist/Types/Code/YamlSyntax.js +212 -0
  257. package/build/dist/Types/Code/YamlSyntax.js.map +1 -0
  258. package/build/dist/Types/Exception/ExceptionCode.js +1 -0
  259. package/build/dist/Types/Exception/ExceptionCode.js.map +1 -1
  260. package/build/dist/Types/Exception/PayloadTooLargeException.js +8 -0
  261. package/build/dist/Types/Exception/PayloadTooLargeException.js.map +1 -0
  262. package/build/dist/Types/Marketing/Attribution.js +10 -0
  263. package/build/dist/Types/Marketing/Attribution.js.map +1 -1
  264. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +112 -51
  265. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
  266. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js +2 -1
  267. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js.map +1 -1
  268. package/build/dist/Types/NetworkAutomation/RuleRunResult.js +24 -0
  269. package/build/dist/Types/NetworkAutomation/RuleRunResult.js.map +1 -1
  270. package/build/dist/Types/Permission.js +41 -0
  271. package/build/dist/Types/Permission.js.map +1 -1
  272. package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js +9 -0
  273. package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js.map +1 -1
  274. package/build/dist/Types/Telemetry/LlmMetricConventions.js +98 -0
  275. package/build/dist/Types/Telemetry/LlmMetricConventions.js.map +1 -0
  276. package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js +1 -1
  277. package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js.map +1 -1
  278. package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js +5 -2
  279. package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js.map +1 -1
  280. package/build/dist/UI/Components/CodeEditor/CodeEditor.js +206 -76
  281. package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
  282. package/build/dist/UI/Components/CodeEditor/YamlEditor.js +201 -0
  283. package/build/dist/UI/Components/CodeEditor/YamlEditor.js.map +1 -0
  284. package/build/dist/UI/Components/Detail/Detail.js +9 -0
  285. package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
  286. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js +16 -1
  287. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js.map +1 -1
  288. package/build/dist/UI/Components/Forms/Fields/FormField.js +14 -1
  289. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  290. package/build/dist/UI/Components/Forms/Types/Field.js.map +1 -1
  291. package/build/dist/UI/Components/Forms/Types/FormFieldSchemaType.js +1 -0
  292. package/build/dist/UI/Components/Forms/Types/FormFieldSchemaType.js.map +1 -1
  293. package/build/dist/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.js +2 -0
  294. package/build/dist/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.js.map +1 -1
  295. package/build/dist/UI/Components/Forms/Validation.js +27 -0
  296. package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
  297. package/build/dist/UI/Components/Input/Input.js +1 -1
  298. package/build/dist/UI/Components/Input/Input.js.map +1 -1
  299. package/build/dist/UI/Components/ModelTable/AttributeColumns.js +159 -0
  300. package/build/dist/UI/Components/ModelTable/AttributeColumns.js.map +1 -0
  301. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +179 -17
  302. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  303. package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js +133 -2
  304. package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js.map +1 -1
  305. package/build/dist/UI/Components/ModelTable/ColumnPreference.js +7 -1
  306. package/build/dist/UI/Components/ModelTable/ColumnPreference.js.map +1 -1
  307. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.js +2 -2
  308. package/build/dist/UI/Components/SideOver/SideOver.js +5 -5
  309. package/build/dist/UI/Components/StatusPage/ResourceGrid.js +28 -2
  310. package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -1
  311. package/build/dist/UI/Components/StatusPage/ResourceList.js +87 -20
  312. package/build/dist/UI/Components/StatusPage/ResourceList.js.map +1 -1
  313. package/build/dist/UI/Components/Toggle/Toggle.js +9 -1
  314. package/build/dist/UI/Components/Toggle/Toggle.js.map +1 -1
  315. package/build/dist/UI/Components/Types/FieldType.js +1 -0
  316. package/build/dist/UI/Components/Types/FieldType.js.map +1 -1
  317. package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js +37 -1
  318. package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js.map +1 -1
  319. package/build/dist/Utils/NetworkDiscovery/AutoImportRuleMatcher.js +149 -0
  320. package/build/dist/Utils/NetworkDiscovery/AutoImportRuleMatcher.js.map +1 -0
  321. package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js +121 -0
  322. package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js.map +1 -0
  323. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js +75 -0
  324. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js.map +1 -0
  325. package/build/dist/Utils/NetworkDiscovery/DiscoveryImportEligibility.js +59 -0
  326. package/build/dist/Utils/NetworkDiscovery/DiscoveryImportEligibility.js.map +1 -0
  327. package/build/dist/Utils/NetworkDiscovery/ScanNameUtil.js +120 -0
  328. package/build/dist/Utils/NetworkDiscovery/ScanNameUtil.js.map +1 -0
  329. package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js +60 -0
  330. package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js.map +1 -1
  331. package/build/dist/Utils/StatusPage/ResourceExplorer.js +171 -0
  332. package/build/dist/Utils/StatusPage/ResourceExplorer.js.map +1 -1
  333. package/build/dist/Utils/Telemetry/LlmMetricQuery.js +119 -0
  334. package/build/dist/Utils/Telemetry/LlmMetricQuery.js.map +1 -0
  335. package/jest.config.json +2 -0
  336. package/package.json +1 -1
  337. package/tsconfig.json +2 -1
  338. package/Tests/UI/Components/Toast.test.tsx +0 -197
  339. package/Tests/UI/Components/ToastStacking.test.tsx +0 -198
  340. package/UI/Components/Toast/Toast.tsx +0 -189
  341. package/UI/Components/Toast/ToastInit.tsx +0 -110
  342. package/build/dist/UI/Components/Toast/Toast.js +0 -123
  343. package/build/dist/UI/Components/Toast/Toast.js.map +0 -1
  344. package/build/dist/UI/Components/Toast/ToastInit.js +0 -53
  345. package/build/dist/UI/Components/Toast/ToastInit.js.map +0 -1
@@ -0,0 +1,272 @@
1
+ import {
2
+ MAX_DEVICE_DESCRIPTION_LENGTH,
3
+ MAX_DEVICE_NAME_LENGTH,
4
+ buildDeviceName,
5
+ buildFallbackDeviceName,
6
+ buildNetworkDeviceFromDiscoveredHost,
7
+ DiscoveredDeviceScanSource,
8
+ } from "../../../Utils/NetworkDiscovery/DiscoveredDeviceBuilder";
9
+ import NetworkDevice from "../../../Models/DatabaseModels/NetworkDevice";
10
+ import { DiscoveredNetworkDevice } from "../../../Models/DatabaseModels/NetworkDeviceDiscoveryScan";
11
+ import NetworkDeviceMonitoringMethod from "../../../Types/NetworkDevice/NetworkDeviceMonitoringMethod";
12
+ import ObjectID from "../../../Types/ObjectID";
13
+ import { describe, expect, it } from "@jest/globals";
14
+
15
+ /*
16
+ * Contract under test — one discovered host maps to one NetworkDevice the
17
+ * SAME way everywhere. The builder is the shared recipe behind the
18
+ * dashboard's Review-dialog import and the server-side auto-import rule
19
+ * engine, so what these tests pin is the device both paths must agree on:
20
+ * which host field lands in which device column, which scan credentials ride
21
+ * along (and when they must NOT), and the length ceilings that keep a
22
+ * real-world 255-octet sysName from failing the create on the slug.
23
+ */
24
+
25
+ const PROJECT_ID: ObjectID = new ObjectID(
26
+ "22222222-2222-4222-8222-222222222222",
27
+ );
28
+ const PROBE_ID: ObjectID = new ObjectID("11111111-1111-4111-8111-111111111111");
29
+
30
+ function snmpHost(
31
+ overrides: Partial<DiscoveredNetworkDevice> = {},
32
+ ): DiscoveredNetworkDevice {
33
+ return {
34
+ ipAddress: "10.0.0.5",
35
+ sysName: "core-switch-01",
36
+ sysDescr: "Cisco IOS Software, C2960X",
37
+ ...overrides,
38
+ };
39
+ }
40
+
41
+ // A scan carrying every credential column the builder knows how to copy.
42
+ function fullScanSource(): DiscoveredDeviceScanSource {
43
+ return {
44
+ probeId: PROBE_ID,
45
+ snmpVersion: "V3",
46
+ snmpCommunityString: "public",
47
+ snmpPort: 1161,
48
+ snmpV3SecurityLevel: "authPriv",
49
+ snmpV3Username: "observer",
50
+ snmpV3AuthProtocol: "SHA",
51
+ snmpV3AuthKey: "auth-key-value",
52
+ snmpV3PrivProtocol: "AES",
53
+ snmpV3PrivKey: "priv-key-value",
54
+ };
55
+ }
56
+
57
+ function build(data: {
58
+ host?: DiscoveredNetworkDevice | undefined;
59
+ scan?: DiscoveredDeviceScanSource | undefined;
60
+ name?: string | undefined;
61
+ }): NetworkDevice {
62
+ return buildNetworkDeviceFromDiscoveredHost({
63
+ projectId: PROJECT_ID,
64
+ host: data.host || snmpHost(),
65
+ scan: data.scan || fullScanSource(),
66
+ name: data.name,
67
+ });
68
+ }
69
+
70
+ describe("buildNetworkDeviceFromDiscoveredHost - vendor template auto-apply flag", () => {
71
+ it("is off unless the caller asks for it", () => {
72
+ expect(build({}).autoApplyVendorHealthTemplate).toBeUndefined();
73
+ });
74
+
75
+ it("is set on an SNMP host when the caller asks (the rule engine does)", () => {
76
+ const device: NetworkDevice = buildNetworkDeviceFromDiscoveredHost({
77
+ projectId: PROJECT_ID,
78
+ host: snmpHost(),
79
+ scan: fullScanSource(),
80
+ autoApplyVendorHealthTemplate: true,
81
+ });
82
+
83
+ expect(device.autoApplyVendorHealthTemplate).toBe(true);
84
+ });
85
+
86
+ /*
87
+ * A ping-only host is never SNMP-polled, so no poll can ever fingerprint
88
+ * its vendor — the flag would be a dead toggle that reads as a promise.
89
+ */
90
+ it("stays off on a ping-only host even when requested", () => {
91
+ const device: NetworkDevice = buildNetworkDeviceFromDiscoveredHost({
92
+ projectId: PROJECT_ID,
93
+ host: snmpHost({ snmpReachable: false }),
94
+ scan: fullScanSource(),
95
+ autoApplyVendorHealthTemplate: true,
96
+ });
97
+
98
+ expect(device.autoApplyVendorHealthTemplate).toBeUndefined();
99
+ });
100
+ });
101
+
102
+ describe("buildNetworkDeviceFromDiscoveredHost - SNMP host", () => {
103
+ it("maps identity fields: sysName to name, address to hostname, sysDescr to description", () => {
104
+ const device: NetworkDevice = build({});
105
+
106
+ expect(device.projectId?.toString()).toBe(PROJECT_ID.toString());
107
+ expect(device.name).toBe("core-switch-01");
108
+ // The address is both the hostname and the registered-host dedup key.
109
+ expect(device.hostname).toBe("10.0.0.5");
110
+ expect(device.description).toBe("Cisco IOS Software, C2960X");
111
+ expect(device.monitoringMethod).toBe(NetworkDeviceMonitoringMethod.Snmp);
112
+ });
113
+
114
+ /*
115
+ * Every credential the scan swept with must land on the device, or the
116
+ * imported device can never poll. The v3 block matters most: a v3 scan
117
+ * that imported as a credential-less device would sit unreachable with no
118
+ * error anywhere.
119
+ */
120
+ it("copies the probe and every SNMP credential from the scan source", () => {
121
+ const device: NetworkDevice = build({});
122
+
123
+ expect(device.probeId?.toString()).toBe(PROBE_ID.toString());
124
+ expect(device.snmpVersion).toBe("V3");
125
+ expect(device.snmpCommunityString).toBe("public");
126
+ expect(device.snmpPort).toBe(1161);
127
+ expect(device.snmpV3SecurityLevel).toBe("authPriv");
128
+ expect(device.snmpV3Username).toBe("observer");
129
+ expect(device.snmpV3AuthProtocol).toBe("SHA");
130
+ expect(device.snmpV3AuthKey).toBe("auth-key-value");
131
+ expect(device.snmpV3PrivProtocol).toBe("AES");
132
+ expect(device.snmpV3PrivKey).toBe("priv-key-value");
133
+ });
134
+
135
+ /*
136
+ * The scan row may hold a serialized id rather than an ObjectID instance
137
+ * (the rule engine selects its scan itself); the builder re-wraps rather
138
+ * than trusting the shape.
139
+ */
140
+ it("re-wraps the probe id so a serialized id still becomes an ObjectID", () => {
141
+ const device: NetworkDevice = build({
142
+ scan: {
143
+ ...fullScanSource(),
144
+ probeId: PROBE_ID.toString() as unknown as ObjectID,
145
+ },
146
+ });
147
+
148
+ expect(device.probeId).toBeInstanceOf(ObjectID);
149
+ expect(device.probeId?.toString()).toBe(PROBE_ID.toString());
150
+ });
151
+ });
152
+
153
+ describe("buildNetworkDeviceFromDiscoveredHost - ping-only host", () => {
154
+ /*
155
+ * A ping-only host becomes a monitor-backed device: recorded so it can
156
+ * belong to a site and appear on the topology map, with binding a monitor
157
+ * to it a separate deliberate step. Credentials it never answered to must
158
+ * not ride along.
159
+ */
160
+ it("builds a monitor-backed device with polling off and no credentials", () => {
161
+ const device: NetworkDevice = build({
162
+ host: snmpHost({ snmpReachable: false, sysName: undefined }),
163
+ });
164
+
165
+ expect(device.monitoringMethod).toBe(NetworkDeviceMonitoringMethod.Monitor);
166
+ expect(device.isPollingEnabled).toBe(false);
167
+ expect(device.probeId).toBeUndefined();
168
+ expect(device.snmpVersion).toBeUndefined();
169
+ expect(device.snmpCommunityString).toBeUndefined();
170
+ expect(device.snmpPort).toBeUndefined();
171
+ expect(device.snmpV3SecurityLevel).toBeUndefined();
172
+ expect(device.snmpV3Username).toBeUndefined();
173
+ expect(device.snmpV3AuthProtocol).toBeUndefined();
174
+ expect(device.snmpV3AuthKey).toBeUndefined();
175
+ expect(device.snmpV3PrivProtocol).toBeUndefined();
176
+ expect(device.snmpV3PrivKey).toBeUndefined();
177
+ });
178
+ });
179
+
180
+ describe("buildDeviceName", () => {
181
+ it("prefers the sysName", () => {
182
+ expect(buildDeviceName(snmpHost())).toBe("core-switch-01");
183
+ });
184
+
185
+ // A ping-only host has no SNMP identity; the address is all there is.
186
+ it("falls back to the address when sysName is missing", () => {
187
+ expect(buildDeviceName(snmpHost({ sysName: undefined }))).toBe("10.0.0.5");
188
+ });
189
+
190
+ it("falls back to the address when sysName is whitespace", () => {
191
+ expect(buildDeviceName(snmpHost({ sysName: " " }))).toBe("10.0.0.5");
192
+ });
193
+
194
+ /*
195
+ * SNMP sysName is a DisplayString of up to 255 octets, so over-long names
196
+ * are routine on real gear — and the create path THROWS on an over-long
197
+ * name (via the slug ceiling) rather than truncating, so the clamp has to
198
+ * happen here.
199
+ */
200
+ it("clamps a 255-character sysName to MAX_DEVICE_NAME_LENGTH", () => {
201
+ const longSysName: string = "x".repeat(255);
202
+ const name: string = buildDeviceName(snmpHost({ sysName: longSysName }));
203
+
204
+ expect(name.length).toBe(MAX_DEVICE_NAME_LENGTH);
205
+ expect(name).toBe(longSysName.substring(0, MAX_DEVICE_NAME_LENGTH));
206
+ });
207
+ });
208
+
209
+ describe("buildFallbackDeviceName", () => {
210
+ it("appends the address that tells name-twins apart", () => {
211
+ expect(buildFallbackDeviceName(snmpHost())).toBe(
212
+ "core-switch-01 (10.0.0.5)",
213
+ );
214
+ });
215
+
216
+ /*
217
+ * The collision fallback must fit under the SAME ceiling as the first
218
+ * attempt: the widest possible suffix is " (255.255.255.255)", so the base
219
+ * name is cut down first rather than the composed string overflowing into
220
+ * the very slug-length failure the ceiling exists to avoid.
221
+ */
222
+ it("keeps the composed name within MAX_DEVICE_NAME_LENGTH at the widest address", () => {
223
+ const name: string = buildFallbackDeviceName(
224
+ snmpHost({
225
+ sysName: "y".repeat(255),
226
+ ipAddress: "255.255.255.255",
227
+ }),
228
+ );
229
+
230
+ expect(name.length).toBeLessThanOrEqual(MAX_DEVICE_NAME_LENGTH);
231
+ expect(name.endsWith(" (255.255.255.255)")).toBe(true);
232
+ });
233
+ });
234
+
235
+ describe("description clamping", () => {
236
+ it("clamps an over-long sysDescr to MAX_DEVICE_DESCRIPTION_LENGTH", () => {
237
+ const longSysDescr: string = "d".repeat(700);
238
+ const device: NetworkDevice = build({
239
+ host: snmpHost({ sysDescr: longSysDescr }),
240
+ });
241
+
242
+ expect(device.description?.length).toBe(MAX_DEVICE_DESCRIPTION_LENGTH);
243
+ expect(device.description).toBe(
244
+ longSysDescr.substring(0, MAX_DEVICE_DESCRIPTION_LENGTH),
245
+ );
246
+ });
247
+
248
+ it("sets no description when the host has no sysDescr", () => {
249
+ const device: NetworkDevice = build({
250
+ host: snmpHost({ sysDescr: undefined }),
251
+ });
252
+
253
+ expect(device.description).toBeUndefined();
254
+ });
255
+ });
256
+
257
+ describe("the name override", () => {
258
+ /*
259
+ * The caller supplies the name so the collision retry can rebuild the SAME
260
+ * device under the fallback name without re-deciding anything else — the
261
+ * override must therefore be taken verbatim.
262
+ */
263
+ it("uses a supplied name verbatim instead of deriving one", () => {
264
+ const device: NetworkDevice = build({
265
+ name: "core-switch-01 (10.0.0.5)",
266
+ });
267
+
268
+ expect(device.name).toBe("core-switch-01 (10.0.0.5)");
269
+ // Everything else is still derived from the host as usual.
270
+ expect(device.hostname).toBe("10.0.0.5");
271
+ });
272
+ });
@@ -0,0 +1,221 @@
1
+ import { DiscoveredNetworkDevice } from "../../../Models/DatabaseModels/NetworkDeviceDiscoveryScan";
2
+ import { normalizeDiscoveredHosts } from "../../../Utils/NetworkDiscovery/DiscoveredHostUtil";
3
+ import { describe, expect, test } from "@jest/globals";
4
+
5
+ /*
6
+ * normalizeDiscoveredHosts is the one reading of a scan's `discoveredDevices`
7
+ * jsonb that both the dashboard's Review dialog and the server-side
8
+ * auto-import rule engine share. The column is written verbatim from the
9
+ * probe's payload and the only guard on it is "the value is an array", so the
10
+ * three payload shapes documented on the function - a null row, a non-string
11
+ * address, and the same address carrying two different isAlreadyRegistered
12
+ * values - are shapes nothing upstream stops. These tests pin that each is
13
+ * neutralised here, once, so no downstream reader can be handed a different
14
+ * reading of the same payload.
15
+ *
16
+ * A cast through `unknown` is used wherever a test feeds a shape the type
17
+ * forbids on purpose: the whole point of the function is that the runtime
18
+ * value does not honour the type, so the tests have to be able to say so.
19
+ */
20
+
21
+ function host(
22
+ overrides: Partial<DiscoveredNetworkDevice>,
23
+ ): DiscoveredNetworkDevice {
24
+ return { ipAddress: "10.0.0.1", ...overrides };
25
+ }
26
+
27
+ describe("normalizeDiscoveredHosts", () => {
28
+ describe("rows that are not hosts at all", () => {
29
+ test("an empty scan normalises to an empty list", () => {
30
+ expect(normalizeDiscoveredHosts([])).toEqual([]);
31
+ });
32
+
33
+ test.each([
34
+ ["null", null],
35
+ ["undefined", undefined],
36
+ ["a number", 10],
37
+ ["a string", "10.0.0.1"],
38
+ ["a boolean", true],
39
+ ])(
40
+ "%s is dropped rather than dereferenced",
41
+ (_label: string, value: unknown) => {
42
+ expect(
43
+ normalizeDiscoveredHosts([
44
+ value,
45
+ ] as unknown as Array<DiscoveredNetworkDevice>),
46
+ ).toEqual([]);
47
+ },
48
+ );
49
+
50
+ test("the real hosts around a null row survive it", () => {
51
+ const result: Array<DiscoveredNetworkDevice> = normalizeDiscoveredHosts([
52
+ host({ ipAddress: "10.0.0.1" }),
53
+ null as unknown as DiscoveredNetworkDevice,
54
+ host({ ipAddress: "10.0.0.2" }),
55
+ ]);
56
+
57
+ expect(
58
+ result.map((entry: DiscoveredNetworkDevice) => {
59
+ return entry.ipAddress;
60
+ }),
61
+ ).toEqual(["10.0.0.1", "10.0.0.2"]);
62
+ });
63
+ });
64
+
65
+ describe("the address is made a trimmed string", () => {
66
+ test("a numeric address becomes its string spelling, so Set.has can find it", () => {
67
+ const result: Array<DiscoveredNetworkDevice> = normalizeDiscoveredHosts([
68
+ { ipAddress: 10 } as unknown as DiscoveredNetworkDevice,
69
+ ]);
70
+
71
+ expect(result[0]!.ipAddress).toBe("10");
72
+ });
73
+
74
+ test.each([
75
+ ["a leading and trailing space", " 10.0.0.1 ", "10.0.0.1"],
76
+ ["a tab", "\t10.0.0.1", "10.0.0.1"],
77
+ ["only whitespace", " ", ""],
78
+ ])("%s is trimmed", (_label: string, given: string, expected: string) => {
79
+ const result: Array<DiscoveredNetworkDevice> = normalizeDiscoveredHosts([
80
+ host({ ipAddress: given }),
81
+ ]);
82
+
83
+ expect(result[0]!.ipAddress).toBe(expected);
84
+ });
85
+
86
+ test.each([
87
+ ["undefined", undefined],
88
+ ["null", null],
89
+ ])(
90
+ "a %s address becomes the empty string, never the literal word",
91
+ (_label: string, value: unknown) => {
92
+ const result: Array<DiscoveredNetworkDevice> = normalizeDiscoveredHosts(
93
+ [{ ipAddress: value } as unknown as DiscoveredNetworkDevice],
94
+ );
95
+
96
+ expect(result[0]!.ipAddress).toBe("");
97
+ },
98
+ );
99
+ });
100
+
101
+ describe("everything else on the row is carried through untouched", () => {
102
+ test("the SNMP system group and reachability flags survive normalisation", () => {
103
+ const result: Array<DiscoveredNetworkDevice> = normalizeDiscoveredHosts([
104
+ {
105
+ ipAddress: " 10.0.0.5 ",
106
+ sysName: "core-switch",
107
+ sysDescr: "vendor OS 1.2",
108
+ sysObjectId: "1.3.6.1.4.1.9",
109
+ sysLocation: "rack 4",
110
+ sysContact: "neteng",
111
+ sysUpTimeSeconds: 4200,
112
+ snmpReachable: true,
113
+ },
114
+ ]);
115
+
116
+ expect(result[0]).toEqual({
117
+ ipAddress: "10.0.0.5",
118
+ sysName: "core-switch",
119
+ sysDescr: "vendor OS 1.2",
120
+ sysObjectId: "1.3.6.1.4.1.9",
121
+ sysLocation: "rack 4",
122
+ sysContact: "neteng",
123
+ sysUpTimeSeconds: 4200,
124
+ snmpReachable: true,
125
+ });
126
+ });
127
+ });
128
+
129
+ describe("an address registered on one row is registered on every row", () => {
130
+ test("a duplicate address inherits the registered verdict regardless of probe ordering", () => {
131
+ const result: Array<DiscoveredNetworkDevice> = normalizeDiscoveredHosts([
132
+ host({ ipAddress: "10.0.0.9", isAlreadyRegistered: false }),
133
+ host({ ipAddress: "10.0.0.9", isAlreadyRegistered: true }),
134
+ ]);
135
+
136
+ expect(
137
+ result.map((entry: DiscoveredNetworkDevice) => {
138
+ return entry.isAlreadyRegistered;
139
+ }),
140
+ ).toEqual([true, true]);
141
+ });
142
+
143
+ test("the verdict does not bleed onto a different address", () => {
144
+ const result: Array<DiscoveredNetworkDevice> = normalizeDiscoveredHosts([
145
+ host({ ipAddress: "10.0.0.9", isAlreadyRegistered: true }),
146
+ host({ ipAddress: "10.0.0.10", isAlreadyRegistered: false }),
147
+ ]);
148
+
149
+ expect(
150
+ result.find((entry: DiscoveredNetworkDevice) => {
151
+ return entry.ipAddress === "10.0.0.10";
152
+ })!.isAlreadyRegistered,
153
+ ).toBe(false);
154
+ });
155
+
156
+ test("an already-registered row is handed back as the very same object", () => {
157
+ const registered: DiscoveredNetworkDevice = host({
158
+ ipAddress: "10.0.0.11",
159
+ isAlreadyRegistered: true,
160
+ });
161
+
162
+ const result: Array<DiscoveredNetworkDevice> = normalizeDiscoveredHosts([
163
+ registered,
164
+ ]);
165
+
166
+ /*
167
+ * With nothing to flip, the normaliser must not churn objects it did
168
+ * not change - a fresh clone every render would defeat any memoised
169
+ * reader keyed on identity. The first pass already re-spread the row,
170
+ * so identity is asserted against that cleaned object, not the input.
171
+ */
172
+ expect(result[0]).toEqual(registered);
173
+ });
174
+
175
+ test("the empty-string address is never treated as a registered key", () => {
176
+ /*
177
+ * A blank address is what an undefined or whitespace-only address
178
+ * normalises to. If it counted as a registered key, every other blank
179
+ * row - unrelated hosts the probe could not address - would be marked
180
+ * registered together.
181
+ */
182
+ const result: Array<DiscoveredNetworkDevice> = normalizeDiscoveredHosts([
183
+ host({ ipAddress: " ", isAlreadyRegistered: true }),
184
+ host({ ipAddress: undefined as unknown as string }),
185
+ ]);
186
+
187
+ expect(
188
+ result.every((entry: DiscoveredNetworkDevice) => {
189
+ return entry.ipAddress === "";
190
+ }),
191
+ ).toBe(true);
192
+ expect(result[1]!.isAlreadyRegistered).toBeUndefined();
193
+ });
194
+
195
+ test("three rows of one address all end up registered when any one is", () => {
196
+ const result: Array<DiscoveredNetworkDevice> = normalizeDiscoveredHosts([
197
+ host({ ipAddress: "10.0.0.20", isAlreadyRegistered: false }),
198
+ host({ ipAddress: "10.0.0.20" }),
199
+ host({ ipAddress: "10.0.0.20", isAlreadyRegistered: true }),
200
+ ]);
201
+
202
+ expect(
203
+ result.map((entry: DiscoveredNetworkDevice) => {
204
+ return entry.isAlreadyRegistered;
205
+ }),
206
+ ).toEqual([true, true, true]);
207
+ });
208
+ });
209
+
210
+ describe("a scan with nothing registered is left as it is found", () => {
211
+ test("no row gains a registered flag it did not arrive with", () => {
212
+ const result: Array<DiscoveredNetworkDevice> = normalizeDiscoveredHosts([
213
+ host({ ipAddress: "10.0.0.30", isAlreadyRegistered: false }),
214
+ host({ ipAddress: "10.0.0.31" }),
215
+ ]);
216
+
217
+ expect(result[0]!.isAlreadyRegistered).toBe(false);
218
+ expect(result[1]!.isAlreadyRegistered).toBeUndefined();
219
+ });
220
+ });
221
+ });