@oneuptime/common 12.0.4 → 12.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (374) hide show
  1. package/Models/DatabaseModels/AIRun.ts +38 -1
  2. package/Models/DatabaseModels/AlertFeed.ts +30 -0
  3. package/Models/DatabaseModels/Dashboard.ts +28 -0
  4. package/Models/DatabaseModels/Incident.ts +2 -0
  5. package/Models/DatabaseModels/IncidentEpisode.ts +1 -0
  6. package/Models/DatabaseModels/IncidentEpisodePublicNote.ts +1 -0
  7. package/Models/DatabaseModels/IncidentEpisodeStateTimeline.ts +1 -0
  8. package/Models/DatabaseModels/IncidentFeed.ts +30 -0
  9. package/Models/DatabaseModels/IncidentPublicNote.ts +1 -0
  10. package/Models/DatabaseModels/IncidentStateTimeline.ts +1 -0
  11. package/Models/DatabaseModels/LogSavedView.ts +33 -0
  12. package/Models/DatabaseModels/Project.ts +231 -9
  13. package/Models/DatabaseModels/ScheduledMaintenance.ts +1 -0
  14. package/Models/DatabaseModels/ScheduledMaintenancePublicNote.ts +1 -0
  15. package/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.ts +1 -0
  16. package/Models/DatabaseModels/ScheduledMaintenanceTemplateOwnerUser.ts +4 -4
  17. package/Models/DatabaseModels/StatusPage.ts +28 -0
  18. package/Models/DatabaseModels/StatusPageAnnouncement.ts +1 -0
  19. package/Server/API/AIAgentDataAPI.ts +44 -55
  20. package/Server/API/AIInvestigationAPI.ts +175 -17
  21. package/Server/API/DashboardAPI.ts +10 -10
  22. package/Server/API/StatusPageAPI.ts +38 -19
  23. package/Server/EnvironmentConfig.ts +16 -0
  24. package/Server/Infrastructure/ClickhouseConfig.ts +15 -1
  25. package/Server/Infrastructure/ClickhouseDatabase.ts +1 -1
  26. package/Server/Infrastructure/Postgres/SchemaMigrations/1786096660558-AddTimeRangeToLogSavedView.ts +27 -0
  27. package/Server/Infrastructure/Postgres/SchemaMigrations/1786101798351-MigrationName.ts +89 -0
  28. package/Server/Infrastructure/Postgres/SchemaMigrations/1786105470826-MigrationName.ts +27 -0
  29. package/Server/Infrastructure/Postgres/SchemaMigrations/1786400000000-AddMasterPasswordSalt.ts +40 -0
  30. package/Server/Infrastructure/Postgres/SchemaMigrations/1786500000000-AddInvestigationCodeFixRecommendation.ts +26 -0
  31. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -0
  32. package/Server/Services/AIRunService.ts +78 -10
  33. package/Server/Services/AIService.ts +94 -22
  34. package/Server/Services/AlertFeedService.ts +5 -0
  35. package/Server/Services/AnalyticsDatabaseService.ts +29 -0
  36. package/Server/Services/DatabaseService.ts +43 -2
  37. package/Server/Services/IncidentFeedService.ts +5 -0
  38. package/Server/Services/LlmLogService.ts +46 -2
  39. package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +6 -0
  40. package/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.ts +4 -0
  41. package/Server/Utils/AI/CodeFix/CodeFixReadiness.ts +8 -5
  42. package/Server/Utils/AI/CodeFix/FixRunBudget.ts +90 -16
  43. package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +2 -0
  44. package/Server/Utils/AI/Remediation/RemediationPlanRunner.ts +2 -0
  45. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +223 -22
  46. package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +25 -24
  47. package/Server/Utils/AI/SRE/ConfidenceSignal.ts +182 -71
  48. package/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.ts +252 -43
  49. package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +25 -24
  50. package/Server/Utils/AI/SRE/InstrumentationTaskTrigger.ts +47 -13
  51. package/Server/Utils/AI/SRE/InvestigationGrader.ts +9 -5
  52. package/Server/Utils/AI/SRE/InvestigationQueue.ts +141 -24
  53. package/Server/Utils/AI/SRE/InvestigationSubjectLock.ts +66 -0
  54. package/Server/Utils/AI/SRE/PostedRootCause.ts +94 -3
  55. package/Server/Utils/AI/SRE/README.md +1 -1
  56. package/Server/Utils/AI/SRE/SubjectCodeFixRun.ts +132 -10
  57. package/Server/Utils/AnalyticsDatabase/ClusterConfig.ts +24 -0
  58. package/Server/Utils/Monitor/Criteria/APIRequestCriteria.ts +29 -0
  59. package/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.ts +56 -0
  60. package/Server/Utils/Monitor/MonitorMetricUtil.ts +46 -0
  61. package/Server/Utils/Monitor/MonitorTemplateUtil.ts +1 -0
  62. package/Server/Utils/PasswordHash.ts +28 -4
  63. package/Tests/App/Dashboard/AIInvestigationHeaderStatus.test.tsx +251 -0
  64. package/Tests/App/Dashboard/EventStatusPanel.test.tsx +747 -0
  65. package/Tests/App/Dashboard/InvestigationFeedRefresh.test.tsx +480 -0
  66. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +1184 -0
  67. package/Tests/App/Dashboard/InvestigationPanelStatus.test.tsx +500 -0
  68. package/Tests/App/Dashboard/PortMonitorCriteriaFilter.test.ts +93 -0
  69. package/Tests/App/StatusPage/PublicStatusPageAPIErrorHandling.test.ts +117 -0
  70. package/Tests/App/StatusPage/StatusPageModelAPIPolymorphism.test.ts +223 -0
  71. package/Tests/Models/DatabaseModels/PermissionCatalogueCoverage.test.ts +243 -0
  72. package/Tests/Server/API/AIAgentDataFixFromIncidentContext.test.ts +266 -0
  73. package/Tests/Server/API/AIInvestigationAPI.test.ts +594 -0
  74. package/Tests/Server/API/AIInvestigationCreateFixTask.test.ts +80 -0
  75. package/Tests/Server/API/DashboardMasterPasswordAPI.test.ts +172 -1
  76. package/Tests/Server/API/StatusPageMasterPasswordAPI.test.ts +335 -0
  77. package/Tests/Server/Services/AIRunCodeFixClaim.test.ts +59 -5
  78. package/Tests/Server/Services/AIRunHumanVerdict.test.ts +127 -58
  79. package/Tests/Server/Services/AIServiceDailyBudget.test.ts +396 -20
  80. package/Tests/Server/Services/AddMasterPasswordSaltMigration.test.ts +283 -0
  81. package/Tests/Server/Services/AnalyticsDatabaseService.test.ts +25 -1
  82. package/Tests/Server/Services/DatabaseServicePerUserPasswordSalt.test.ts +45 -35
  83. package/Tests/Server/Services/DatabaseServiceUpdateColumnsWithoutHooks.test.ts +54 -0
  84. package/Tests/Server/Services/FeedAIRunAssociation.test.ts +125 -0
  85. package/Tests/Server/Services/FixFromIncidentTaskTrigger.test.ts +418 -57
  86. package/Tests/Server/Services/InstrumentationTaskTrigger.test.ts +173 -14
  87. package/Tests/Server/Services/MasterPasswordScrypt.test.ts +567 -0
  88. package/Tests/Server/Services/SeparateIncidentAlertAiSettingsMigration.test.ts +194 -0
  89. package/Tests/Server/Utils/AI/AIAlertGating.test.ts +37 -4
  90. package/Tests/Server/Utils/AI/AIConfidenceSignal.test.ts +330 -29
  91. package/Tests/Server/Utils/AI/AIIncidentGating.test.ts +25 -0
  92. package/Tests/Server/Utils/AI/AIInvestigationQueue.test.ts +293 -5
  93. package/Tests/Server/Utils/AI/CodeFixAgentCompletion.test.ts +59 -1
  94. package/Tests/Server/Utils/AI/FixFromIncidentAutoTrigger.test.ts +342 -19
  95. package/Tests/Server/Utils/AI/FixRunBudget.test.ts +214 -30
  96. package/Tests/Server/Utils/AI/Insights/InvestigationQueueInsightSubject.test.ts +2 -0
  97. package/Tests/Server/Utils/AI/InvestigationGrader.test.ts +65 -17
  98. package/Tests/Server/Utils/AI/InvestigationQueueRemediationExecution.test.ts +3 -0
  99. package/Tests/Server/Utils/AI/InvestigationSettlement.test.ts +313 -20
  100. package/Tests/Server/Utils/AI/PostedRootCause.test.ts +254 -0
  101. package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +4 -0
  102. package/Tests/Server/Utils/AI/RemediationPlanRunner.test.ts +2 -0
  103. package/Tests/Server/Utils/AI/SubjectCodeFixRunDedupe.test.ts +44 -0
  104. package/Tests/Server/Utils/AnalyticsDatabase/ClusterAwareSchema.test.ts +30 -0
  105. package/Tests/Server/Utils/Monitor/Criteria/APIRequestCriteriaPortTimings.test.ts +212 -0
  106. package/Tests/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.test.ts +401 -0
  107. package/Tests/Server/Utils/Monitor/MonitorMetricUtilPortTimings.test.ts +243 -0
  108. package/Tests/Server/Utils/PasswordHash.test.ts +30 -0
  109. package/Tests/Types/Dashboard/DashboardTemplates.test.ts +111 -0
  110. package/Tests/Types/Monitor/CephMetricCatalog.test.ts +104 -0
  111. package/Tests/Types/Monitor/CriteriaFilter.test.ts +4 -0
  112. package/Tests/Types/Monitor/DockerMetricCatalog.test.ts +109 -0
  113. package/Tests/Types/Monitor/DockerSwarmMetricCatalog.test.ts +118 -0
  114. package/Tests/Types/Monitor/HostMetricCatalog.test.ts +104 -0
  115. package/Tests/Types/Monitor/IotMetricCatalog.test.ts +104 -0
  116. package/Tests/Types/Monitor/KubernetesMetricCatalog.test.ts +118 -0
  117. package/Tests/Types/Monitor/MonitorCriteriaInstance.test.ts +42 -0
  118. package/Tests/Types/Monitor/MonitorStep.test.ts +38 -0
  119. package/Tests/Types/Monitor/MonitorStepDomainMonitor.test.ts +75 -0
  120. package/Tests/Types/Monitor/PodmanMetricCatalog.test.ts +109 -0
  121. package/Tests/Types/Monitor/ProxmoxMetricCatalog.test.ts +107 -0
  122. package/Tests/Types/Monitor/SnmpMonitor/SnmpVendorTemplate.test.ts +151 -0
  123. package/Tests/Types/Permission.test.ts +137 -0
  124. package/Tests/Types/Rum/SessionReplayMaskingMode.test.ts +101 -0
  125. package/Tests/UI/Components/AiInvestigationSettingsCard.test.tsx +5 -5
  126. package/Tests/UI/Components/CardModelDetailEdit.test.tsx +3 -5
  127. package/Tests/UI/Components/ErrorBoundary.test.tsx +158 -0
  128. package/Tests/UI/Components/FeedItemSafeMode.test.tsx +74 -0
  129. package/Tests/UI/Components/MoreMenu.test.tsx +756 -0
  130. package/Tests/UI/Components/StatusPage/ResourceGroupSection.test.tsx +62 -0
  131. package/Tests/UI/Monitor/PortMonitorView.test.tsx +237 -0
  132. package/Tests/UI/Telemetry/TelemetrySnapshotWindowAlert.test.tsx +91 -0
  133. package/Tests/UI/Utils/DownloadFile.test.ts +103 -0
  134. package/Tests/UI/Utils/ErrorSupportBundle.test.ts +582 -0
  135. package/Tests/UI/Utils/Project.test.ts +31 -0
  136. package/Tests/UI/Utils/StatusPageModelAPIInjection.test.ts +245 -0
  137. package/Tests/UI/Utils/UseDashboardGridDnd.test.tsx +820 -0
  138. package/Tests/Utils/Dashboard/DashboardViewConfig.test.ts +329 -0
  139. package/Tests/Utils/Dashboard/GridLayout.test.ts +951 -0
  140. package/Tests/Utils/Monitor/MonitorMetricType.test.ts +117 -0
  141. package/Tests/Utils/RecordingRuleExpression.test.ts +327 -0
  142. package/Tests/Utils/StatusPage/GroupNestingLayout.test.ts +186 -0
  143. package/Tests/Utils/StatusPage/GroupTree.test.ts +456 -0
  144. package/Tests/Utils/StatusPage/OverviewGroupHierarchyVisibility.test.ts +588 -0
  145. package/Tests/Utils/Telemetry/SavedViewTimeRange.test.ts +244 -0
  146. package/Tests/Utils/Telemetry/TelemetryQueryTimeRange.test.ts +825 -0
  147. package/Types/AI/AIRunCodeFixRecommendation.ts +17 -0
  148. package/Types/AI/CodeFixTaskContext.ts +63 -3
  149. package/Types/AI/CodeFixTaskType.ts +3 -3
  150. package/Types/HashedString.ts +4 -3
  151. package/Types/Monitor/CriteriaFilter.ts +4 -0
  152. package/Types/Monitor/DomainMonitor/DomainLookupMethod.ts +23 -0
  153. package/Types/Monitor/DomainMonitor/DomainMonitorResponse.ts +7 -0
  154. package/Types/Monitor/MonitorCriteriaInstance.ts +24 -7
  155. package/Types/Monitor/MonitorMetricType.ts +8 -0
  156. package/Types/Monitor/MonitorStep.ts +10 -1
  157. package/Types/Monitor/MonitorStepDomainMonitor.ts +21 -0
  158. package/Types/Monitor/PortMonitor/PortMonitorTimings.ts +10 -0
  159. package/Types/Permission.ts +23 -175
  160. package/Types/Probe/ProbeMonitorResponse.ts +6 -0
  161. package/UI/Components/BulkUpdate/BulkUpdateForm.tsx +2 -3
  162. package/UI/Components/ErrorBoundary.tsx +138 -11
  163. package/UI/Components/Feed/FeedItem.tsx +9 -2
  164. package/UI/Components/ModelDetail/CardModelDetail.tsx +0 -12
  165. package/UI/Components/ModelTable/BaseModelTable.tsx +1 -1
  166. package/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.ts +12 -2
  167. package/UI/Components/MoreMenu/MoreMenu.tsx +285 -97
  168. package/UI/Components/MoreMenu/MoreMenuItem.tsx +8 -5
  169. package/UI/Utils/DownloadFile.ts +16 -8
  170. package/UI/Utils/ErrorSupportBundle.ts +805 -0
  171. package/UI/Utils/ModelAPI/ModelAPI.ts +19 -8
  172. package/UI/Utils/Project.ts +4 -0
  173. package/UI/Utils/StatusPage.ts +8 -2
  174. package/UI/Utils/UseDashboardGridDnd.ts +917 -0
  175. package/Utils/Dashboard/DashboardViewConfig.ts +80 -36
  176. package/Utils/Dashboard/GridLayout.ts +523 -0
  177. package/Utils/Metrics/RecordingRuleExpression.ts +19 -17
  178. package/Utils/Monitor/MonitorMetricType.ts +44 -2
  179. package/Utils/StatusPage/GroupNestingLayout.ts +69 -0
  180. package/Utils/StatusPage/GroupTree.ts +157 -66
  181. package/Utils/StatusPage/ResourceUptime.ts +37 -2
  182. package/Utils/Telemetry/SavedViewTimeRange.ts +114 -0
  183. package/Utils/Telemetry/TelemetryQueryTimeRange.ts +334 -0
  184. package/build/dist/Models/DatabaseModels/AIRun.js +39 -1
  185. package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -1
  186. package/build/dist/Models/DatabaseModels/AlertFeed.js +32 -0
  187. package/build/dist/Models/DatabaseModels/AlertFeed.js.map +1 -1
  188. package/build/dist/Models/DatabaseModels/Dashboard.js +29 -0
  189. package/build/dist/Models/DatabaseModels/Dashboard.js.map +1 -1
  190. package/build/dist/Models/DatabaseModels/Incident.js +2 -0
  191. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  192. package/build/dist/Models/DatabaseModels/IncidentEpisode.js +1 -0
  193. package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -1
  194. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js +1 -0
  195. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js.map +1 -1
  196. package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js +1 -0
  197. package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js.map +1 -1
  198. package/build/dist/Models/DatabaseModels/IncidentFeed.js +32 -0
  199. package/build/dist/Models/DatabaseModels/IncidentFeed.js.map +1 -1
  200. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js +1 -0
  201. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js.map +1 -1
  202. package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js +1 -0
  203. package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js.map +1 -1
  204. package/build/dist/Models/DatabaseModels/LogSavedView.js +33 -0
  205. package/build/dist/Models/DatabaseModels/LogSavedView.js.map +1 -1
  206. package/build/dist/Models/DatabaseModels/Project.js +241 -11
  207. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  208. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +1 -0
  209. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
  210. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js +1 -0
  211. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js.map +1 -1
  212. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js +1 -0
  213. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js.map +1 -1
  214. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplateOwnerUser.js +4 -4
  215. package/build/dist/Models/DatabaseModels/StatusPage.js +29 -0
  216. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  217. package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js +1 -0
  218. package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js.map +1 -1
  219. package/build/dist/Server/API/AIAgentDataAPI.js +28 -46
  220. package/build/dist/Server/API/AIAgentDataAPI.js.map +1 -1
  221. package/build/dist/Server/API/AIInvestigationAPI.js +111 -11
  222. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  223. package/build/dist/Server/API/DashboardAPI.js +8 -5
  224. package/build/dist/Server/API/DashboardAPI.js.map +1 -1
  225. package/build/dist/Server/API/StatusPageAPI.js +29 -11
  226. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  227. package/build/dist/Server/EnvironmentConfig.js +14 -0
  228. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  229. package/build/dist/Server/Infrastructure/ClickhouseConfig.js +12 -1
  230. package/build/dist/Server/Infrastructure/ClickhouseConfig.js.map +1 -1
  231. package/build/dist/Server/Infrastructure/ClickhouseDatabase.js +1 -1
  232. package/build/dist/Server/Infrastructure/ClickhouseDatabase.js.map +1 -1
  233. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786096660558-AddTimeRangeToLogSavedView.js +22 -0
  234. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786096660558-AddTimeRangeToLogSavedView.js.map +1 -0
  235. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786101798351-MigrationName.js +36 -0
  236. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786101798351-MigrationName.js.map +1 -0
  237. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786105470826-MigrationName.js +18 -0
  238. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786105470826-MigrationName.js.map +1 -0
  239. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786400000000-AddMasterPasswordSalt.js +19 -0
  240. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786400000000-AddMasterPasswordSalt.js.map +1 -0
  241. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786500000000-AddInvestigationCodeFixRecommendation.js +19 -0
  242. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786500000000-AddInvestigationCodeFixRecommendation.js.map +1 -0
  243. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +10 -0
  244. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  245. package/build/dist/Server/Services/AIRunService.js +58 -12
  246. package/build/dist/Server/Services/AIRunService.js.map +1 -1
  247. package/build/dist/Server/Services/AIService.js +74 -21
  248. package/build/dist/Server/Services/AIService.js.map +1 -1
  249. package/build/dist/Server/Services/AlertFeedService.js +3 -0
  250. package/build/dist/Server/Services/AlertFeedService.js.map +1 -1
  251. package/build/dist/Server/Services/AnalyticsDatabaseService.js +29 -1
  252. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  253. package/build/dist/Server/Services/DatabaseService.js +21 -2
  254. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  255. package/build/dist/Server/Services/IncidentFeedService.js +3 -0
  256. package/build/dist/Server/Services/IncidentFeedService.js.map +1 -1
  257. package/build/dist/Server/Services/LlmLogService.js +22 -1
  258. package/build/dist/Server/Services/LlmLogService.js.map +1 -1
  259. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +3 -0
  260. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
  261. package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.js +4 -0
  262. package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.js.map +1 -1
  263. package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js +8 -5
  264. package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js.map +1 -1
  265. package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js +63 -24
  266. package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js.map +1 -1
  267. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +2 -0
  268. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
  269. package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js +2 -0
  270. package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js.map +1 -1
  271. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +156 -28
  272. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  273. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +21 -19
  274. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
  275. package/build/dist/Server/Utils/AI/SRE/ConfidenceSignal.js +128 -49
  276. package/build/dist/Server/Utils/AI/SRE/ConfidenceSignal.js.map +1 -1
  277. package/build/dist/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.js +170 -31
  278. package/build/dist/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.js.map +1 -1
  279. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +21 -19
  280. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
  281. package/build/dist/Server/Utils/AI/SRE/InstrumentationTaskTrigger.js +33 -9
  282. package/build/dist/Server/Utils/AI/SRE/InstrumentationTaskTrigger.js.map +1 -1
  283. package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js +9 -5
  284. package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js.map +1 -1
  285. package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js +121 -41
  286. package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js.map +1 -1
  287. package/build/dist/Server/Utils/AI/SRE/InvestigationSubjectLock.js +46 -0
  288. package/build/dist/Server/Utils/AI/SRE/InvestigationSubjectLock.js.map +1 -0
  289. package/build/dist/Server/Utils/AI/SRE/PostedRootCause.js +61 -14
  290. package/build/dist/Server/Utils/AI/SRE/PostedRootCause.js.map +1 -1
  291. package/build/dist/Server/Utils/AI/SRE/SubjectCodeFixRun.js +79 -1
  292. package/build/dist/Server/Utils/AI/SRE/SubjectCodeFixRun.js.map +1 -1
  293. package/build/dist/Server/Utils/AnalyticsDatabase/ClusterConfig.js +21 -0
  294. package/build/dist/Server/Utils/AnalyticsDatabase/ClusterConfig.js.map +1 -1
  295. package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js +20 -3
  296. package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js.map +1 -1
  297. package/build/dist/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.js +43 -0
  298. package/build/dist/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.js.map +1 -1
  299. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +33 -0
  300. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
  301. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +1 -0
  302. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
  303. package/build/dist/Server/Utils/PasswordHash.js +25 -4
  304. package/build/dist/Server/Utils/PasswordHash.js.map +1 -1
  305. package/build/dist/Types/AI/AIRunCodeFixRecommendation.js +18 -0
  306. package/build/dist/Types/AI/AIRunCodeFixRecommendation.js.map +1 -0
  307. package/build/dist/Types/AI/CodeFixTaskContext.js +23 -3
  308. package/build/dist/Types/AI/CodeFixTaskContext.js.map +1 -1
  309. package/build/dist/Types/AI/CodeFixTaskType.js +3 -3
  310. package/build/dist/Types/HashedString.js +4 -3
  311. package/build/dist/Types/HashedString.js.map +1 -1
  312. package/build/dist/Types/Monitor/CriteriaFilter.js +4 -0
  313. package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
  314. package/build/dist/Types/Monitor/DomainMonitor/DomainLookupMethod.js +24 -0
  315. package/build/dist/Types/Monitor/DomainMonitor/DomainLookupMethod.js.map +1 -0
  316. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +24 -7
  317. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
  318. package/build/dist/Types/Monitor/MonitorMetricType.js +7 -0
  319. package/build/dist/Types/Monitor/MonitorMetricType.js.map +1 -1
  320. package/build/dist/Types/Monitor/MonitorStep.js +6 -1
  321. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  322. package/build/dist/Types/Monitor/MonitorStepDomainMonitor.js +15 -0
  323. package/build/dist/Types/Monitor/MonitorStepDomainMonitor.js.map +1 -1
  324. package/build/dist/Types/Monitor/PortMonitor/PortMonitorTimings.js +2 -0
  325. package/build/dist/Types/Monitor/PortMonitor/PortMonitorTimings.js.map +1 -0
  326. package/build/dist/Types/Permission.js +23 -155
  327. package/build/dist/Types/Permission.js.map +1 -1
  328. package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js +2 -5
  329. package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js.map +1 -1
  330. package/build/dist/UI/Components/ErrorBoundary.js +67 -8
  331. package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
  332. package/build/dist/UI/Components/Feed/FeedItem.js +2 -2
  333. package/build/dist/UI/Components/Feed/FeedItem.js.map +1 -1
  334. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +0 -12
  335. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
  336. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +1 -1
  337. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  338. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js +12 -2
  339. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js.map +1 -1
  340. package/build/dist/UI/Components/MoreMenu/MoreMenu.js +193 -65
  341. package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
  342. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +1 -1
  343. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
  344. package/build/dist/UI/Utils/DownloadFile.js +15 -8
  345. package/build/dist/UI/Utils/DownloadFile.js.map +1 -1
  346. package/build/dist/UI/Utils/ErrorSupportBundle.js +476 -0
  347. package/build/dist/UI/Utils/ErrorSupportBundle.js.map +1 -0
  348. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +18 -8
  349. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
  350. package/build/dist/UI/Utils/Project.js +3 -0
  351. package/build/dist/UI/Utils/Project.js.map +1 -1
  352. package/build/dist/UI/Utils/StatusPage.js +4 -4
  353. package/build/dist/UI/Utils/StatusPage.js.map +1 -1
  354. package/build/dist/UI/Utils/UseDashboardGridDnd.js +551 -0
  355. package/build/dist/UI/Utils/UseDashboardGridDnd.js.map +1 -0
  356. package/build/dist/Utils/Dashboard/DashboardViewConfig.js +42 -26
  357. package/build/dist/Utils/Dashboard/DashboardViewConfig.js.map +1 -1
  358. package/build/dist/Utils/Dashboard/GridLayout.js +338 -0
  359. package/build/dist/Utils/Dashboard/GridLayout.js.map +1 -0
  360. package/build/dist/Utils/Metrics/RecordingRuleExpression.js +19 -17
  361. package/build/dist/Utils/Metrics/RecordingRuleExpression.js.map +1 -1
  362. package/build/dist/Utils/Monitor/MonitorMetricType.js +38 -6
  363. package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
  364. package/build/dist/Utils/StatusPage/GroupNestingLayout.js +46 -0
  365. package/build/dist/Utils/StatusPage/GroupNestingLayout.js.map +1 -1
  366. package/build/dist/Utils/StatusPage/GroupTree.js +79 -34
  367. package/build/dist/Utils/StatusPage/GroupTree.js.map +1 -1
  368. package/build/dist/Utils/StatusPage/ResourceUptime.js +22 -1
  369. package/build/dist/Utils/StatusPage/ResourceUptime.js.map +1 -1
  370. package/build/dist/Utils/Telemetry/SavedViewTimeRange.js +79 -0
  371. package/build/dist/Utils/Telemetry/SavedViewTimeRange.js.map +1 -0
  372. package/build/dist/Utils/Telemetry/TelemetryQueryTimeRange.js +236 -0
  373. package/build/dist/Utils/Telemetry/TelemetryQueryTimeRange.js.map +1 -0
  374. package/package.json +1 -1
