@oneuptime/common 12.0.2 → 12.0.4

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 (384) hide show
  1. package/Models/DatabaseModels/CephCluster.ts +14 -0
  2. package/Models/DatabaseModels/CodeRepository.ts +14 -6
  3. package/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.ts +14 -0
  4. package/Models/DatabaseModels/DockerHost.ts +14 -0
  5. package/Models/DatabaseModels/GlobalOidcProject.ts +18 -0
  6. package/Models/DatabaseModels/GlobalSsoProject.ts +18 -0
  7. package/Models/DatabaseModels/Index.ts +2 -0
  8. package/Models/DatabaseModels/IoTFleet.ts +14 -0
  9. package/Models/DatabaseModels/KubernetesCluster.ts +14 -0
  10. package/Models/DatabaseModels/LogDropFilter.ts +9 -20
  11. package/Models/DatabaseModels/NetworkDevice.ts +14 -0
  12. package/Models/DatabaseModels/NetworkInterface.ts +18 -0
  13. package/Models/DatabaseModels/Project.ts +16 -2
  14. package/Models/DatabaseModels/ProxmoxCluster.ts +14 -0
  15. package/Models/DatabaseModels/Service.ts +30 -0
  16. package/Models/DatabaseModels/StatusPageMonitorRule.ts +911 -0
  17. package/Models/DatabaseModels/StatusPagePrivateUser.ts +30 -0
  18. package/Models/DatabaseModels/StatusPageResource.ts +73 -0
  19. package/Models/DatabaseModels/TraceDropFilter.ts +9 -20
  20. package/Models/DatabaseModels/User.ts +30 -0
  21. package/Server/API/AIAgentDataAPI.ts +31 -0
  22. package/Server/API/BaseAPI.ts +13 -2
  23. package/Server/API/DashboardAPI.ts +458 -12
  24. package/Server/API/GitHubAPI.ts +119 -283
  25. package/Server/API/UserAPI.ts +263 -1
  26. package/Server/EnvironmentConfig.ts +38 -4
  27. package/Server/Infrastructure/GlobalCache.ts +152 -0
  28. package/Server/Infrastructure/Postgres/DataSourceOptions.ts +16 -0
  29. package/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.ts +123 -0
  30. package/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.ts +21 -0
  31. package/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.ts +68 -0
  32. package/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.ts +100 -0
  33. package/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.ts +214 -0
  34. package/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.ts +69 -0
  35. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +12 -0
  36. package/Server/Services/AlertStateTimelineService.ts +20 -0
  37. package/Server/Services/AnalyticsDatabaseService.ts +72 -1
  38. package/Server/Services/CephClusterService.ts +26 -86
  39. package/Server/Services/CloudResourceService.ts +23 -83
  40. package/Server/Services/CodeRepositoryService.ts +105 -2
  41. package/Server/Services/DatabaseService.ts +296 -2
  42. package/Server/Services/DockerHostService.ts +19 -79
  43. package/Server/Services/DockerSwarmClusterService.ts +27 -88
  44. package/Server/Services/HostService.ts +46 -99
  45. package/Server/Services/IncidentStateTimelineService.ts +20 -0
  46. package/Server/Services/Index.ts +2 -0
  47. package/Server/Services/IoTFleetService.ts +19 -79
  48. package/Server/Services/KubernetesClusterService.ts +17 -78
  49. package/Server/Services/LabelService.ts +57 -10
  50. package/Server/Services/MetricTypeService.ts +84 -0
  51. package/Server/Services/MonitorService.ts +89 -14
  52. package/Server/Services/MonitorStatusTimelineService.ts +29 -0
  53. package/Server/Services/MonitorTemplateService.ts +65 -0
  54. package/Server/Services/OpenTelemetryIngestService.ts +230 -26
  55. package/Server/Services/PodmanHostService.ts +19 -79
  56. package/Server/Services/ProxmoxClusterService.ts +23 -83
  57. package/Server/Services/RumApplicationService.ts +19 -80
  58. package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +21 -0
  59. package/Server/Services/ServerlessFunctionService.ts +24 -85
  60. package/Server/Services/ServiceService.ts +299 -73
  61. package/Server/Services/StatusPageMonitorRuleEngineService.ts +752 -0
  62. package/Server/Services/StatusPageMonitorRuleService.ts +471 -0
  63. package/Server/Services/TelemetryEntityRelationshipService.ts +1 -0
  64. package/Server/Services/TelemetryEntityService.ts +1 -0
  65. package/Server/Services/UserService.ts +184 -0
  66. package/Server/Utils/AI/Toolbox/CodeTools.ts +45 -4
  67. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +13 -0
  68. package/Server/Utils/CodeRepository/GitHub/GitHub.ts +157 -4
  69. package/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.ts +130 -0
  70. package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +170 -39
  71. package/Server/Utils/Monitor/MonitorResource.ts +63 -36
  72. package/Server/Utils/Monitor/MonitorStatusTimeline.ts +36 -0
  73. package/Server/Utils/Monitor/MonitorStepsProjectValidator.ts +148 -127
  74. package/Server/Utils/Monitor/MonitorStepsReferenceExtractor.ts +434 -0
  75. package/Server/Utils/PasswordHash.ts +306 -0
  76. package/Server/Utils/SingleFlight.ts +83 -0
  77. package/Server/Utils/StatusPage/MonitorRulePatternValidator.ts +46 -0
  78. package/Server/Utils/Telemetry/EntityRegistry.ts +107 -26
  79. package/Server/Utils/Telemetry/ResourceHeartbeat.ts +314 -0
  80. package/Server/Utils/Telemetry/Telemetry.ts +351 -122
  81. package/Tests/App/Dashboard/OverviewCustomFields.test.tsx +333 -0
  82. package/Tests/App/Dashboard/UserCustomFields.test.tsx +392 -0
  83. package/Tests/App/Dashboard/WidgetCatalog.test.ts +2 -0
  84. package/Tests/Models/DatabaseModels/ColumnTransformerInvariants.test.ts +306 -0
  85. package/Tests/Models/DatabaseModels/NumberColumnDeserialization.test.ts +333 -0
  86. package/Tests/Models/DatabaseModels/StatusPageMonitorRuleModel.test.ts +190 -0
  87. package/Tests/Server/API/AIAgentDataRepositoryToken.test.ts +381 -0
  88. package/Tests/Server/API/DashboardPublicAttributeValuesAPI.test.ts +943 -0
  89. package/Tests/Server/API/DashboardPublicMetricsAggregateAPI.test.ts +1296 -0
  90. package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +26 -0
  91. package/Tests/Server/API/DashboardPublicTemplatePayloads.test.ts +598 -0
  92. package/Tests/Server/API/GitHubAppInstallationBindingAPI.test.ts +539 -0
  93. package/Tests/Server/API/Helpers.ts +6 -1
  94. package/Tests/Server/API/UserProjectsAPI.test.ts +852 -0
  95. package/Tests/Server/Infrastructure/GlobalCache.test.ts +354 -0
  96. package/Tests/Server/Infrastructure/PostgresLockTimeoutOptions.test.ts +181 -0
  97. package/Tests/Server/Infrastructure/SemaphoreMutex.test.ts +215 -0
  98. package/Tests/Server/Services/AddPerUserPasswordSaltMigration.test.ts +178 -0
  99. package/Tests/Server/Services/AnalyticsDatabasePaginationStability.test.ts +582 -0
  100. package/Tests/Server/Services/AnalyticsDatabaseService.test.ts +5 -0
  101. package/Tests/Server/Services/CodeRepositoryInstallationBinding.test.ts +305 -0
  102. package/Tests/Server/Services/CodeRepositoryResolutionBinding.test.ts +147 -0
  103. package/Tests/Server/Services/DatabaseServicePerUserPasswordSalt.test.ts +870 -0
  104. package/Tests/Server/Services/DatabaseServiceUpdateColumnsIfUnlocked.test.ts +342 -0
  105. package/Tests/Server/Services/DropFilterSaveValidation.test.ts +245 -0
  106. package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +163 -15
  107. package/Tests/Server/Services/MetricTypeAttachServices.test.ts +222 -0
  108. package/Tests/Server/Services/MissingReferenceWriteGuard.test.ts +614 -0
  109. package/Tests/Server/Services/MonitorStatusTimelineFastPath.test.ts +23 -0
  110. package/Tests/Server/Services/OpenTelemetryServiceResolutionCache.test.ts +568 -0
  111. package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +305 -56
  112. package/Tests/Server/Services/RestoreDroppedUniqueIndexesMigration.test.ts +386 -0
  113. package/Tests/Server/Services/RestoreServiceLowerNameIndexMigration.test.ts +240 -0
  114. package/Tests/Server/Services/ServiceUpdateLastSeenSplitGates.test.ts +705 -0
  115. package/Tests/Server/Services/StatusPageMonitorRuleEngineService.test.ts +1719 -0
  116. package/Tests/Server/Services/StatusPageMonitorRuleHooks.test.ts +1212 -0
  117. package/Tests/Server/Services/StatusPageMonitorRuleMonitorCreateHook.test.ts +228 -0
  118. package/Tests/Server/Services/UserServiceFirstMasterAdminElection.test.ts +885 -0
  119. package/Tests/Server/Services/WidenHashedStringColumnsForScryptMigration.test.ts +220 -0
  120. package/Tests/Server/Utils/AI/CodeTools.test.ts +47 -1
  121. package/Tests/Server/Utils/AI/CodeWriteTools.test.ts +10 -0
  122. package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +267 -0
  123. package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +120 -0
  124. package/Tests/Server/Utils/CodeRepository/GitHubInstallationBinding.test.ts +292 -0
  125. package/Tests/Server/Utils/Database/ProjectScopedReferenceValidator.test.ts +260 -13
  126. package/Tests/Server/Utils/EntityRegistryRowFence.test.ts +302 -0
  127. package/Tests/Server/Utils/GitHubInstallationOwnershipVerification.test.ts +340 -0
  128. package/Tests/Server/Utils/GitHubWebhookAndTreeCacheIsolation.test.ts +250 -0
  129. package/Tests/Server/Utils/MetricTypeIndexConvergence.test.ts +598 -0
  130. package/Tests/Server/Utils/Monitor/MonitorStatusTimelineUnusableStatus.test.ts +154 -0
  131. package/Tests/Server/Utils/Monitor/MonitorStepsProjectValidator.test.ts +638 -92
  132. package/Tests/Server/Utils/Monitor/MonitorStepsReferenceExtractor.test.ts +724 -0
  133. package/Tests/Server/Utils/PasswordHash.test.ts +555 -0
  134. package/Tests/Server/Utils/SingleFlight.test.ts +221 -0
  135. package/Tests/Types/Dashboard/NetworkDashboardTemplate.test.ts +793 -0
  136. package/Tests/Types/Database/BigIntColumnTransformer.test.ts +147 -0
  137. package/Tests/Types/Database/ColumnLength.test.ts +7 -1
  138. package/Tests/Types/Database/DatabasePropertyTransformer.test.ts +140 -0
  139. package/Tests/Types/Database/NumericColumnValue.test.ts +127 -0
  140. package/Tests/Types/Decimal.test.ts +37 -0
  141. package/Tests/Types/HashedStringPerUserSalt.test.ts +476 -0
  142. package/Tests/Types/Monitor/DockerAlertTemplates.test.ts +293 -0
  143. package/Tests/Types/Monitor/HostAlertTemplates.test.ts +249 -0
  144. package/Tests/Types/Monitor/IotAlertTemplates.test.ts +320 -0
  145. package/Tests/Types/Monitor/PodmanAlertTemplates.test.ts +271 -0
  146. package/Tests/Types/Monitor/SnmpVersionParsing.test.ts +152 -0
  147. package/Tests/Types/Port.test.ts +38 -0
  148. package/Tests/UI/Components/CustomFields/CustomFieldsDetail.test.tsx +696 -0
  149. package/Tests/UI/Components/LogsHistogram.test.tsx +276 -0
  150. package/Tests/UI/Components/UseLiveLogsRefresh.test.tsx +489 -0
  151. package/Tests/UI/Components/UseLogsHistogram.test.tsx +500 -0
  152. package/Tests/UI/Utils/UserProjectsModelAPI.test.ts +714 -0
  153. package/Tests/Utils/Dashboard/Components/DashboardComponentsUtil.test.ts +120 -0
  154. package/Tests/Utils/Dashboard/DashboardNetworkMapComponent.test.ts +327 -0
  155. package/Tests/Utils/TeamMembersByProject.test.ts +655 -0
  156. package/Types/Dashboard/DashboardComponentType.ts +1 -0
  157. package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +1 -0
  158. package/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.ts +40 -0
  159. package/Types/Dashboard/DashboardTemplates.ts +397 -0
  160. package/Types/Database/BigIntColumnTransformer.ts +74 -0
  161. package/Types/Database/ColumnLength.ts +9 -1
  162. package/Types/Database/DatabaseProperty.ts +26 -0
  163. package/Types/Database/NumericColumnValue.ts +128 -0
  164. package/Types/Database/TableColumn.ts +11 -0
  165. package/Types/Database/UnsynchronizedIndex.ts +48 -0
  166. package/Types/Decimal.ts +16 -4
  167. package/Types/HashedString.ts +139 -4
  168. package/Types/Permission.ts +48 -0
  169. package/Types/Port.ts +16 -4
  170. package/UI/Components/CustomFields/CustomFieldsDetail.tsx +85 -11
  171. package/UI/Components/LogsViewer/useLiveLogsRefresh.ts +86 -0
  172. package/UI/Components/LogsViewer/useLogsHistogram.ts +113 -0
  173. package/UI/Components/Navbar/NavBar.tsx +12 -2
  174. package/UI/Components/Navbar/NavBarMenuModal.tsx +19 -7
  175. package/UI/Utils/ModelAPI/UserProjectsModelAPI.ts +257 -0
  176. package/Utils/Dashboard/Components/DashboardNetworkMapComponent.ts +136 -0
  177. package/Utils/Dashboard/Components/Index.ts +7 -0
  178. package/Utils/TeamMembersByProject.ts +237 -0
  179. package/build/dist/Models/DatabaseModels/CephCluster.js +16 -1
  180. package/build/dist/Models/DatabaseModels/CephCluster.js.map +1 -1
  181. package/build/dist/Models/DatabaseModels/CodeRepository.js +14 -6
  182. package/build/dist/Models/DatabaseModels/CodeRepository.js.map +1 -1
  183. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js +12 -0
  184. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js.map +1 -1
  185. package/build/dist/Models/DatabaseModels/DockerHost.js +16 -1
  186. package/build/dist/Models/DatabaseModels/DockerHost.js.map +1 -1
  187. package/build/dist/Models/DatabaseModels/GlobalOidcProject.js +16 -1
  188. package/build/dist/Models/DatabaseModels/GlobalOidcProject.js.map +1 -1
  189. package/build/dist/Models/DatabaseModels/GlobalSsoProject.js +16 -1
  190. package/build/dist/Models/DatabaseModels/GlobalSsoProject.js.map +1 -1
  191. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  192. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  193. package/build/dist/Models/DatabaseModels/IoTFleet.js +16 -1
  194. package/build/dist/Models/DatabaseModels/IoTFleet.js.map +1 -1
  195. package/build/dist/Models/DatabaseModels/KubernetesCluster.js +16 -1
  196. package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
  197. package/build/dist/Models/DatabaseModels/LogDropFilter.js +9 -20
  198. package/build/dist/Models/DatabaseModels/LogDropFilter.js.map +1 -1
  199. package/build/dist/Models/DatabaseModels/NetworkDevice.js +16 -1
  200. package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
  201. package/build/dist/Models/DatabaseModels/NetworkInterface.js +16 -1
  202. package/build/dist/Models/DatabaseModels/NetworkInterface.js.map +1 -1
  203. package/build/dist/Models/DatabaseModels/Project.js +16 -2
  204. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  205. package/build/dist/Models/DatabaseModels/ProxmoxCluster.js +16 -1
  206. package/build/dist/Models/DatabaseModels/ProxmoxCluster.js.map +1 -1
  207. package/build/dist/Models/DatabaseModels/Service.js +28 -1
  208. package/build/dist/Models/DatabaseModels/Service.js.map +1 -1
  209. package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js +925 -0
  210. package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js.map +1 -0
  211. package/build/dist/Models/DatabaseModels/StatusPagePrivateUser.js +32 -0
  212. package/build/dist/Models/DatabaseModels/StatusPagePrivateUser.js.map +1 -1
  213. package/build/dist/Models/DatabaseModels/StatusPageResource.js +72 -0
  214. package/build/dist/Models/DatabaseModels/StatusPageResource.js.map +1 -1
  215. package/build/dist/Models/DatabaseModels/TraceDropFilter.js +9 -20
  216. package/build/dist/Models/DatabaseModels/TraceDropFilter.js.map +1 -1
  217. package/build/dist/Models/DatabaseModels/User.js +32 -0
  218. package/build/dist/Models/DatabaseModels/User.js.map +1 -1
  219. package/build/dist/Server/API/AIAgentDataAPI.js +21 -2
  220. package/build/dist/Server/API/AIAgentDataAPI.js.map +1 -1
  221. package/build/dist/Server/API/BaseAPI.js +11 -1
  222. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  223. package/build/dist/Server/API/DashboardAPI.js +343 -10
  224. package/build/dist/Server/API/DashboardAPI.js.map +1 -1
  225. package/build/dist/Server/API/GitHubAPI.js +94 -167
  226. package/build/dist/Server/API/GitHubAPI.js.map +1 -1
  227. package/build/dist/Server/API/UserAPI.js +187 -2
  228. package/build/dist/Server/API/UserAPI.js.map +1 -1
  229. package/build/dist/Server/EnvironmentConfig.js +34 -4
  230. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  231. package/build/dist/Server/Infrastructure/GlobalCache.js +120 -0
  232. package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
  233. package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js +5 -3
  234. package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js.map +1 -1
  235. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.js +48 -0
  236. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.js.map +1 -0
  237. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.js +14 -0
  238. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.js.map +1 -0
  239. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.js +59 -0
  240. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.js.map +1 -0
  241. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.js +93 -0
  242. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.js.map +1 -0
  243. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.js +101 -0
  244. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.js.map +1 -0
  245. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.js +66 -0
  246. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.js.map +1 -0
  247. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +12 -0
  248. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  249. package/build/dist/Server/Services/AlertStateTimelineService.js +19 -0
  250. package/build/dist/Server/Services/AlertStateTimelineService.js.map +1 -1
  251. package/build/dist/Server/Services/AnalyticsDatabaseService.js +57 -1
  252. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  253. package/build/dist/Server/Services/CephClusterService.js +26 -76
  254. package/build/dist/Server/Services/CephClusterService.js.map +1 -1
  255. package/build/dist/Server/Services/CloudResourceService.js +23 -73
  256. package/build/dist/Server/Services/CloudResourceService.js.map +1 -1
  257. package/build/dist/Server/Services/CodeRepositoryService.js +90 -2
  258. package/build/dist/Server/Services/CodeRepositoryService.js.map +1 -1
  259. package/build/dist/Server/Services/DatabaseService.js +246 -2
  260. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  261. package/build/dist/Server/Services/DockerHostService.js +19 -69
  262. package/build/dist/Server/Services/DockerHostService.js.map +1 -1
  263. package/build/dist/Server/Services/DockerSwarmClusterService.js +27 -78
  264. package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
  265. package/build/dist/Server/Services/HostService.js +58 -102
  266. package/build/dist/Server/Services/HostService.js.map +1 -1
  267. package/build/dist/Server/Services/IncidentStateTimelineService.js +19 -0
  268. package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
  269. package/build/dist/Server/Services/Index.js +2 -0
  270. package/build/dist/Server/Services/Index.js.map +1 -1
  271. package/build/dist/Server/Services/IoTFleetService.js +19 -69
  272. package/build/dist/Server/Services/IoTFleetService.js.map +1 -1
  273. package/build/dist/Server/Services/KubernetesClusterService.js +17 -68
  274. package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
  275. package/build/dist/Server/Services/LabelService.js +42 -7
  276. package/build/dist/Server/Services/LabelService.js.map +1 -1
  277. package/build/dist/Server/Services/MetricTypeService.js +74 -0
  278. package/build/dist/Server/Services/MetricTypeService.js.map +1 -1
  279. package/build/dist/Server/Services/MonitorService.js +81 -9
  280. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  281. package/build/dist/Server/Services/MonitorStatusTimelineService.js +28 -0
  282. package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
  283. package/build/dist/Server/Services/MonitorTemplateService.js +60 -0
  284. package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
  285. package/build/dist/Server/Services/OpenTelemetryIngestService.js +155 -25
  286. package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
  287. package/build/dist/Server/Services/PodmanHostService.js +19 -69
  288. package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
  289. package/build/dist/Server/Services/ProxmoxClusterService.js +23 -73
  290. package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
  291. package/build/dist/Server/Services/RumApplicationService.js +19 -70
  292. package/build/dist/Server/Services/RumApplicationService.js.map +1 -1
  293. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +20 -0
  294. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
  295. package/build/dist/Server/Services/ServerlessFunctionService.js +24 -75
  296. package/build/dist/Server/Services/ServerlessFunctionService.js.map +1 -1
  297. package/build/dist/Server/Services/ServiceService.js +236 -61
  298. package/build/dist/Server/Services/ServiceService.js.map +1 -1
  299. package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js +603 -0
  300. package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js.map +1 -0
  301. package/build/dist/Server/Services/StatusPageMonitorRuleService.js +401 -0
  302. package/build/dist/Server/Services/StatusPageMonitorRuleService.js.map +1 -0
  303. package/build/dist/Server/Services/TelemetryEntityRelationshipService.js +1 -0
  304. package/build/dist/Server/Services/TelemetryEntityRelationshipService.js.map +1 -1
  305. package/build/dist/Server/Services/TelemetryEntityService.js +1 -0
  306. package/build/dist/Server/Services/TelemetryEntityService.js.map +1 -1
  307. package/build/dist/Server/Services/UserService.js +165 -0
  308. package/build/dist/Server/Services/UserService.js.map +1 -1
  309. package/build/dist/Server/Utils/AI/Toolbox/CodeTools.js +31 -1
  310. package/build/dist/Server/Utils/AI/Toolbox/CodeTools.js.map +1 -1
  311. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +13 -0
  312. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
  313. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js +126 -4
  314. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js.map +1 -1
  315. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.js +136 -0
  316. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.js.map +1 -0
  317. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +105 -29
  318. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
  319. package/build/dist/Server/Utils/Monitor/MonitorResource.js +56 -32
  320. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  321. package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js +30 -3
  322. package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js.map +1 -1
  323. package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js +110 -98
  324. package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js.map +1 -1
  325. package/build/dist/Server/Utils/Monitor/MonitorStepsReferenceExtractor.js +312 -0
  326. package/build/dist/Server/Utils/Monitor/MonitorStepsReferenceExtractor.js.map +1 -0
  327. package/build/dist/Server/Utils/PasswordHash.js +237 -0
  328. package/build/dist/Server/Utils/PasswordHash.js.map +1 -0
  329. package/build/dist/Server/Utils/SingleFlight.js +76 -0
  330. package/build/dist/Server/Utils/SingleFlight.js.map +1 -0
  331. package/build/dist/Server/Utils/StatusPage/MonitorRulePatternValidator.js +29 -0
  332. package/build/dist/Server/Utils/StatusPage/MonitorRulePatternValidator.js.map +1 -0
  333. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +78 -19
  334. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
  335. package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js +160 -0
  336. package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js.map +1 -0
  337. package/build/dist/Server/Utils/Telemetry/Telemetry.js +279 -104
  338. package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
  339. package/build/dist/Types/Dashboard/DashboardComponentType.js +1 -0
  340. package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
  341. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +1 -0
  342. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
  343. package/build/dist/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.js +2 -0
  344. package/build/dist/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.js.map +1 -0
  345. package/build/dist/Types/Dashboard/DashboardTemplates.js +368 -0
  346. package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
  347. package/build/dist/Types/Database/BigIntColumnTransformer.js +66 -0
  348. package/build/dist/Types/Database/BigIntColumnTransformer.js.map +1 -0
  349. package/build/dist/Types/Database/ColumnLength.js +9 -1
  350. package/build/dist/Types/Database/ColumnLength.js.map +1 -1
  351. package/build/dist/Types/Database/DatabaseProperty.js +25 -0
  352. package/build/dist/Types/Database/DatabaseProperty.js.map +1 -1
  353. package/build/dist/Types/Database/NumericColumnValue.js +97 -0
  354. package/build/dist/Types/Database/NumericColumnValue.js.map +1 -0
  355. package/build/dist/Types/Database/TableColumn.js.map +1 -1
  356. package/build/dist/Types/Database/UnsynchronizedIndex.js +46 -0
  357. package/build/dist/Types/Database/UnsynchronizedIndex.js.map +1 -0
  358. package/build/dist/Types/Decimal.js +14 -3
  359. package/build/dist/Types/Decimal.js.map +1 -1
  360. package/build/dist/Types/HashedString.js +95 -5
  361. package/build/dist/Types/HashedString.js.map +1 -1
  362. package/build/dist/Types/Permission.js +42 -0
  363. package/build/dist/Types/Permission.js.map +1 -1
  364. package/build/dist/Types/Port.js +15 -5
  365. package/build/dist/Types/Port.js.map +1 -1
  366. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +51 -10
  367. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
  368. package/build/dist/UI/Components/LogsViewer/useLiveLogsRefresh.js +44 -0
  369. package/build/dist/UI/Components/LogsViewer/useLiveLogsRefresh.js.map +1 -0
  370. package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js +75 -0
  371. package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js.map +1 -0
  372. package/build/dist/UI/Components/Navbar/NavBar.js +3 -1
  373. package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
  374. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +19 -7
  375. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
  376. package/build/dist/UI/Utils/ModelAPI/UserProjectsModelAPI.js +156 -0
  377. package/build/dist/UI/Utils/ModelAPI/UserProjectsModelAPI.js.map +1 -0
  378. package/build/dist/Utils/Dashboard/Components/DashboardNetworkMapComponent.js +114 -0
  379. package/build/dist/Utils/Dashboard/Components/DashboardNetworkMapComponent.js.map +1 -0
  380. package/build/dist/Utils/Dashboard/Components/Index.js +4 -0
  381. package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
  382. package/build/dist/Utils/TeamMembersByProject.js +145 -0
  383. package/build/dist/Utils/TeamMembersByProject.js.map +1 -0
  384. package/package.json +1 -1
