@oneuptime/common 11.6.1 → 11.7.0

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 (420) hide show
  1. package/Models/AnalyticsModels/Index.ts +4 -0
  2. package/Models/AnalyticsModels/KubernetesCostAllocation.ts +842 -0
  3. package/Models/AnalyticsModels/NetworkFlow.ts +54 -0
  4. package/Models/AnalyticsModels/SloHistory.ts +243 -0
  5. package/Models/DatabaseModels/Index.ts +20 -0
  6. package/Models/DatabaseModels/NetworkDevice.ts +453 -6
  7. package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +7 -6
  8. package/Models/DatabaseModels/NetworkEndpoint.ts +753 -0
  9. package/Models/DatabaseModels/NetworkSite.ts +1155 -0
  10. package/Models/DatabaseModels/NetworkSiteAssignmentRule.ts +517 -0
  11. package/Models/DatabaseModels/NetworkSiteLink.ts +611 -0
  12. package/Models/DatabaseModels/NetworkSiteStatusTimeline.ts +520 -0
  13. package/Models/DatabaseModels/NetworkSiteType.ts +499 -0
  14. package/Models/DatabaseModels/ServiceLevelObjective.ts +1115 -0
  15. package/Models/DatabaseModels/ServiceLevelObjectiveBurnRateRule.ts +762 -0
  16. package/Models/DatabaseModels/ServiceLevelObjectiveOwnerTeam.ts +444 -0
  17. package/Models/DatabaseModels/ServiceLevelObjectiveOwnerUser.ts +443 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/1784757142154-AddNetworkSiteHierarchyAndEndpoints.ts +359 -0
  19. package/Server/Infrastructure/Postgres/SchemaMigrations/1784897317860-AddNetworkSiteAlerting.ts +48 -0
  20. package/Server/Infrastructure/Postgres/SchemaMigrations/1784970388777-AddNetworkDevicePollingColumns.ts +65 -0
  21. package/Server/Infrastructure/Postgres/SchemaMigrations/1784986826214-AddNetworkSiteTypeTable.ts +88 -0
  22. package/Server/Infrastructure/Postgres/SchemaMigrations/1784987015619-AddServiceLevelObjective.ts +391 -0
  23. package/Server/Infrastructure/Postgres/SchemaMigrations/1785066759532-MigrationName.ts +30 -0
  24. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +12 -0
  25. package/Server/Services/AnalyticsDatabaseService.ts +25 -9
  26. package/Server/Services/Index.ts +24 -0
  27. package/Server/Services/KubernetesCostAllocationService.ts +14 -0
  28. package/Server/Services/MetricService.ts +21 -2
  29. package/Server/Services/MonitorService.ts +22 -0
  30. package/Server/Services/NetworkDeviceService.ts +438 -2
  31. package/Server/Services/NetworkEndpointService.ts +414 -0
  32. package/Server/Services/NetworkSiteAssignmentRuleService.ts +98 -0
  33. package/Server/Services/NetworkSiteLinkService.ts +10 -0
  34. package/Server/Services/NetworkSiteService.ts +1254 -0
  35. package/Server/Services/NetworkSiteStatusTimelineService.ts +10 -0
  36. package/Server/Services/NetworkSiteTypeService.ts +10 -0
  37. package/Server/Services/ProjectService.ts +122 -2
  38. package/Server/Services/ServiceLevelObjectiveBurnRateRuleService.ts +457 -0
  39. package/Server/Services/ServiceLevelObjectiveOwnerTeamService.ts +10 -0
  40. package/Server/Services/ServiceLevelObjectiveOwnerUserService.ts +10 -0
  41. package/Server/Services/ServiceLevelObjectiveService.ts +685 -0
  42. package/Server/Services/SloHistoryService.ts +101 -0
  43. package/Server/Services/UserNotificationSettingService.ts +37 -0
  44. package/Server/Types/AnalyticsDatabase/AggregateBy.ts +9 -0
  45. package/Server/Types/Database/Permissions/OwnerTableRegistry.ts +15 -0
  46. package/Server/Utils/AnalyticsDatabase/Statement.ts +39 -3
  47. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +87 -48
  48. package/Server/Utils/Memory.ts +9 -1
  49. package/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.ts +190 -12
  50. package/Server/Utils/Monitor/Data/OuiVendors.json +6920 -0
  51. package/Server/Utils/Monitor/DnsResolutionCache.ts +129 -0
  52. package/Server/Utils/Monitor/MonitorResource.ts +4 -30
  53. package/Server/Utils/Monitor/NetworkDeviceHydrationUtil.ts +214 -39
  54. package/Server/Utils/Monitor/NetworkDeviceMetricUtil.ts +343 -0
  55. package/Server/Utils/Monitor/NetworkDeviceWalkUtil.ts +251 -0
  56. package/Server/Utils/Monitor/NetworkInventoryUtil.ts +176 -142
  57. package/Server/Utils/Monitor/OuiLookupUtil.ts +49 -0
  58. package/Server/Utils/Monitor/SnmpInterfaceRateUtil.ts +17 -13
  59. package/Server/Utils/WhatsAppTemplateUtil.ts +7 -3
  60. package/Tests/Models/AnalyticsModels/KubernetesCostAllocation.test.ts +135 -0
  61. package/Tests/Models/NetworkSiteHierarchy.test.ts +134 -0
  62. package/Tests/Server/Services/AnalyticsDatabaseService.test.ts +239 -0
  63. package/Tests/Server/Services/KubernetesCostAllocationAggregate.test.ts +338 -0
  64. package/Tests/Server/Services/MetricServiceAggregate.test.ts +142 -0
  65. package/Tests/Server/Services/NetworkDeviceSiteAssignment.test.ts +484 -0
  66. package/Tests/Server/Services/NetworkEndpointServiceUpsert.test.ts +686 -0
  67. package/Tests/Server/Services/NetworkSiteAssignmentRuleService.test.ts +185 -0
  68. package/Tests/Server/Services/NetworkSiteService.test.ts +1354 -0
  69. package/Tests/Server/Services/ServiceLevelObjectiveBurnRateRuleService.test.ts +1313 -0
  70. package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +1988 -0
  71. package/Tests/Server/Utils/AnalyticsDatabase/Statement.test.ts +22 -0
  72. package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +404 -0
  73. package/Tests/Server/Utils/Encryption.test.ts +70 -0
  74. package/Tests/Server/Utils/FileAttachmentMarkdownUtil.test.ts +225 -0
  75. package/Tests/Server/Utils/JsonWebToken.test.ts +175 -0
  76. package/Tests/Server/Utils/LocalFile.test.ts +258 -0
  77. package/Tests/Server/Utils/Memory.test.ts +213 -0
  78. package/Tests/Server/Utils/Monitor/Criteria/LogMonitorCriteria.test.ts +30 -0
  79. package/Tests/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.test.ts +385 -0
  80. package/Tests/Server/Utils/Monitor/DnsResolutionCache.test.ts +194 -0
  81. package/Tests/Server/Utils/Monitor/NetworkDeviceHydrationUtil.test.ts +155 -0
  82. package/Tests/Server/Utils/Monitor/NetworkDeviceWalkUtil.test.ts +600 -0
  83. package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +151 -124
  84. package/Tests/Server/Utils/Monitor/SnmpInterfaceRateUtil.test.ts +496 -0
  85. package/Tests/Server/Utils/MonitorCriteriaDataExtractor.test.ts +175 -0
  86. package/Tests/Server/Utils/MonitorCriteriaMessageFormatter.test.ts +282 -0
  87. package/Tests/Server/Utils/OuiLookupUtil.test.ts +59 -0
  88. package/Tests/Server/Utils/SeriesResourceLabels.test.ts +117 -0
  89. package/Tests/Server/Utils/StackTraceParser.test.ts +370 -0
  90. package/Tests/Server/Utils/Stream.test.ts +87 -0
  91. package/Tests/Server/Utils/ValidateGlobalProviderProjectTeams.test.ts +221 -0
  92. package/Tests/Server/Utils/WhatsAppTemplateUtil.test.ts +199 -0
  93. package/Tests/Types/Database/CompareOperatorWireSerialization.test.ts +33 -0
  94. package/Tests/Types/Email.test.ts +198 -0
  95. package/Tests/Types/Kubernetes/KubernetesObjectParser.test.ts +177 -0
  96. package/Tests/Types/Log/LogQueryToFilter.test.ts +111 -0
  97. package/Tests/Types/Monitor/MonitorCriteria.test.ts +155 -0
  98. package/Tests/Types/Monitor/MonitorCriteriaInstance.test.ts +465 -0
  99. package/Tests/Types/Monitor/MonitorStep.test.ts +183 -0
  100. package/Tests/Types/Monitor/MonitorStepNetworkDeviceMonitor.test.ts +141 -1
  101. package/Tests/Types/Monitor/MonitorSteps.test.ts +183 -0
  102. package/Tests/Types/Monitor/MonitorType.test.ts +59 -1
  103. package/Tests/Types/Monitor/NetworkDeviceAlertPack.test.ts +97 -0
  104. package/Tests/Types/Monitor/SnmpEndpointDiscovery.test.ts +151 -0
  105. package/Tests/Types/Monitor/SnmpVendorTemplate.test.ts +110 -6
  106. package/Tests/Types/NetworkSite/DefaultNetworkSiteType.test.ts +129 -0
  107. package/Tests/Types/SerializableObjectDictionary.test.ts +166 -0
  108. package/Tests/Types/Time/RangeStartAndEndDateTime.test.ts +98 -0
  109. package/Tests/Types/WebsiteRequest.test.ts +230 -0
  110. package/Tests/UI/Components/EntityFilterRoundTrip.test.tsx +103 -0
  111. package/Tests/UI/Components/ModelTable/BaseModelTableUrlState.test.tsx +612 -0
  112. package/Tests/UI/Components/ModelTable/FilterDataToQuery.test.ts +275 -0
  113. package/Tests/UI/Utils/LogExport.test.ts +159 -0
  114. package/Tests/UI/Utils/Navigation.test.ts +183 -0
  115. package/Tests/UI/Utils/TableFilterUrlState.test.ts +539 -0
  116. package/Tests/UI/Utils/TableViewUrlState.test.ts +272 -0
  117. package/Tests/Utils/Base64.test.ts +51 -0
  118. package/Tests/Utils/Boolean.test.ts +76 -0
  119. package/Tests/Utils/Crypto.test.ts +48 -0
  120. package/Tests/Utils/Dashboard/DashboardSloComponent.test.ts +259 -0
  121. package/Tests/Utils/Dashboard/ModelQueryVariableInterpolation.test.ts +162 -0
  122. package/Tests/Utils/Enum.test.ts +54 -0
  123. package/Tests/Utils/IpCanonicalUtil.test.ts +85 -0
  124. package/Tests/Utils/Memory.test.ts +44 -0
  125. package/Tests/Utils/Metrics/MetricSeriesFingerprint.test.ts +160 -0
  126. package/Tests/Utils/Monitor/EndpointAttachmentUtil.test.ts +836 -0
  127. package/Tests/Utils/Monitor/MonitorMetricType.test.ts +218 -0
  128. package/Tests/Utils/Monitor/NetworkTopologyUtil.test.ts +447 -0
  129. package/Tests/Utils/NetworkSite/CidrMatchUtil.test.ts +440 -0
  130. package/Tests/Utils/NetworkSite/MaterializedPathUtil.test.ts +205 -0
  131. package/Tests/Utils/NetworkSite/SiteStatusRollupUtil.test.ts +221 -0
  132. package/Tests/Utils/NetworkSite/SiteUptimeUtil.test.ts +283 -0
  133. package/Tests/Utils/ObjectUtil.test.ts +30 -0
  134. package/Tests/Utils/Slo/SloBurnRateRuleState.test.ts +124 -0
  135. package/Tests/Utils/Slo/SloDuration.test.ts +148 -0
  136. package/Tests/Utils/Slo/SloHealth.test.ts +334 -0
  137. package/Tests/Utils/Slo/SloStatusColor.test.ts +89 -0
  138. package/Tests/Utils/Slo/SloUtil.test.ts +1980 -0
  139. package/Tests/Utils/Slo/SloWidgetFormat.test.ts +605 -0
  140. package/Tests/Utils/TechStack.test.ts +50 -0
  141. package/Tests/Utils/Traces/CriticalPath.test.ts +178 -0
  142. package/Types/AnalyticsDatabase/AnalyticsTableName.ts +10 -0
  143. package/Types/BaseDatabase/EqualTo.ts +9 -1
  144. package/Types/BaseDatabase/EqualToOrNull.ts +9 -1
  145. package/Types/BaseDatabase/NotEqual.ts +9 -1
  146. package/Types/Dashboard/DashboardComponentType.ts +1 -0
  147. package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +1 -0
  148. package/Types/Dashboard/DashboardComponents/DashboardSloComponent.ts +42 -0
  149. package/Types/Dashboard/DashboardTemplates.ts +214 -0
  150. package/Types/Email/EmailTemplateType.ts +2 -0
  151. package/Types/Kubernetes/KubernetesCostIngest.ts +79 -0
  152. package/Types/Monitor/CriteriaFilter.ts +9 -0
  153. package/Types/Monitor/MonitorStep.ts +5 -8
  154. package/Types/Monitor/MonitorStepNetworkDeviceMonitor.ts +27 -5
  155. package/Types/Monitor/MonitorType.ts +15 -5
  156. package/Types/Monitor/SnmpMonitor/ArpEntry.ts +14 -0
  157. package/Types/Monitor/SnmpMonitor/FdbEntry.ts +18 -0
  158. package/Types/Monitor/SnmpMonitor/NetworkDeviceAlertPack.ts +10 -3
  159. package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +31 -5
  160. package/Types/Monitor/SnmpMonitor/SnmpMonitorResponse.ts +12 -0
  161. package/Types/Monitor/SnmpMonitor/SnmpVendorTemplate.ts +231 -3
  162. package/Types/NetworkSite/DefaultNetworkSiteType.ts +20 -0
  163. package/Types/NotificationSetting/NotificationSettingEventType.ts +4 -0
  164. package/Types/Permission.ts +449 -0
  165. package/Types/ServiceLevelObjective/SliType.ts +6 -0
  166. package/Types/ServiceLevelObjective/SloMultiMonitorMode.ts +6 -0
  167. package/Types/ServiceLevelObjective/SloStatus.ts +9 -0
  168. package/Types/ServiceLevelObjective/SloWindowType.ts +6 -0
  169. package/Types/Telemetry/ServiceType.ts +1 -0
  170. package/UI/Components/Filters/EntityFilter.tsx +16 -0
  171. package/UI/Components/Filters/FilterViewer.tsx +22 -5
  172. package/UI/Components/List/List.tsx +8 -0
  173. package/UI/Components/ModelTable/BaseModelTable.tsx +309 -106
  174. package/UI/Components/ModelTable/FilterDataToQuery.ts +172 -0
  175. package/UI/Components/Navbar/NavBar.tsx +16 -2
  176. package/UI/Components/Navbar/NavBarMenuModal.tsx +3 -3
  177. package/UI/Utils/Navigation.ts +10 -0
  178. package/UI/Utils/TableFilterUrlState.ts +204 -17
  179. package/UI/Utils/TableViewUrlState.ts +165 -0
  180. package/Utils/Dashboard/Components/DashboardSloComponent.ts +114 -0
  181. package/Utils/Dashboard/Components/Index.ts +7 -0
  182. package/Utils/IpCanonicalUtil.ts +134 -0
  183. package/Utils/Monitor/EndpointAttachmentUtil.ts +508 -0
  184. package/Utils/Monitor/NetworkTopologyUtil.ts +157 -5
  185. package/Utils/NetworkSite/CidrMatchUtil.ts +296 -0
  186. package/Utils/NetworkSite/MaterializedPathUtil.ts +115 -0
  187. package/Utils/NetworkSite/SiteStatusRollupUtil.ts +97 -0
  188. package/Utils/NetworkSite/SiteUptimeUtil.ts +111 -0
  189. package/Utils/Slo/SloBurnRateRuleState.ts +91 -0
  190. package/Utils/Slo/SloDuration.ts +165 -0
  191. package/Utils/Slo/SloEvaluation.ts +22 -0
  192. package/Utils/Slo/SloHealth.ts +302 -0
  193. package/Utils/Slo/SloStatusColor.ts +54 -0
  194. package/Utils/Slo/SloUtil.ts +637 -0
  195. package/Utils/Slo/SloWidgetFormat.ts +383 -0
  196. package/build/dist/Models/AnalyticsModels/Index.js +4 -0
  197. package/build/dist/Models/AnalyticsModels/Index.js.map +1 -1
  198. package/build/dist/Models/AnalyticsModels/KubernetesCostAllocation.js +713 -0
  199. package/build/dist/Models/AnalyticsModels/KubernetesCostAllocation.js.map +1 -0
  200. package/build/dist/Models/AnalyticsModels/NetworkFlow.js +44 -0
  201. package/build/dist/Models/AnalyticsModels/NetworkFlow.js.map +1 -1
  202. package/build/dist/Models/AnalyticsModels/SloHistory.js +218 -0
  203. package/build/dist/Models/AnalyticsModels/SloHistory.js.map +1 -0
  204. package/build/dist/Models/DatabaseModels/Index.js +20 -0
  205. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  206. package/build/dist/Models/DatabaseModels/NetworkDevice.js +458 -6
  207. package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
  208. package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js +0 -6
  209. package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
  210. package/build/dist/Models/DatabaseModels/NetworkEndpoint.js +779 -0
  211. package/build/dist/Models/DatabaseModels/NetworkEndpoint.js.map +1 -0
  212. package/build/dist/Models/DatabaseModels/NetworkSite.js +1180 -0
  213. package/build/dist/Models/DatabaseModels/NetworkSite.js.map +1 -0
  214. package/build/dist/Models/DatabaseModels/NetworkSiteAssignmentRule.js +534 -0
  215. package/build/dist/Models/DatabaseModels/NetworkSiteAssignmentRule.js.map +1 -0
  216. package/build/dist/Models/DatabaseModels/NetworkSiteLink.js +629 -0
  217. package/build/dist/Models/DatabaseModels/NetworkSiteLink.js.map +1 -0
  218. package/build/dist/Models/DatabaseModels/NetworkSiteStatusTimeline.js +538 -0
  219. package/build/dist/Models/DatabaseModels/NetworkSiteStatusTimeline.js.map +1 -0
  220. package/build/dist/Models/DatabaseModels/NetworkSiteType.js +522 -0
  221. package/build/dist/Models/DatabaseModels/NetworkSiteType.js.map +1 -0
  222. package/build/dist/Models/DatabaseModels/ServiceLevelObjective.js +1134 -0
  223. package/build/dist/Models/DatabaseModels/ServiceLevelObjective.js.map +1 -0
  224. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveBurnRateRule.js +769 -0
  225. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveBurnRateRule.js.map +1 -0
  226. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerTeam.js +460 -0
  227. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerTeam.js.map +1 -0
  228. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerUser.js +459 -0
  229. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerUser.js.map +1 -0
  230. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784757142154-AddNetworkSiteHierarchyAndEndpoints.js +130 -0
  231. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784757142154-AddNetworkSiteHierarchyAndEndpoints.js.map +1 -0
  232. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784897317860-AddNetworkSiteAlerting.js +31 -0
  233. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784897317860-AddNetworkSiteAlerting.js.map +1 -0
  234. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784970388777-AddNetworkDevicePollingColumns.js +36 -0
  235. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784970388777-AddNetworkDevicePollingColumns.js.map +1 -0
  236. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784986826214-AddNetworkSiteTypeTable.js +49 -0
  237. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784986826214-AddNetworkSiteTypeTable.js.map +1 -0
  238. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784987015619-AddServiceLevelObjective.js +140 -0
  239. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784987015619-AddServiceLevelObjective.js.map +1 -0
  240. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785066759532-MigrationName.js +21 -0
  241. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785066759532-MigrationName.js.map +1 -0
  242. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +12 -0
  243. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  244. package/build/dist/Server/Services/AnalyticsDatabaseService.js +23 -6
  245. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  246. package/build/dist/Server/Services/Index.js +24 -0
  247. package/build/dist/Server/Services/Index.js.map +1 -1
  248. package/build/dist/Server/Services/KubernetesCostAllocationService.js +12 -0
  249. package/build/dist/Server/Services/KubernetesCostAllocationService.js.map +1 -0
  250. package/build/dist/Server/Services/MetricService.js +21 -3
  251. package/build/dist/Server/Services/MetricService.js.map +1 -1
  252. package/build/dist/Server/Services/MonitorService.js +19 -0
  253. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  254. package/build/dist/Server/Services/NetworkDeviceService.js +351 -1
  255. package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
  256. package/build/dist/Server/Services/NetworkEndpointService.js +319 -0
  257. package/build/dist/Server/Services/NetworkEndpointService.js.map +1 -0
  258. package/build/dist/Server/Services/NetworkSiteAssignmentRuleService.js +90 -0
  259. package/build/dist/Server/Services/NetworkSiteAssignmentRuleService.js.map +1 -0
  260. package/build/dist/Server/Services/NetworkSiteLinkService.js +9 -0
  261. package/build/dist/Server/Services/NetworkSiteLinkService.js.map +1 -0
  262. package/build/dist/Server/Services/NetworkSiteService.js +1038 -0
  263. package/build/dist/Server/Services/NetworkSiteService.js.map +1 -0
  264. package/build/dist/Server/Services/NetworkSiteStatusTimelineService.js +9 -0
  265. package/build/dist/Server/Services/NetworkSiteStatusTimelineService.js.map +1 -0
  266. package/build/dist/Server/Services/NetworkSiteTypeService.js +9 -0
  267. package/build/dist/Server/Services/NetworkSiteTypeService.js.map +1 -0
  268. package/build/dist/Server/Services/ProjectService.js +98 -2
  269. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  270. package/build/dist/Server/Services/ServiceLevelObjectiveBurnRateRuleService.js +358 -0
  271. package/build/dist/Server/Services/ServiceLevelObjectiveBurnRateRuleService.js.map +1 -0
  272. package/build/dist/Server/Services/ServiceLevelObjectiveOwnerTeamService.js +9 -0
  273. package/build/dist/Server/Services/ServiceLevelObjectiveOwnerTeamService.js.map +1 -0
  274. package/build/dist/Server/Services/ServiceLevelObjectiveOwnerUserService.js +9 -0
  275. package/build/dist/Server/Services/ServiceLevelObjectiveOwnerUserService.js.map +1 -0
  276. package/build/dist/Server/Services/ServiceLevelObjectiveService.js +588 -0
  277. package/build/dist/Server/Services/ServiceLevelObjectiveService.js.map +1 -0
  278. package/build/dist/Server/Services/SloHistoryService.js +77 -0
  279. package/build/dist/Server/Services/SloHistoryService.js.map +1 -0
  280. package/build/dist/Server/Services/UserNotificationSettingService.js +20 -0
  281. package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
  282. package/build/dist/Server/Types/AnalyticsDatabase/AggregateBy.js +9 -0
  283. package/build/dist/Server/Types/AnalyticsDatabase/AggregateBy.js.map +1 -1
  284. package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js +15 -0
  285. package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js.map +1 -1
  286. package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js +31 -0
  287. package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js.map +1 -1
  288. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +76 -48
  289. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
  290. package/build/dist/Server/Utils/Memory.js +9 -1
  291. package/build/dist/Server/Utils/Memory.js.map +1 -1
  292. package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js +132 -10
  293. package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js.map +1 -1
  294. package/build/dist/Server/Utils/Monitor/Data/OuiVendors.json +6920 -0
  295. package/build/dist/Server/Utils/Monitor/DnsResolutionCache.js +77 -0
  296. package/build/dist/Server/Utils/Monitor/DnsResolutionCache.js.map +1 -0
  297. package/build/dist/Server/Utils/Monitor/MonitorResource.js +21 -41
  298. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  299. package/build/dist/Server/Utils/Monitor/NetworkDeviceHydrationUtil.js +152 -40
  300. package/build/dist/Server/Utils/Monitor/NetworkDeviceHydrationUtil.js.map +1 -1
  301. package/build/dist/Server/Utils/Monitor/NetworkDeviceMetricUtil.js +262 -0
  302. package/build/dist/Server/Utils/Monitor/NetworkDeviceMetricUtil.js.map +1 -0
  303. package/build/dist/Server/Utils/Monitor/NetworkDeviceWalkUtil.js +201 -0
  304. package/build/dist/Server/Utils/Monitor/NetworkDeviceWalkUtil.js.map +1 -0
  305. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +152 -120
  306. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
  307. package/build/dist/Server/Utils/Monitor/OuiLookupUtil.js +44 -0
  308. package/build/dist/Server/Utils/Monitor/OuiLookupUtil.js.map +1 -0
  309. package/build/dist/Server/Utils/Monitor/SnmpInterfaceRateUtil.js +10 -10
  310. package/build/dist/Server/Utils/Monitor/SnmpInterfaceRateUtil.js.map +1 -1
  311. package/build/dist/Server/Utils/WhatsAppTemplateUtil.js +5 -0
  312. package/build/dist/Server/Utils/WhatsAppTemplateUtil.js.map +1 -1
  313. package/build/dist/Types/AnalyticsDatabase/AnalyticsTableName.js +10 -0
  314. package/build/dist/Types/AnalyticsDatabase/AnalyticsTableName.js.map +1 -1
  315. package/build/dist/Types/BaseDatabase/EqualTo.js +9 -1
  316. package/build/dist/Types/BaseDatabase/EqualTo.js.map +1 -1
  317. package/build/dist/Types/BaseDatabase/EqualToOrNull.js +9 -1
  318. package/build/dist/Types/BaseDatabase/EqualToOrNull.js.map +1 -1
  319. package/build/dist/Types/BaseDatabase/NotEqual.js +9 -1
  320. package/build/dist/Types/BaseDatabase/NotEqual.js.map +1 -1
  321. package/build/dist/Types/Dashboard/DashboardComponentType.js +1 -0
  322. package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
  323. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +1 -0
  324. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
  325. package/build/dist/Types/Dashboard/DashboardComponents/DashboardSloComponent.js +23 -0
  326. package/build/dist/Types/Dashboard/DashboardComponents/DashboardSloComponent.js.map +1 -0
  327. package/build/dist/Types/Dashboard/DashboardTemplates.js +205 -0
  328. package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
  329. package/build/dist/Types/Email/EmailTemplateType.js +1 -0
  330. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  331. package/build/dist/Types/Kubernetes/KubernetesCostIngest.js +21 -0
  332. package/build/dist/Types/Kubernetes/KubernetesCostIngest.js.map +1 -0
  333. package/build/dist/Types/Monitor/CriteriaFilter.js +1 -0
  334. package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
  335. package/build/dist/Types/Monitor/MonitorStep.js +5 -5
  336. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  337. package/build/dist/Types/Monitor/MonitorStepNetworkDeviceMonitor.js +8 -0
  338. package/build/dist/Types/Monitor/MonitorStepNetworkDeviceMonitor.js.map +1 -1
  339. package/build/dist/Types/Monitor/MonitorType.js +15 -5
  340. package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
  341. package/build/dist/Types/Monitor/SnmpMonitor/ArpEntry.js +2 -0
  342. package/build/dist/Types/Monitor/SnmpMonitor/ArpEntry.js.map +1 -0
  343. package/build/dist/Types/Monitor/SnmpMonitor/FdbEntry.js +2 -0
  344. package/build/dist/Types/Monitor/SnmpMonitor/FdbEntry.js.map +1 -0
  345. package/build/dist/Types/Monitor/SnmpMonitor/NetworkDeviceAlertPack.js +9 -3
  346. package/build/dist/Types/Monitor/SnmpMonitor/NetworkDeviceAlertPack.js.map +1 -1
  347. package/build/dist/Types/Monitor/SnmpMonitor/SnmpVendorTemplate.js +217 -3
  348. package/build/dist/Types/Monitor/SnmpMonitor/SnmpVendorTemplate.js.map +1 -1
  349. package/build/dist/Types/NetworkSite/DefaultNetworkSiteType.js +21 -0
  350. package/build/dist/Types/NetworkSite/DefaultNetworkSiteType.js.map +1 -0
  351. package/build/dist/Types/NotificationSetting/NotificationSettingEventType.js +3 -0
  352. package/build/dist/Types/NotificationSetting/NotificationSettingEventType.js.map +1 -1
  353. package/build/dist/Types/Permission.js +409 -0
  354. package/build/dist/Types/Permission.js.map +1 -1
  355. package/build/dist/Types/ServiceLevelObjective/SliType.js +7 -0
  356. package/build/dist/Types/ServiceLevelObjective/SliType.js.map +1 -0
  357. package/build/dist/Types/ServiceLevelObjective/SloMultiMonitorMode.js +7 -0
  358. package/build/dist/Types/ServiceLevelObjective/SloMultiMonitorMode.js.map +1 -0
  359. package/build/dist/Types/ServiceLevelObjective/SloStatus.js +10 -0
  360. package/build/dist/Types/ServiceLevelObjective/SloStatus.js.map +1 -0
  361. package/build/dist/Types/ServiceLevelObjective/SloWindowType.js +7 -0
  362. package/build/dist/Types/ServiceLevelObjective/SloWindowType.js.map +1 -0
  363. package/build/dist/Types/Telemetry/ServiceType.js +1 -0
  364. package/build/dist/Types/Telemetry/ServiceType.js.map +1 -1
  365. package/build/dist/UI/Components/Filters/EntityFilter.js +15 -0
  366. package/build/dist/UI/Components/Filters/EntityFilter.js.map +1 -1
  367. package/build/dist/UI/Components/Filters/FilterViewer.js +19 -4
  368. package/build/dist/UI/Components/Filters/FilterViewer.js.map +1 -1
  369. package/build/dist/UI/Components/List/List.js +1 -1
  370. package/build/dist/UI/Components/List/List.js.map +1 -1
  371. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +235 -89
  372. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  373. package/build/dist/UI/Components/ModelTable/FilterDataToQuery.js +96 -0
  374. package/build/dist/UI/Components/ModelTable/FilterDataToQuery.js.map +1 -0
  375. package/build/dist/UI/Components/Navbar/NavBar.js +14 -2
  376. package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
  377. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +3 -2
  378. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
  379. package/build/dist/UI/Utils/Navigation.js +9 -0
  380. package/build/dist/UI/Utils/Navigation.js.map +1 -1
  381. package/build/dist/UI/Utils/TableFilterUrlState.js +159 -15
  382. package/build/dist/UI/Utils/TableFilterUrlState.js.map +1 -1
  383. package/build/dist/UI/Utils/TableViewUrlState.js +103 -0
  384. package/build/dist/UI/Utils/TableViewUrlState.js.map +1 -0
  385. package/build/dist/Utils/Dashboard/Components/DashboardSloComponent.js +93 -0
  386. package/build/dist/Utils/Dashboard/Components/DashboardSloComponent.js.map +1 -0
  387. package/build/dist/Utils/Dashboard/Components/Index.js +4 -0
  388. package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
  389. package/build/dist/Utils/IpCanonicalUtil.js +110 -0
  390. package/build/dist/Utils/IpCanonicalUtil.js.map +1 -0
  391. package/build/dist/Utils/Monitor/EndpointAttachmentUtil.js +311 -0
  392. package/build/dist/Utils/Monitor/EndpointAttachmentUtil.js.map +1 -0
  393. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +98 -5
  394. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
  395. package/build/dist/Utils/NetworkSite/CidrMatchUtil.js +222 -0
  396. package/build/dist/Utils/NetworkSite/CidrMatchUtil.js.map +1 -0
  397. package/build/dist/Utils/NetworkSite/MaterializedPathUtil.js +89 -0
  398. package/build/dist/Utils/NetworkSite/MaterializedPathUtil.js.map +1 -0
  399. package/build/dist/Utils/NetworkSite/SiteStatusRollupUtil.js +60 -0
  400. package/build/dist/Utils/NetworkSite/SiteStatusRollupUtil.js.map +1 -0
  401. package/build/dist/Utils/NetworkSite/SiteUptimeUtil.js +76 -0
  402. package/build/dist/Utils/NetworkSite/SiteUptimeUtil.js.map +1 -0
  403. package/build/dist/Utils/Slo/SloBurnRateRuleState.js +35 -0
  404. package/build/dist/Utils/Slo/SloBurnRateRuleState.js.map +1 -0
  405. package/build/dist/Utils/Slo/SloDuration.js +87 -0
  406. package/build/dist/Utils/Slo/SloDuration.js.map +1 -0
  407. package/build/dist/Utils/Slo/SloEvaluation.js +21 -0
  408. package/build/dist/Utils/Slo/SloEvaluation.js.map +1 -0
  409. package/build/dist/Utils/Slo/SloHealth.js +179 -0
  410. package/build/dist/Utils/Slo/SloHealth.js.map +1 -0
  411. package/build/dist/Utils/Slo/SloStatusColor.js +18 -0
  412. package/build/dist/Utils/Slo/SloStatusColor.js.map +1 -0
  413. package/build/dist/Utils/Slo/SloUtil.js +393 -0
  414. package/build/dist/Utils/Slo/SloUtil.js.map +1 -0
  415. package/build/dist/Utils/Slo/SloWidgetFormat.js +174 -0
  416. package/build/dist/Utils/Slo/SloWidgetFormat.js.map +1 -0
  417. package/package.json +2 -2
  418. package/Types/Dashboard/DashboardComponents/Index.ts +0 -7
  419. package/build/dist/Types/Dashboard/DashboardComponents/Index.js +0 -8
  420. package/build/dist/Types/Dashboard/DashboardComponents/Index.js.map +0 -1