@@ -37,6 +37,16 @@ describe("getAggregationTypeByMonitorMetricType", () => {
37
37
  MonitorMetricType.IsOnline,
38
38
  ),
39
39
  ).toBe(AggregationType.Min);
40
+ expect(
41
+ MonitorMetricTypeUtil.getAggregationTypeByMonitorMetricType(
42
+ MonitorMetricType.PortDnsLookupTime,
43
+ ),
44
+ ).toBe(AggregationType.Avg);
45
+ expect(
46
+ MonitorMetricTypeUtil.getAggregationTypeByMonitorMetricType(
47
+ MonitorMetricType.PortTcpConnectTime,
48
+ ),
49
+ ).toBe(AggregationType.Avg);
40
50
  });
41
51
 
42
52
  test("cumulative byte/op counters aggregate with Max (latest-in-bucket)", () => {
@@ -70,6 +80,16 @@ describe("getMonitorMeticTypeByCheckOn", () => {
70
80
  CheckOn.CPUIoWaitPercent,
71
81
  ),
72
82
  ).toBe(MonitorMetricType.CPUTimeIoWaitPercent);
83
+ expect(
84
+ MonitorMetricTypeUtil.getMonitorMeticTypeByCheckOn(
85
+ CheckOn.PortDnsLookupTime,
86
+ ),
87
+ ).toBe(MonitorMetricType.PortDnsLookupTime);
88
+ expect(
89
+ MonitorMetricTypeUtil.getMonitorMeticTypeByCheckOn(
90
+ CheckOn.PortTcpConnectTime,
91
+ ),
92
+ ).toBe(MonitorMetricType.PortTcpConnectTime);
73
93
  });