@@ -0,0 +1,147 @@
1
+ import { getBigIntDatabaseTransformer } from "../../../Types/Database/BigIntColumnTransformer";
2
+ import { ValueTransformer } from "typeorm/decorator/options/ValueTransformer";
3
+ import { describe, expect, it } from "@jest/globals";
4
+
5
+ /*
6
+ * Contract under test — the Postgres bigint round-trip used by counter
7
+ * columns.
8
+ *
9
+ * The interesting half is `to(undefined)`. TypeORM applies a column
10
+ * transformer BEFORE it decides whether the column was supplied at all:
11
+ *
12
+ * InsertQueryBuilder.createColumnValueExpression
13
+ * -> driver.preparePersistentValue (runs transformer.to)
14
+ * -> if (value === undefined) expression += "DEFAULT"
15
+ *
16
+ * so a transformer that answers `null` for `undefined` has already turned
17
+ * "the caller did not set this column" into "the caller set it to NULL" by
18
+ * the time that check runs, and the column DEFAULT is unreachable. On the
19
+ * drop-filter `droppedCount` columns — NOT NULL DEFAULT 0, written only by
20
+ * the ingest path — that made every single insert fail:
21
+ *
22
+ * null value in column "droppedCount" of relation "LogDropFilter"
23
+ * violates not-null constraint
24
+ *
25
+ * i.e. creating any log drop filter in the dashboard returned HTTP 500.
26
+ * github.com/OneUptime/oneuptime/issues/3026
27
+ */
28
+
29
+ const transformer: ValueTransformer = getBigIntDatabaseTransformer();
30
+
31
+ describe("getBigIntDatabaseTransformer().to", () => {
32
+ /*
33
+ * The regression. `undefined` and `null` are NOT interchangeable here:
34
+ * one means "use the column default", the other means "write NULL".
35
+ */
36
+ it("passes undefined through so the column DEFAULT applies", () => {
37
+ expect(transformer.to(undefined)).toBeUndefined();
38
+ });
39
+
40
+ it("does not confuse undefined with null", () => {
41
+ expect(transformer.to(undefined)).not.toBeNull();
42
+ });
43
+
44
+ it("keeps an explicit null as null", () => {
45
+ expect(transformer.to(null)).toBeNull();
46
+ });
47
+
48
+ it("stringifies a number, because the pg driver binds bigint as text", () => {
49
+ expect(transformer.to(0)).toBe("0");
50
+ expect(transformer.to(1)).toBe("1");
51
+ expect(transformer.to(42)).toBe("42");
52
+ });
53
+
54
+ it("truncates rather than emitting a decimal Postgres would reject", () => {
55
+ expect(transformer.to(10.9)).toBe("10");
56
+ expect(transformer.to(-10.9)).toBe("-10");
57
+ });
58
+
59
+ /*
60
+ * The reason these columns are bigint at all: a sample filter on a
61
+ * high-volume project passes 2^31 well within a year, and a silently
62
+ * wrapped diagnostic counter is worse than no counter.
63
+ */
64
+ it("carries a value past the 32-bit range without losing precision", () => {
65
+ expect(transformer.to(4_294_967_296)).toBe("4294967296");
66
+ expect(transformer.to(Number.MAX_SAFE_INTEGER)).toBe("9007199254740991");
67
+ });
68
+
69
+ /*
70
+ * `Math.trunc(NaN).toString()` is the string "NaN", which Postgres
71
+ * rejects with a syntax error naming neither the column nor the caller.
72
+ */
73
+ it("refuses to emit a non-finite value as text", () => {
74
+ expect(transformer.to(NaN)).toBeNull();
75
+ expect(transformer.to(Infinity)).toBeNull();
76
+ expect(transformer.to(-Infinity)).toBeNull();
77
+ });
78
+ });
79
+
80
+ describe("getBigIntDatabaseTransformer().from", () => {
81
+ /*
82
+ * node-postgres hands bigint back as a string — it does not fit a JS
83
+ * number in the general case. Every value stored in these counters is
84
+ * inside MAX_SAFE_INTEGER, and the dashboard renders them as numbers.
85
+ */
86
+ it("parses the string the driver returns into a number", () => {
87
+ expect(transformer.from("0")).toBe(0);
88
+ expect(transformer.from("42")).toBe(42);
89
+ expect(transformer.from("9007199254740991")).toBe(9007199254740991);
90
+ });
91
+
92
+ it("accepts a number unchanged, in case a driver already parsed it", () => {
93
+ expect(transformer.from(7)).toBe(7);
94
+ });
95
+
96
+ it("maps a null column to null rather than 0", () => {
97
+ expect(transformer.from(null)).toBeNull();
98
+ expect(transformer.from(undefined)).toBeNull();
99
+ });
100
+
101
+ it("maps unparsable text to null rather than NaN", () => {
102
+ expect(transformer.from("not a number")).toBeNull();
103
+ expect(transformer.from("")).toBeNull();
104
+ });
105
+
106
+ it("round-trips a counter value", () => {
107
+ for (const value of [0, 1, 999, 4_294_967_296]) {
108
+ expect(transformer.from(transformer.to(value))).toBe(value);
109
+ }
110
+ });
111
+ });
112
+
113
+ /*
114
+ * A miniature of TypeORM's own decision, so the consequence of the
115
+ * transformer's answer is asserted rather than left to a comment. Mirrors
116
+ * PostgresDriver.preparePersistentValue followed by
117
+ * InsertQueryBuilder.createColumnValueExpression.
118
+ */
119
+ describe("the INSERT expression TypeORM derives from this transformer", () => {
120
+ type InsertExpression = "DEFAULT" | "NULL" | string;
121
+
122
+ function insertExpressionFor(value: number | null | undefined): string {
123
+ const prepared: unknown = transformer.to(value);
124
+
125
+ if (prepared === undefined) {
126
+ return "DEFAULT";
127
+ }
128
+
129
+ if (prepared === null) {
130
+ return "NULL";
131
+ }
132
+
133
+ return String(prepared);
134
+ }
135
+
136
+ it("emits DEFAULT for a counter the caller never set", () => {
137
+ const expression: InsertExpression = insertExpressionFor(undefined);
138
+
139
+ expect(expression).toBe("DEFAULT");
140
+ // The old inline transformer produced this, and NOT NULL rejected it.
141
+ expect(expression).not.toBe("NULL");
142
+ });
143
+
144
+ it("emits the value for a counter the caller did set", () => {
145
+ expect(insertExpressionFor(5)).toBe("5");
146
+ });
147
+ });
@@ -42,7 +42,13 @@ describe("enum ColumnLength", () => {
42
42
  });