@@ -0,0 +1,508 @@
1
+ import ArpEntry from "../../Types/Monitor/SnmpMonitor/ArpEntry";
2
+ import CdpNeighbor from "../../Types/Monitor/SnmpMonitor/CdpNeighbor";
3
+ import FdbEntry from "../../Types/Monitor/SnmpMonitor/FdbEntry";
4
+ import LldpNeighbor from "../../Types/Monitor/SnmpMonitor/LldpNeighbor";
5
+
6
+ /*
7
+ * Pure helpers that turn ONE device's ARP/FDB walk snapshot into endpoint
8
+ * attachments (which MAC sits on which switch port) and IP bindings (which
9
+ * MAC answers for which IP on a router). Everything here is deterministic
10
+ * and side-effect free so it can run — and be tested — anywhere; persistence
11
+ * lives in NetworkEndpointService, which consumes this output.
12
+ */
13
+
14
+ // One interface row from the walk, reduced to what attachment needs.
15
+ export interface EndpointWalkInterface {
16
+ interfaceIndex: number;
17
+ name?: string | undefined;
18
+ macAddress?: string | undefined;
19
+ }
20
+
21
+ // Everything one device's walk knows that endpoint discovery cares about.
22
+ export interface EndpointWalkSnapshot {
23
+ deviceId: string;
24
+ fdbEntries?: Array<FdbEntry> | undefined;
25
+ arpEntries?: Array<ArpEntry> | undefined;
26
+ lldpNeighbors?: Array<LldpNeighbor> | undefined;
27
+ cdpNeighbors?: Array<CdpNeighbor> | undefined;
28
+ interfaces?: Array<EndpointWalkInterface> | undefined;
29
+ }
30
+
31
+ // "This MAC was learned on this access port of the walked device."
32
+ export interface EndpointAttachment {
33
+ macAddress: string;
34
+ attachedInterfaceIndex: number;
35
+ attachedPortName?: string | undefined;
36
+ vlanId?: number | undefined;
37
+ }
38
+
39
+ // "This MAC answers for this IP in the walked device's ARP table."
40
+ export interface EndpointIpBinding {
41
+ macAddress: string;
42
+ ipAddress: string;
43
+ routerInterfaceIndex: number;
44
+ }
45
+
46
+ export interface EndpointAttachmentResult {
47
+ /*
48
+ * Interfaces with an LLDP/CDP neighbor on them. MACs learned there are
49
+ * transit traffic from the rest of the network, not local endpoints.
50
+ */
51
+ uplinkInterfaceIndexes: Array<number>;
52
+ // The device's own interface MACs — never endpoints.
53
+ selfMacs: Array<string>;
54
+ attachments: Array<EndpointAttachment>;
55
+ ipBindings: Array<EndpointIpBinding>;
56
+ }
57
+
58
+ /*
59
+ * What the persistence layer should do for one (existing row, new
60
+ * observation) pair. Kept as plain data so the precedence rules are
61
+ * exhaustively unit-testable without a database.
62
+ */
63
+ export interface EndpointIncomingObservation {
64
+ macAddress: string;
65
+ // The walked device (switch and/or router) that saw this MAC.
66
+ deviceId: string;
67
+ // Present when the device's FDB learned the MAC on an access port.
68
+ attachment?:
69
+ | {
70
+ interfaceIndex: number;
71
+ portName?: string | undefined;
72
+ vlanId?: number | undefined;
73
+ }
74
+ | undefined;
75
+ // Present when the device's ARP table binds the MAC to an IP.
76
+ ipBinding?:
77
+ | {
78
+ ipAddress: string;
79
+ routerInterfaceIndex: number;
80
+ }
81
+ | undefined;
82
+ }
83
+
84
+ /*
85
+ * The stored row, as far as the precedence rules care. Everything past
86
+ * attachedNetworkDeviceId exists so decideUpsert can tell a genuinely
87
+ * new observation from a re-sighting of an endpoint that has not moved
88
+ * since the last poll (see ENDPOINT_LAST_SEEN_REFRESH_MS).
89
+ */
90
+ export interface EndpointExistingRowSnapshot {
91
+ attachedNetworkDeviceId?: string | undefined;
92
+ attachedInterfaceIndex?: number | undefined;
93
+ attachedPortName?: string | undefined;
94
+ vlanId?: number | undefined;
95
+ ipAddress?: string | undefined;
96
+ siteId?: string | undefined;
97
+ firstSeenAt?: Date | undefined;
98
+ lastSeenAt?: Date | undefined;
99
+ }
100
+
101
+ /*
102
+ * How stale a row's lastSeenAt is allowed to get before an otherwise
103
+ * unchanged observation is written purely to refresh it.
104
+ *
105
+ * Freshness is load-bearing: NetworkTopologyUtil calls an endpoint
106
+ * "down" once lastSeenAt is older than 15 minutes. Suppressing the
107
+ * refresh entirely would mark every stable endpoint down, so the real
108
+ * bound is that stored lastSeenAt may lag reality by at most
109
+ * (refresh threshold + poll interval). At 5 minutes that stays inside
110
+ * the 15-minute window for any poll interval up to 10 minutes, and for
111
+ * slower polls the threshold simply stops suppressing anything (age is
112
+ * always past it) and the behaviour degrades back to write-every-poll —
113
+ * so the optimisation can never make a device look down.
114
+ */
115
+ export const ENDPOINT_LAST_SEEN_REFRESH_MS: number = 5 * 60 * 1000;
116
+
117
+ /*
118
+ * Caller-supplied context for the "has anything actually changed?"
119
+ * check. Optional: without it decideUpsert keeps its original
120
+ * write-on-every-sighting behaviour.
121
+ */
122
+ export interface EndpointUpsertContext {
123
+ now: Date;
124
+ // Site of the observing device, when the caller knows it.
125
+ siteId?: string | undefined;
126
+ lastSeenRefreshMs?: number | undefined;
127
+ }
128
+
129
+ export interface EndpointUpsertDecision {
130
+ action: "create" | "update" | "none";
131
+ /*
132
+ * When true, write attachedNetworkDeviceId (the observing device),
133
+ * attachedInterfaceIndex, and — when defined — attachedPortName / vlanId.
134
+ * Undefined port/vlan never clear previously stored values.
135
+ */
136
+ setAttachment: boolean;
137
+ attachedInterfaceIndex?: number | undefined;
138
+ attachedPortName?: string | undefined;
139
+ vlanId?: number | undefined;
140
+ setIpAddress: boolean;
141
+ ipAddress?: string | undefined;
142
+ }
143
+
144
+ /*
145
+ * Multicast/broadcast MACs (group bit set in the first octet) are traffic
146
+ * artifacts, not endpoints — e.g. 01:00:5e:… IPv4 multicast and the
147
+ * ff:ff:ff:ff:ff:ff broadcast rows some ARP tables carry.
148
+ */
149
+ const GROUP_ADDRESS_BIT: number = 0x01;
150
+
151
+ /*
152
+ * Hoisted out of the call site so the literal is not the object of a member
153
+ * expression, which `wrap-regex` and Prettier cannot agree on.
154
+ */
155
+ const MAC_HEX_PATTERN: RegExp = /^[0-9a-f]{12}$/;
156
+
157
+ type MacCandidate = {
158
+ entry: FdbEntry;
159
+ interfaceIndex: number;
160
+ };
161
+
162
+ export default class EndpointAttachmentUtil {
163
+ /*
164
+ * Normalizes any common MAC spelling — AA-BB-CC-DD-EE-FF,
165
+ * aabb.ccdd.eeff (Cisco dot form), aa:bb:cc:dd:ee:ff, bare hex, with or
166
+ * without an 0x prefix — to lowercase colon form. Returns undefined for
167
+ * anything that isn't exactly 12 hex digits once separators are removed.
168
+ */
169
+ public static normalizeMac(value: string | undefined): string | undefined {
170
+ if (!value) {
171
+ return undefined;
172
+ }
173
+ let hex: string = value.trim().toLowerCase();
174
+ if (hex.startsWith("0x")) {
175
+ hex = hex.substring(2);
176
+ }
177
+ hex = hex.replace(/[:\-.\s]/g, "");
178
+ if (!MAC_HEX_PATTERN.test(hex)) {
179
+ return undefined;
180
+ }
181
+ const pairs: Array<string> = [];
182
+ for (let i: number = 0; i < 12; i += 2) {
183
+ pairs.push(hex.substring(i, i + 2));
184
+ }
185
+ return pairs.join(":");
186
+ }
187
+
188
+ public static computeEndpointAttachments(
189
+ snapshot: EndpointWalkSnapshot,
190
+ ): EndpointAttachmentResult {
191
+ /*
192
+ * Uplinks: any interface an LLDP or CDP neighbor claims sits on. This
193
+ * is the same local-port linkage NetworkTopologyUtil uses to draw
194
+ * device-to-device edges — both protocols report the local ifIndex
195
+ * directly on the neighbor entry.
196
+ */
197
+ const uplinkSet: Set<number> = new Set();
198
+ for (const neighbor of snapshot.lldpNeighbors || []) {
199
+ if (neighbor.localInterfaceIndex !== undefined) {
200
+ uplinkSet.add(neighbor.localInterfaceIndex);
201
+ }
202
+ }
203
+ for (const neighbor of snapshot.cdpNeighbors || []) {
204
+ if (neighbor.localInterfaceIndex !== undefined) {
205
+ uplinkSet.add(neighbor.localInterfaceIndex);
206
+ }
207
+ }
208
+
209
+ const selfMacSet: Set<string> = new Set();
210
+ const interfaceNameByIndex: Map<number, string> = new Map();
211
+ for (const walkInterface of snapshot.interfaces || []) {
212
+ const selfMac: string | undefined = EndpointAttachmentUtil.normalizeMac(
213
+ walkInterface.macAddress,
214
+ );
215
+ if (selfMac) {
216
+ selfMacSet.add(selfMac);
217
+ }
218
+ if (walkInterface.name) {
219
+ interfaceNameByIndex.set(
220
+ walkInterface.interfaceIndex,
221
+ walkInterface.name,
222
+ );
223
+ }
224
+ }
225
+
226
+ /*
227
+ * FDB → attachments. An entry qualifies when:
228
+ * - its MAC normalizes, is unicast, and is not one of the device's own,
229
+ * - its status (when reported) is "learned" — "self"/"mgmt" rows are
230
+ * the bridge talking about itself,
231
+ * - its bridge port resolved to an ifIndex (without one we cannot rule
232
+ * out an uplink, and misattaching every transit MAC to this switch
233
+ * is worse than skipping the entry), and
234
+ * - that ifIndex is not an uplink.
235
+ */
236
+ const candidatesByMac: Map<string, Array<MacCandidate>> = new Map();
237
+ for (const entry of snapshot.fdbEntries || []) {
238
+ const mac: string | undefined = EndpointAttachmentUtil.normalizeMac(
239
+ entry.macAddress,
240
+ );
241
+ if (!mac || EndpointAttachmentUtil.isGroupMac(mac)) {
242
+ continue;
243
+ }
244
+ if (selfMacSet.has(mac)) {
245
+ continue;
246
+ }
247
+ if (
248
+ entry.status !== undefined &&
249
+ entry.status.toLowerCase() !== "learned"
250
+ ) {
251
+ continue;
252
+ }
253
+ if (entry.interfaceIndex === undefined) {
254
+ continue;
255
+ }
256
+ if (uplinkSet.has(entry.interfaceIndex)) {
257
+ continue;
258
+ }
259
+ const candidates: Array<MacCandidate> = candidatesByMac.get(mac) || [];
260
+ candidates.push({ entry, interfaceIndex: entry.interfaceIndex });
261
+ candidatesByMac.set(mac, candidates);
262
+ }
263
+
264
+ const attachments: Array<EndpointAttachment> = [];
265
+ for (const mac of Array.from(candidatesByMac.keys()).sort()) {
266
+ /*
267
+ * One attachment per MAC. Duplicates (Q-BRIDGE reports the same MAC
268
+ * per VLAN) collapse deterministically: lowest ifIndex, then lowest
269
+ * VLAN (undefined last), then lowest bridge port.
270
+ */
271
+ const candidates: Array<MacCandidate> = candidatesByMac.get(mac)!;
272
+ candidates.sort((a: MacCandidate, b: MacCandidate) => {
273
+ if (a.interfaceIndex !== b.interfaceIndex) {
274
+ return a.interfaceIndex - b.interfaceIndex;
275
+ }
276
+ const aVlan: number = a.entry.vlanId ?? Number.MAX_SAFE_INTEGER;
277
+ const bVlan: number = b.entry.vlanId ?? Number.MAX_SAFE_INTEGER;
278
+ if (aVlan !== bVlan) {
279
+ return aVlan - bVlan;
280
+ }
281
+ return a.entry.bridgePort - b.entry.bridgePort;
282
+ });
283
+ const chosen: MacCandidate = candidates[0]!;
284
+ attachments.push({
285
+ macAddress: mac,
286
+ attachedInterfaceIndex: chosen.interfaceIndex,
287
+ attachedPortName: interfaceNameByIndex.get(chosen.interfaceIndex),
288
+ vlanId: chosen.entry.vlanId,
289
+ });
290
+ }
291
+
292
+ /*
293
+ * ARP → IP bindings. Self and group MACs are excluded, as are rows the
294
+ * device itself marked invalid. Duplicates for one MAC collapse to the
295
+ * lexicographically smallest IP (then lowest ifIndex) so repeated walks
296
+ * always report the same binding.
297
+ */
298
+ const bindingsByMac: Map<string, Array<ArpEntry>> = new Map();
299
+ for (const entry of snapshot.arpEntries || []) {
300
+ const mac: string | undefined = EndpointAttachmentUtil.normalizeMac(
301
+ entry.macAddress,
302
+ );
303
+ if (!mac || EndpointAttachmentUtil.isGroupMac(mac)) {
304
+ continue;
305
+ }
306
+ if (selfMacSet.has(mac)) {
307
+ continue;
308
+ }
309
+ if (
310
+ entry.entryType !== undefined &&
311
+ entry.entryType.toLowerCase() === "invalid"
312
+ ) {
313
+ continue;
314
+ }
315
+ if (!entry.ipAddress) {
316
+ continue;
317
+ }
318
+ const rows: Array<ArpEntry> = bindingsByMac.get(mac) || [];
319
+ rows.push(entry);
320
+ bindingsByMac.set(mac, rows);
321
+ }
322
+
323
+ const ipBindings: Array<EndpointIpBinding> = [];
324
+ for (const mac of Array.from(bindingsByMac.keys()).sort()) {
325
+ const rows: Array<ArpEntry> = bindingsByMac.get(mac)!;
326
+ rows.sort((a: ArpEntry, b: ArpEntry) => {
327
+ if (a.ipAddress !== b.ipAddress) {
328
+ return a.ipAddress < b.ipAddress ? -1 : 1;
329
+ }
330
+ return a.interfaceIndex - b.interfaceIndex;
331
+ });
332
+ const chosen: ArpEntry = rows[0]!;
333
+ ipBindings.push({
334
+ macAddress: mac,
335
+ ipAddress: chosen.ipAddress,
336
+ routerInterfaceIndex: chosen.interfaceIndex,
337
+ });
338
+ }
339
+
340
+ return {
341
+ uplinkInterfaceIndexes: Array.from(uplinkSet).sort(
342
+ (a: number, b: number) => {
343
+ return a - b;
344
+ },
345
+ ),
346
+ selfMacs: Array.from(selfMacSet).sort(),
347
+ attachments,
348
+ ipBindings,
349
+ };
350
+ }
351
+
352
+ /*
353
+ * The precedence rules for persisting one observation onto one
354
+ * (projectId, macAddress) row:
355
+ *
356
+ * - A switch-FDB attachment always wins: the FDB says which physical
357
+ * port the endpoint hangs off, which no ARP row can know.
358
+ * - An ARP-only observation always refreshes ipAddress, but only claims
359
+ * the attachment (pointing at the router's own interface) when the row
360
+ * has no attachment yet or is already attached to that same router —
361
+ * it must never steal an attachment a switch reported.
362
+ * - Rows are created for both kinds; every observation counts as a
363
+ * "touch" (the caller bumps lastSeenAt on any create/update action).
364
+ *
365
+ * When `context` is supplied, an update that would write nothing but a
366
+ * lastSeenAt already refreshed within lastSeenRefreshMs collapses to
367
+ * "none" — a stable 4096-MAC switch must not issue 4096 writes a poll
368
+ * just to bump a timestamp nobody reads that precisely.
369
+ */
370
+ public static decideUpsert(
371
+ existing: EndpointExistingRowSnapshot | null,
372
+ incoming: EndpointIncomingObservation,
373
+ context?: EndpointUpsertContext | undefined,
374
+ ): EndpointUpsertDecision {
375
+ if (!incoming.attachment && !incoming.ipBinding) {
376
+ return {
377
+ action: "none",
378
+ setAttachment: false,
379
+ setIpAddress: false,
380
+ };
381
+ }
382
+
383
+ const decision: EndpointUpsertDecision = {
384
+ action: existing ? "update" : "create",
385
+ setAttachment: false,
386
+ setIpAddress: false,
387
+ };
388
+
389
+ if (incoming.ipBinding) {
390
+ decision.setIpAddress = true;
391
+ decision.ipAddress = incoming.ipBinding.ipAddress;
392
+ }
393
+
394
+ if (incoming.attachment) {
395
+ decision.setAttachment = true;
396
+ decision.attachedInterfaceIndex = incoming.attachment.interfaceIndex;
397
+ decision.attachedPortName = incoming.attachment.portName;
398
+ decision.vlanId = incoming.attachment.vlanId;
399
+ } else {
400
+ // ARP-only from here on — incoming.ipBinding is set (guard above).
401
+ const attachedElsewhere: boolean = Boolean(
402
+ existing?.attachedNetworkDeviceId &&
403
+ existing.attachedNetworkDeviceId !== incoming.deviceId,
404
+ );
405
+ if (!attachedElsewhere) {
406
+ decision.setAttachment = true;
407
+ decision.attachedInterfaceIndex =
408
+ incoming.ipBinding!.routerInterfaceIndex;
409
+ }
410
+ }
411
+
412
+ if (
413
+ existing &&
414
+ context &&
415
+ EndpointAttachmentUtil.isUnchangedSighting(
416
+ existing,
417
+ decision,
418
+ incoming.deviceId,
419
+ context,
420
+ )
421
+ ) {
422
+ return {
423
+ action: "none",
424
+ setAttachment: false,
425
+ setIpAddress: false,
426
+ };
427
+ }
428
+
429
+ return decision;
430
+ }
431
+
432
+ /*
433
+ * True when applying `decision` to `existing` would change nothing a
434
+ * reader can observe, and lastSeenAt is fresh enough that skipping the
435
+ * refresh is safe. Deliberately conservative — any doubt (a row that
436
+ * still needs the firstSeenAt backfill, a row whose lastSeenAt sits in
437
+ * the future because of clock skew, a row we have never seen a
438
+ * timestamp for) falls through to a real write.
439
+ */
440
+ private static isUnchangedSighting(
441
+ existing: EndpointExistingRowSnapshot,
442
+ decision: EndpointUpsertDecision,
443
+ incomingDeviceId: string,
444
+ context: EndpointUpsertContext,
445
+ ): boolean {
446
+ // Rows predating first-seen tracking still owe us a backfill.
447
+ if (!existing.firstSeenAt || !existing.lastSeenAt) {
448
+ return false;
449
+ }
450
+
451
+ const refreshMs: number =
452
+ context.lastSeenRefreshMs ?? ENDPOINT_LAST_SEEN_REFRESH_MS;
453
+ const ageMs: number =
454
+ context.now.getTime() - new Date(existing.lastSeenAt).getTime();
455
+ if (ageMs < 0 || ageMs >= refreshMs) {
456
+ return false;
457
+ }
458
+
459
+ if (
460
+ decision.setIpAddress &&
461
+ decision.ipAddress &&
462
+ decision.ipAddress !== existing.ipAddress
463
+ ) {
464
+ return false;
465
+ }
466
+
467
+ if (decision.setAttachment) {
468
+ if (existing.attachedNetworkDeviceId !== incomingDeviceId) {
469
+ return false;
470
+ }
471
+ if (
472
+ decision.attachedInterfaceIndex !== undefined &&
473
+ decision.attachedInterfaceIndex !== existing.attachedInterfaceIndex
474
+ ) {
475
+ return false;
476
+ }
477
+ /*
478
+ * Undefined port/vlan never clear a stored value, so "the walk did
479
+ * not report one" is not a change.
480
+ */
481
+ if (
482
+ decision.attachedPortName !== undefined &&
483
+ decision.attachedPortName !== existing.attachedPortName
484
+ ) {
485
+ return false;
486
+ }
487
+ if (
488
+ decision.vlanId !== undefined &&
489
+ decision.vlanId !== existing.vlanId
490
+ ) {
491
+ return false;
492
+ }
493
+ if (context.siteId !== undefined && context.siteId !== existing.siteId) {
494
+ return false;
495
+ }
496
+ }
497
+
498
+ return true;
499
+ }
500
+
501
+ private static isGroupMac(normalizedMac: string): boolean {
502
+ const firstOctet: number = parseInt(normalizedMac.substring(0, 2), 16);
503
+ return (firstOctet & GROUP_ADDRESS_BIT) !== 0;
504
+ }
505
+ }
506
+
507
+ export const normalizeMac: (value: string | undefined) => string | undefined =
508
+ EndpointAttachmentUtil.normalizeMac;