74
94
 
75
95
  test("throws for a CheckOn without a metric mapping", () => {
@@ -115,6 +135,34 @@ describe("getMonitorMetricTypesByMonitorType", () => {
115
135
  expect(ping).toContain(MonitorMetricType.Jitter);
116
136
  });
117
137
 
138
+ test("Port exposes total connection time and its Port-specific phases", () => {
139
+ expect(
140
+ MonitorMetricTypeUtil.getMonitorMetricTypesByMonitorType(
141
+ MonitorType.Port,
142
+ ),
143
+ ).toEqual([
144
+ MonitorMetricType.IsOnline,
145
+ MonitorMetricType.ResponseTime,
146
+ MonitorMetricType.PortDnsLookupTime,
147
+ MonitorMetricType.PortTcpConnectTime,
148
+ ]);
149
+ });
150
+
151
+ test("Port phase metrics do not alias the HTTP phase metric names", () => {
152
+ expect(MonitorMetricType.PortDnsLookupTime).toBe(
153
+ "oneuptime.monitor.port.dns.lookup.time",
154
+ );
155
+ expect(MonitorMetricType.PortTcpConnectTime).toBe(
156
+ "oneuptime.monitor.port.tcp.connect.time",
157
+ );
158
+ expect(MonitorMetricType.PortDnsLookupTime).not.toBe(
159
+ MonitorMetricType.DnsLookupTime,
160
+ );
161
+ expect(MonitorMetricType.PortTcpConnectTime).not.toBe(
162
+ MonitorMetricType.TcpConnectTime,
163
+ );
164
+ });
165
+
118
166
  test("a monitor type with no metrics returns an empty list", () => {
119
167
  expect(
120
168
  MonitorMetricTypeUtil.getMonitorMetricTypesByMonitorType(
@@ -187,6 +235,75 @@ describe("display metadata", () => {
187
235
  MonitorMetricType.ResponseStatusCode,
188
236
  ),
189
237
  ).toBe("");
238
+ expect(
239
+ MonitorMetricTypeUtil.getLegendUnitByMonitorMetricType(
240
+ MonitorMetricType.PortDnsLookupTime,
241
+ ),
242
+ ).toBe("ms");
243
+ expect(
244
+ MonitorMetricTypeUtil.getLegendUnitByMonitorMetricType(
245
+ MonitorMetricType.PortTcpConnectTime,
246
+ ),
247
+ ).toBe("ms");
248
+ });
249
+
250
+ test("Port phase metrics have distinct user-facing metadata", () => {
251
+ expect(
252
+ MonitorMetricTypeUtil.getTitleByMonitorMetricType(
253
+ MonitorMetricType.PortDnsLookupTime,
254
+ ),
255
+ ).toBe("Port DNS Lookup Time");
256
+ expect(
257
+ MonitorMetricTypeUtil.getTitleByMonitorMetricType(
258
+ MonitorMetricType.PortTcpConnectTime,
259
+ ),
260
+ ).toBe("Port TCP Connect Time");
261
+ expect(
262
+ MonitorMetricTypeUtil.getDescriptionByMonitorMetricType(
263
+ MonitorMetricType.PortDnsLookupTime,
264
+ ),
265
+ ).toContain("IP address");
266
+ expect(
267
+ MonitorMetricTypeUtil.getDescriptionByMonitorMetricType(
268
+ MonitorMetricType.PortTcpConnectTime,
269
+ ),
270
+ ).toContain("IPv6/IPv4 fallback");
271
+ });
272
+
273
+ test("contextualizes the existing response-time metric for Port charts", () => {
274
+ expect(
275
+ MonitorMetricTypeUtil.getTitleByMonitorMetricType(
276
+ MonitorMetricType.ResponseTime,
277
+ MonitorType.Port,
278
+ ),
279
+ ).toBe("Total Connection Time (DNS + TCP)");
280
+ expect(
281
+ MonitorMetricTypeUtil.getLegendByMonitorMetricType(
282
+ MonitorMetricType.ResponseTime,
283
+ MonitorType.Port,
284
+ ),
285
+ ).toBe("Total Connection Time (DNS + TCP)");
286
+ expect(
287
+ MonitorMetricTypeUtil.getDescriptionByMonitorMetricType(
288
+ MonitorMetricType.ResponseTime,
289
+ MonitorType.Port,
290
+ ),
291
+ ).toContain("resolving the hostname");
292
+ });
293
+
294
+ test("keeps response-time metadata unchanged outside Port charts", () => {
295
+ expect(
296
+ MonitorMetricTypeUtil.getTitleByMonitorMetricType(
297
+ MonitorMetricType.ResponseTime,
298
+ MonitorType.Website,
299
+ ),
300
+ ).toBe("Response Time");
301
+ expect(
302
+ MonitorMetricTypeUtil.getDescriptionByMonitorMetricType(
303
+ MonitorMetricType.ResponseTime,
304
+ MonitorType.Website,
305
+ ),
306
+ ).toContain("server to respond to a request");
190
307
  });
191
308
  });
192
309
 
@@ -1,10 +1,13 @@
1
1
  import {
2
2
  evaluate,
3
3
  parse,
4
+ tokenize,
4
5
  validate,
5
6
  type Node,
6
7
  type ParseError,
7
8
  type ParseResult,
9
+ type Token,
10
+ type ValidateResult,
8
11
  } from "../../Utils/Metrics/RecordingRuleExpression";
9
12
 
10
13
  function parseOk(input: string): ParseResult {
@@ -17,6 +20,53 @@ function parseOk(input: string): ParseResult {
17
20
  return r;
18
21
  }
19
22
 
23
+ function parseErr(input: string): ParseError {
24
+ const r: ParseResult | ParseError = parse(input);
25
+ if (r.ok) {
26
+ throw new Error(`Expected parse to fail for "${input}" but it succeeded`);
27
+ }
28
+ return r;
29
+ }
30
+
31
+ function tokensOf(input: string): Array<Token> {
32
+ const t: Array<Token> | ParseError = tokenize(input);
33
+ if (!Array.isArray(t)) {
34
+ throw new Error(
35
+ `Expected tokenize to succeed for "${input}" but got: ${t.error}`,
36
+ );
37
+ }
38
+ return t;
39
+ }
40
+
41
+ function tokenTypes(input: string): Array<string> {
42
+ return tokensOf(input).map((t: Token) => {
43
+ return t.type;
44
+ });
45
+ }
46
+
47
+ function tokenValues(input: string): Array<string> {
48
+ return tokensOf(input).map((t: Token) => {
49
+ return t.value;
50
+ });
51
+ }
52
+
53
+ function tokenizeErr(input: string): ParseError {
54
+ const t: Array<Token> | ParseError = tokenize(input);
55
+ if (Array.isArray(t)) {
56
+ throw new Error(`Expected tokenize to fail for "${input}" but it did not`);
57
+ }
58
+ return t;
59
+ }
60
+
61
+ // ((((A)))) with `depth` pairs of parens.
62
+ function nestParens(depth: number): string {
63
+ let input: string = "A";
64
+ for (let i: number = 0; i < depth; i++) {
65
+ input = `(${input})`;
66
+ }
67
+ return input;
68
+ }
69
+
20
70
  function evalOk(input: string, bindings: Record<string, number>): number {
21
71
  const r: ParseResult = parseOk(input);
22
72
  const v: number | null = evaluate(r.ast, bindings);
@@ -174,4 +224,281 @@ describe("RecordingRuleExpression", () => {
174
224
  expect(ast.right.op).toBe("*");
175
225
  });
176
226
  });
227
+
228
+ describe("tokenize", () => {
229
+ test("emits typed tokens carrying their source position", () => {
230
+ expect(tokensOf("A + 1")).toEqual([
231
+ { type: "ident", value: "A", pos: 0 },
232
+ { type: "op", value: "+", pos: 2 },
233
+ { type: "number", value: "1", pos: 4 },
234
+ ]);
235
+ });
236
+
237
+ test("parens get their own token types", () => {
238
+ expect(tokenTypes("(A)")).toEqual(["lparen", "ident", "rparen"]);
239
+ });
240
+
241
+ test("adjacent operators need no separating space", () => {
242
+ expect(tokenValues("A*B/C")).toEqual(["A", "*", "B", "/", "C"]);
243
+ });
244
+
245
+ test("space, tab, newline and carriage return are all skipped, and positions still point at the original string", () => {
246
+ expect(tokensOf(" A\t\n\rB ")).toEqual([
247
+ { type: "ident", value: "A", pos: 2 },
248
+ { type: "ident", value: "B", pos: 6 },
249
+ ]);
250
+ });
251
+
252
+ test("an unknown character aborts tokenizing and reports where", () => {
253
+ const r: ParseError = tokenizeErr("A@B");
254
+ expect(r.error).toBe('Unexpected character "@" at position 1');
255
+ expect(r.position).toBe(1);
256
+ });
257
+ });
258
+
259
+ describe("number lexing", () => {
260
+ test("a trailing dot is consumed into the number and reads as the integer", () => {
261
+ // The lexer takes the '.' unconditionally, so "1." is one number token.
262
+ expect(tokensOf("1.")).toEqual([{ type: "number", value: "1.", pos: 0 }]);
263
+ expect(evalOk("1.", {})).toBe(1);
264
+ });
265
+
266
+ test("leading zeros are kept in the token but not in the value", () => {
267
+ expect(tokensOf("007")[0]?.value).toBe("007");
268
+ expect(evalOk("007", {})).toBe(7);
269
+ });
270
+
271
+ test("a second dot is not part of the number and is rejected as a stray character", () => {
272
+ const r: ParseError = parseErr("2.5.5");
273
+ expect(r.error).toBe('Unexpected character "." at position 3');
274
+ expect(r.position).toBe(3);
275
+ });
276
+
277
+ test("a number may not start with a dot", () => {
278
+ const r: ParseError = parseErr(".5");
279
+ expect(r.error).toBe('Unexpected character "." at position 0');
280
+ expect(r.position).toBe(0);
281
+ });
282
+
283
+ test("digits followed by letters lex as two tokens, so the parser rejects them", () => {
284
+ expect(tokenTypes("1a")).toEqual(["number", "ident"]);
285
+ expect(parseErr("1a").error).toBe('Unexpected token "a" at position 1');
286
+ });
287
+ });
288
+
289
+ describe("identifier lexing rules", () => {
290
+ test("an identifier may be a bare underscore", () => {
291
+ expect(tokensOf("_")).toEqual([{ type: "ident", value: "_", pos: 0 }]);
292
+ });
293
+
294
+ test("digits are allowed after the first character but not as the first", () => {
295
+ expect(tokensOf("_9")[0]?.value).toBe("_9");
296
+ expect(tokensOf("a9_b")[0]?.value).toBe("a9_b");
297
+ // A leading digit starts a number instead, which leaves a stray ident.
298
+ expect(tokenTypes("9a")).toEqual(["number", "ident"]);
299
+ });
300
+
301
+ test("characters outside [A-Za-z0-9_] are not identifier characters", () => {
302
+ expect(tokenizeErr("$a").position).toBe(0);
303
+ // '-' ends the identifier and becomes an operator rather than joining it.
304
+ expect(tokenValues("a-b")).toEqual(["a", "-", "b"]);
305
+ });
306
+ });
307
+
308
+ describe("parse error shapes", () => {
309
+ test("running out of input reports no position, because there is no token to point at", () => {
310
+ for (const input of ["", " ", "A +", "("]) {
311
+ const r: ParseError = parseErr(input);
312
+ expect(r.error).toBe("Unexpected end of expression");
313
+ expect(r.position).toBeUndefined();
314
+ }
315
+ });
316
+
317
+ test("an unclosed paren at end of input also has no position", () => {
318
+ const r: ParseError = parseErr("(A + B");
319
+ expect(r.error).toBe("Expected ')'");
320
+ expect(r.position).toBeUndefined();
321
+ });
322
+
323
+ test("a stray trailing token names the position both in the message and in the field", () => {
324
+ const r: ParseError = parseErr("A B");
325
+ expect(r.error).toBe('Unexpected token "B" at position 2');
326
+ expect(r.position).toBe(2);
327
+ });
328
+
329
+ test("a token in operand position reports the field but omits the position from the message", () => {
330
+ // Note the asymmetry with the trailing-token message above.
331
+ const r: ParseError = parseErr("A + + B");
332
+ expect(r.error).toBe('Unexpected token "+"');
333
+ expect(r.position).toBe(4);
334
+ });
335
+
336
+ test("empty parens and a leading binary operator are both rejected in operand position", () => {
337
+ expect(parseErr("()").error).toBe('Unexpected token ")"');
338
+ expect(parseErr("()").position).toBe(1);
339
+ expect(parseErr("* A").error).toBe('Unexpected token "*"');
340
+ expect(parseErr("* A").position).toBe(0);
341
+ });
342
+ });
343
+
344
+ describe("associativity", () => {
345
+ test("'-' is left associative, so A - B - C is (A - B) - C", () => {
346
+ const ast: Node = parseOk("A - B - C").ast;
347
+ expect(ast.type).toBe("binary");
348
+ if (ast.type !== "binary") {
349
+ return;
350
+ }
351
+ expect(ast.left.type).toBe("binary");
352
+ expect(ast.right.type).toBe("ident");
353
+ // Right associativity would give 10 - (3 - 2) = 9.
354
+ expect(evalOk("A - B - C", { A: 10, B: 3, C: 2 })).toBe(5);
355
+ });
356
+
357
+ test("'/' is left associative, so A / B / C is (A / B) / C", () => {
358
+ const ast: Node = parseOk("A / B / C").ast;
359
+ expect(ast.type).toBe("binary");
360
+ if (ast.type !== "binary") {
361
+ return;
362
+ }
363
+ expect(ast.left.type).toBe("binary");
364
+ expect(ast.right.type).toBe("ident");
365
+ // Right associativity would give 100 / (5 / 2) = 40.
366
+ expect(evalOk("A / B / C", { A: 100, B: 5, C: 2 })).toBe(10);
367
+ });
368
+
369
+ test("equal-precedence operators mix left to right", () => {
370
+ const ast: Node = parseOk("A - B + C").ast;
371
+ expect(ast.type).toBe("binary");
372
+ if (ast.type !== "binary") {
373
+ return;
374
+ }
375
+ // The last operator ends up at the root.
376
+ expect(ast.op).toBe("+");
377
+ expect(evalOk("A - B + C", { A: 10, B: 3, C: 2 })).toBe(9);
378
+ expect(evalOk("A / B * C", { A: 100, B: 5, C: 2 })).toBe(40);
379
+ });
380
+ });
381
+
382
+ describe("nesting depth boundary", () => {
383
+ test("32 levels of parens still parse", () => {
384
+ expect(parse(nestParens(32)).ok).toBe(true);
385
+ });
386
+
387
+ test("33 levels are rejected, with no position", () => {
388
+ const r: ParseError = parseErr(nestParens(33));
389
+ expect(r.error).toBe("Expression nesting too deep");
390
+ expect(r.position).toBeUndefined();
391
+ });
392
+
393
+ test("32 unary operators still parse", () => {
394
+ expect(parse(`${"-".repeat(32)}A`).ok).toBe(true);
395
+ });
396
+
397
+ test("33 unary operators are rejected, with no position", () => {
398
+ const r: ParseError = parseErr(`${"-".repeat(33)}A`);
399
+ expect(r.error).toBe("Expression nesting too deep");
400
+ expect(r.position).toBeUndefined();
401
+ });
402
+
403
+ test("parentheses and unary operators share the nesting limit", () => {
404
+ const input: string = `${"(".repeat(16)}${"-".repeat(17)}A${")".repeat(16)}`;
405
+ expect(parseErr(input).error).toBe("Expression nesting too deep");
406
+ });
407
+
408
+ test("the limit is on nesting, not on expression length", () => {
409
+ // 200 chained additions stay at depth 1 and must not trip the guard.
410
+ let chain: string = "A";
411
+ for (let i: number = 0; i < 200; i++) {
412
+ chain += " + A";
413
+ }
414
+ expect(parse(chain).ok).toBe(true);
415
+ expect(evalOk(chain, { A: 1 })).toBe(201);
416
+ });
417
+ });
418
+
419
+ describe("identifier collection order", () => {
420
+ test("identifiers come back in first-appearance order, not sorted", () => {
421
+ expect(parseOk("C + A + B").identifiers).toEqual(["C", "A", "B"]);
422
+ });
423
+
424
+ test("a repeated identifier is listed once", () => {
425
+ expect(parseOk("A + A").identifiers).toEqual(["A"]);
426
+ });
427
+
428
+ test("order follows the parse, so a parenthesised group is collected as it is reached", () => {
429
+ expect(parseOk("Z * (Y + X)").identifiers).toEqual(["Z", "Y", "X"]);
430
+ });
431
+
432
+ test("an expression of pure literals binds nothing", () => {
433
+ expect(parseOk("1 + 2 * 3").identifiers).toEqual([]);
434
+ });
435
+ });
436
+
437
+ describe("validate passthrough", () => {
438
+ test("a parse error carrying a position keeps it", () => {
439
+ const r: ValidateResult = validate("A @ B", ["A"]);
440
+ expect(r.ok).toBe(false);
441
+ expect(r.error).toBe('Unexpected character "@" at position 2');
442
+ expect(r.position).toBe(2);
443
+ });
444
+
445
+ test("a parse error without a position does not invent one", () => {
446
+ const r: ValidateResult = validate("(A", ["A"]);
447
+ expect(r.ok).toBe(false);
448
+ expect(r.error).toBe("Expected ')'");
449
+ expect(r.position).toBeUndefined();
450
+ });
451
+
452
+ test("every unknown identifier is reported, in first-appearance order", () => {
453
+ const r: ValidateResult = validate("A + X + Y", ["A"]);
454
+ expect(r.ok).toBe(false);
455
+ expect(r.error).toBe("Unknown identifier(s): X, Y");
456
+ expect(r.unknownIdentifiers).toEqual(["X", "Y"]);
457
+ });
458
+
459
+ test("an expression with no identifiers passes an empty allow list", () => {
460
+ expect(validate("1 + 1", [])).toEqual({ ok: true });
461
+ });
462
+
463
+ test("a successful validate reports nothing but ok", () => {
464
+ const r: ValidateResult = validate("A + B", ["A", "B", "C"]);
465
+ expect(r.ok).toBe(true);
466
+ expect(r.error).toBeUndefined();
467
+ expect(r.unknownIdentifiers).toBeUndefined();
468
+ });
469
+ });
470
+
471
+ describe("evaluate edge cases", () => {
472
+ test("a non-number Object.prototype member is not a binding", () => {
473
+ /*
474
+ * bindings["toString"] is a function, so the typeof guard must reject it
475
+ * rather than letting a prototype member leak into the result.
476
+ */
477
+ const r: ParseResult = parseOk("toString");
478
+ expect(evaluate(r.ast, {})).toBeNull();
479
+ });
480
+
481
+ test("a numeric inherited property is not a binding", () => {
482
+ const bindings: Record<string, number> = Object.create({ inherited: 7 });
483
+ const r: ParseResult = parseOk("inherited");
484
+ expect(evaluate(r.ast, bindings)).toBeNull();
485
+ });
486
+
487
+ test("an own property on a null-prototype binding map is accepted", () => {
488
+ const bindings: Record<string, number> = Object.create(null);
489
+ bindings["A"] = 7;
490
+ const r: ParseResult = parseOk("A");
491
+ expect(evaluate(r.ast, bindings)).toBe(7);
492
+ });
493
+
494
+ test("0 / 0 is null rather than NaN", () => {
495
+ const r: ParseResult = parseOk("A / B");
496
+ expect(evaluate(r.ast, { A: 0, B: 0 })).toBeNull();
497
+ });
498
+
499
+ test("double unary minus cancels out", () => {
500
+ expect(evalOk("--A", { A: 5 })).toBe(5);
501
+ expect(evalOk("-3 * -2", {})).toBe(6);
502
+ });
503
+ });
177
504
  });