43
43
 
44
44
  test("ColumnLength.HashedString", () => {
45
- expect(ColumnLength.HashedString).toEqual(64);
45
+ /*
46
+ * Not 64 (a bare SHA-256 digest) since user passwords became scrypt
47
+ * hashes that carry their own cost parameters: `scrypt$N=...,r=...,p=...$
48
+ * <64 hex>`, around 90 characters. Narrowing this back would truncate
49
+ * every password on write and lock the whole instance out.
50
+ */
51
+ expect(ColumnLength.HashedString).toEqual(255);
46
52
  });
47
53
 
48
54
  test("ColumnLength.Phone", () => {
@@ -0,0 +1,140 @@
1
+ import Recurring from "../../../Types/Events/Recurring";
2
+ import RestrictionTimes from "../../../Types/OnCallDutyPolicy/RestrictionTimes";
3
+ import ObjectID from "../../../Types/ObjectID";
4
+ import Email from "../../../Types/Email";
5
+ import Color from "../../../Types/Color";
6
+ import EventInterval from "../../../Types/Events/EventInterval";
7
+ import PositiveNumber from "../../../Types/PositiveNumber";
8
+ import { ValueTransformer } from "typeorm/decorator/options/ValueTransformer";
9
+ import { describe, expect, it } from "@jest/globals";
10
+
11
+ /*
12
+ * Contract under test — `DatabaseProperty.getDatabaseTransformer()`, the one
13
+ * transformer behind nearly every non-primitive column in the schema.
14
+ *
15
+ * Its `to` has to keep `undefined` and `null` apart. TypeORM applies the
16
+ * transformer BEFORE deciding whether the caller supplied the column:
17
+ *
18
+ * InsertQueryBuilder.createColumnValueExpression
19
+ * -> driver.preparePersistentValue (runs transformer.to)
20
+ * -> if (value === undefined) expression += "DEFAULT"
21
+ *
22
+ * Every `toDatabase` implementation returns null for a falsy input, so
23
+ * before this guard an omitted column arrived as an explicit NULL and the
24
+ * column's DEFAULT was unreachable. On a NOT NULL column with a default that
25
+ * is a not-null constraint violation — an HTTP 500 — on every create that
26
+ * does not mention the column. That is the mechanism behind
27
+ * github.com/OneUptime/oneuptime/issues/3026 (the drop-filter counters), and
28
+ * `OnCallDutyPolicyScheduleLayer.rotation` / `.restrictionTimes` are the same
29
+ * shape: NOT NULL, with a JSON default, written through this transformer.
30
+ *
31
+ * UPDATE is unaffected in both directions — UpdateQueryBuilder drops
32
+ * undefined properties before the transformer runs — so this only ever
33
+ * changes an INSERT from "write NULL" to "use the DEFAULT".
34
+ */
35
+
36
+ interface TransformerCase {
37
+ name: string;
38
+ transformer: ValueTransformer;
39
+ sample: unknown;
40
+ }
41
+
42
+ const CASES: Array<TransformerCase> = [
43
+ {
44
+ name: "Recurring",
45
+ transformer: Recurring.getDatabaseTransformer(),
46
+ sample: Recurring.getDefault(),
47
+ },
48
+ {
49
+ name: "RestrictionTimes",
50
+ transformer: RestrictionTimes.getDatabaseTransformer(),
51
+ sample: RestrictionTimes.getDefault(),
52
+ },
53
+ {
54
+ name: "ObjectID",
55
+ transformer: ObjectID.getDatabaseTransformer(),
56
+ sample: new ObjectID("11111111-1111-4111-8111-111111111111"),
57
+ },
58
+ {
59
+ name: "Email",
60
+ transformer: Email.getDatabaseTransformer(),
61
+ sample: new Email("someone@oneuptime.com"),
62
+ },
63
+ {
64
+ name: "Color",
65
+ transformer: Color.getDatabaseTransformer(),
66
+ sample: new Color("#ff0000"),
67
+ },
68
+ ];
69
+
70
+ describe.each(CASES)(
71
+ "$name.getDatabaseTransformer()",
72
+ ({ transformer, sample }: TransformerCase) => {
73
+ it("passes undefined through so the column DEFAULT applies", () => {
74
+ expect(transformer.to(undefined)).toBeUndefined();
75
+ });
76
+
77
+ it("does not collapse undefined into null", () => {
78
+ expect(transformer.to(undefined)).not.toBeNull();
79
+ });
80
+
81
+ /*
82
+ * An explicit null is a different instruction — "store nothing here" —
83
+ * and still has to reach the column as NULL.
84
+ */
85
+ it("keeps an explicit null as null", () => {
86
+ expect(transformer.to(null)).toBeNull();
87
+ });
88
+
89
+ it("still serializes a real value", () => {
90
+ expect(transformer.to(sample)).not.toBeUndefined();
91
+ expect(transformer.to(sample)).not.toBeNull();
92
+ });
93
+ },
94
+ );
95
+
96
+ describe("the INSERT expression TypeORM derives for a NOT NULL column", () => {
97
+ /*
98
+ * A miniature of PostgresDriver.preparePersistentValue followed by
99
+ * InsertQueryBuilder.createColumnValueExpression, so the consequence is
100
+ * asserted rather than described.
101
+ */
102
+ function insertExpressionFor(
103
+ transformer: ValueTransformer,
104
+ value: unknown,
105
+ ): string {
106
+ const prepared: unknown = transformer.to(value);
107
+
108
+ if (prepared === undefined) {
109
+ return "DEFAULT";
110
+ }
111
+
112
+ if (prepared === null) {
113
+ return "NULL";
114
+ }
115
+
116
+ return "?";
117
+ }
118
+
119
+ it("uses the schedule layer's rotation default instead of NULL", () => {
120
+ expect(
121
+ insertExpressionFor(Recurring.getDatabaseTransformer(), undefined),
122
+ ).toBe("DEFAULT");
123
+ });
124
+
125
+ it("uses the schedule layer's restriction default instead of NULL", () => {
126
+ expect(
127
+ insertExpressionFor(RestrictionTimes.getDatabaseTransformer(), undefined),
128
+ ).toBe("DEFAULT");
129
+ });
130
+
131
+ it("still binds a supplied value", () => {
132
+ const rotation: Recurring = new Recurring();
133
+ rotation.intervalType = EventInterval.Week;
134
+ rotation.intervalCount = new PositiveNumber(2);
135
+
136
+ expect(
137
+ insertExpressionFor(Recurring.getDatabaseTransformer(), rotation),
138
+ ).toBe("?");
139
+ });
140
+ });
@@ -0,0 +1,127 @@
1
+ import {
2
+ coerceNumericColumnValue,
3
+ isNumericTableColumnType,
4
+ } from "../../../Types/Database/NumericColumnValue";
5
+ import TableColumnType from "../../../Types/Database/TableColumnType";
6
+ import { describe, expect, it } from "@jest/globals";
7
+
8
+ /*
9
+ * Contract under test — normalizing a JSON value bound for a number column.
10
+ *
11
+ * HTML has no numeric input event: `<input type="number">` hands back
12
+ * `e.target.value`, a string. So the dashboard posted the JSON string "10"
13
+ * for every number field, and Postgres quietly coerced '10' to 10 on the way
14
+ * in — which is why nobody noticed until a server-side check read the value
15
+ * BEFORE it was stored:
16
+ *
17
+ * BadDataException: Sample percentage is required when the action is
18
+ * "Sample". Enter the percentage of matching logs to keep, between 1 and 99.
19
+ *
20
+ * on a form where the user had typed a percentage. HTTP 400 on a valid
21
+ * submission. github.com/OneUptime/oneuptime/issues/3027
22
+ */
23
+
24
+ describe("isNumericTableColumnType", () => {
25
+ it("covers every column type stored as a plain JS number", () => {
26
+ for (const type of [
27
+ TableColumnType.Number,
28
+ TableColumnType.SmallNumber,
29
+ TableColumnType.BigNumber,
30
+ TableColumnType.PositiveNumber,
31
+ TableColumnType.SmallPositiveNumber,
32
+ TableColumnType.BigPositiveNumber,
33
+ ]) {
34
+ expect(isNumericTableColumnType(type)).toBe(true);
35
+ }
36
+ });
37
+
38
+ /*
39
+ * The types that would be destroyed by coercion. `Port` and `Version` are
40
+ * wrapped objects and `Date` is a Date, however numeric they look.
41
+ */
42
+ it("excludes types that only look numeric", () => {
43
+ for (const type of [
44
+ TableColumnType.Port,
45
+ TableColumnType.Version,
46
+ TableColumnType.Date,
47
+ TableColumnType.ShortText,
48
+ TableColumnType.LongText,
49
+ TableColumnType.Boolean,
50
+ TableColumnType.ObjectID,
51
+ TableColumnType.Entity,
52
+ TableColumnType.EntityArray,
53
+ TableColumnType.JSON,
54
+ ]) {
55
+ expect(isNumericTableColumnType(type)).toBe(false);
56
+ }
57
+ });
58
+
59
+ it("treats a missing type as not numeric", () => {
60
+ expect(isNumericTableColumnType(undefined)).toBe(false);
61
+ expect(isNumericTableColumnType(null)).toBe(false);
62
+ });
63
+ });
64
+
65
+ describe("coerceNumericColumnValue", () => {
66
+ it("converts the string an <input type=number> produces", () => {
67
+ expect(coerceNumericColumnValue("10")).toBe(10);
68
+ expect(coerceNumericColumnValue("1")).toBe(1);
69
+ expect(coerceNumericColumnValue("99")).toBe(99);
70
+ });
71
+
72
+ it("handles the shapes a browser can put in that string", () => {
73
+ expect(coerceNumericColumnValue("0")).toBe(0);
74
+ expect(coerceNumericColumnValue("-5")).toBe(-5);
75
+ expect(coerceNumericColumnValue("2.5")).toBe(2.5);
76
+ expect(coerceNumericColumnValue(" 42 ")).toBe(42);
77
+ expect(coerceNumericColumnValue("1e3")).toBe(1000);
78
+ });
79
+
80
+ it("leaves a value that is already a number alone", () => {
81
+ expect(coerceNumericColumnValue(10)).toBe(10);
82
+ expect(coerceNumericColumnValue(0)).toBe(0);
83
+ });
84
+
85
+ it("leaves null and undefined alone", () => {
86
+ expect(coerceNumericColumnValue(null)).toBeNull();
87
+ expect(coerceNumericColumnValue(undefined)).toBeUndefined();
88
+ });
89
+
90
+ /*
91
+ * `Number("")` is 0. Writing a real 0 for a field the user cleared would
92
+ * be a silent data change, and for a sample percentage specifically, 0 is
93
+ * the value that used to mean "throw away half". Leave it untouched and
94
+ * let the field's own validation speak.
95
+ */
96
+ it("does not turn a cleared field into zero", () => {
97
+ expect(coerceNumericColumnValue("")).toBe("");
98
+ expect(coerceNumericColumnValue(" ")).toBe(" ");
99
+ });
100
+
101
+ it("leaves unparsable text alone so validation can reject it by name", () => {
102
+ for (const garbage of ["abc", "10abc", "NaN", "Infinity", "--1", "1,000"]) {
103
+ expect(coerceNumericColumnValue(garbage)).toBe(garbage);
104
+ }
105
+ });
106
+
107
+ it("never invents a non-finite number", () => {
108
+ const results: Array<unknown> = ["Infinity", "-Infinity", "NaN"].map(
109
+ (value: string) => {
110
+ return coerceNumericColumnValue(value);
111
+ },
112
+ );
113
+
114
+ for (const result of results) {
115
+ expect(typeof result).toBe("string");
116
+ }
117
+ });
118
+
119
+ it("passes non-string, non-number values through untouched", () => {
120
+ const object: Record<string, string> = { a: "1" };
121
+ const array: Array<number> = [1];
122
+
123
+ expect(coerceNumericColumnValue(object)).toBe(object);
124
+ expect(coerceNumericColumnValue(array)).toBe(array);
125
+ expect(coerceNumericColumnValue(true)).toBe(true);
126
+ });
127
+ });
@@ -89,4 +89,41 @@ describe("Decimal", () => {
89
89
  expect(restored.equals(original)).toBe(true);
90
90
  });
91
91
  });
92
+
93
+ /*
94
+ * A decimal column is declared `TableColumnType.Number`, so whatever
95
+ * reaches the transformer may be a Decimal, a string, or a plain number
96
+ * depending on how far the request body got through deserialization.
97
+ */
98
+ describe("getDatabaseTransformer().to", () => {
99
+ const transformer: ReturnType<typeof Decimal.getDatabaseTransformer> =
100
+ Decimal.getDatabaseTransformer();
101
+
102
+ test("stores a Decimal instance as its string form", () => {
103
+ expect(transformer.to(new Decimal(125.5))).toBe("125.5");
104
+ });
105
+
106
+ test("stores a raw string", () => {
107
+ expect(transformer.to("125.5")).toBe("125.5");
108
+ });
109
+
110
+ test("stores a raw number", () => {
111
+ expect(transformer.to(125.5)).toBe("125.5");
112
+ });
113
+
114
+ /*
115
+ * `0` is a perfectly good decimal, and both of its columns are NOT NULL.
116
+ * A truthiness check used to send it to null, which the column rejects.
117
+ */
118
+ test("stores zero rather than turning it into null", () => {
119
+ expect(transformer.to(0)).toBe("0");
120
+ expect(transformer.to("0")).toBe("0");
121
+ expect(transformer.to(new Decimal(0))).toBe("0");
122
+ });
123
+
124
+ test("keeps undefined distinct from null so a column DEFAULT applies", () => {
125
+ expect(transformer.to(undefined)).toBeUndefined();
126
+ expect(transformer.to(null)).toBeNull();
127
+ });
128
+ });
92
129
  });