@oneuptime/common 11.5.12 → 11.6.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 (193) hide show
  1. package/Models/DatabaseModels/AIInsight.ts +30 -0
  2. package/Models/DatabaseModels/EnterpriseLicense.ts +21 -0
  3. package/Models/DatabaseModels/EnterpriseLicenseInstance.ts +25 -0
  4. package/Models/DatabaseModels/GlobalConfig.ts +76 -0
  5. package/Models/DatabaseModels/Project.ts +30 -0
  6. package/Models/DatabaseModels/TelemetryException.ts +128 -0
  7. package/Server/API/AIAgentDataAPI.ts +129 -5
  8. package/Server/API/AIInvestigationAPI.ts +87 -0
  9. package/Server/API/EnterpriseLicenseAPI.ts +57 -0
  10. package/Server/API/GlobalConfigAPI.ts +62 -1
  11. package/Server/EnvironmentConfig.ts +22 -0
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/1784640000000-AddExceptionTriageAndPrivacyColumns.ts +56 -0
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/1784659816363-AddInstanceVersionAndLatestReleaseColumns.ts +38 -0
  14. package/Server/Infrastructure/Postgres/SchemaMigrations/1784705487674-AddEnterpriseLicenseEvaluationColumns.ts +25 -0
  15. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  16. package/Server/Services/AIRunService.ts +8 -0
  17. package/Server/Services/AnalyticsDatabaseService.ts +48 -45
  18. package/Server/Services/DatabaseService.ts +66 -1
  19. package/Server/Services/TelemetryExceptionService.ts +320 -24
  20. package/Server/Services/WorkflowService.ts +100 -39
  21. package/Server/Types/Workflow/Components/Schedule.ts +57 -32
  22. package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.ts +15 -4
  23. package/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.ts +15 -4
  24. package/Server/Utils/AI/SRE/Insights/InsightScanner.ts +222 -24
  25. package/Server/Utils/AI/SRE/Insights/InsightTriageRunner.ts +225 -9
  26. package/Server/Utils/AI/SRE/SubjectCodeFixRun.ts +39 -0
  27. package/Server/Utils/AI/SRE/TelemetryImprovementTaskTrigger.ts +114 -0
  28. package/Server/Utils/AnalyticsDatabase/InsertDedupContext.ts +59 -0
  29. package/Server/Utils/Express.ts +13 -0
  30. package/Server/Utils/Telemetry/ExceptionSanitizer.ts +222 -0
  31. package/Server/Utils/Telemetry/TelemetryFanInWriter.ts +799 -0
  32. package/Server/Utils/Telemetry/TelemetryWriterClient.ts +263 -0
  33. package/Server/Utils/Telemetry/TelemetryWriterServer.ts +240 -0
  34. package/Server/Utils/Telemetry/TelemetryWriterShedMetrics.ts +93 -0
  35. package/Tests/Server/Services/DatabaseServiceSanitizeUpdateData.test.ts +232 -0
  36. package/Tests/Server/Services/TelemetryExceptionCodeFixRun.test.ts +205 -1
  37. package/Tests/Server/Services/UpdateOneByIdKeepsRowId.test.ts +107 -0
  38. package/Tests/Server/Services/WorkflowService.test.ts +308 -0
  39. package/Tests/Server/Utils/AI/Insights/InsightScanner.test.ts +150 -141
  40. package/Tests/Server/Utils/AI/Insights/InsightTriageRunner.test.ts +277 -1
  41. package/Tests/Server/Utils/Attribution.test.ts +203 -0
  42. package/Tests/Server/Utils/ExceptionSanitizer.test.ts +70 -0
  43. package/Tests/Server/Utils/InsightTriageClassification.test.ts +57 -0
  44. package/Tests/Server/Utils/Marketing/ConversionUploadProvider.test.ts +183 -0
  45. package/Tests/Server/Utils/Marketing/GoogleAds.test.ts +340 -0
  46. package/Tests/Server/Utils/Monitor/Criteria/ExceptionMonitorCriteria.test.ts +87 -0
  47. package/Tests/Server/Utils/Monitor/Criteria/LogMonitorCriteria.test.ts +165 -0
  48. package/Tests/Server/Utils/Monitor/Criteria/TraceMonitorCriteria.test.ts +86 -0
  49. package/Tests/Server/Utils/Telemetry/TelemetryFanInWriter.test.ts +1565 -0
  50. package/Tests/Server/Utils/Telemetry/TelemetryWriterClient.test.ts +313 -0
  51. package/Tests/Server/Utils/Telemetry/TelemetryWriterServer.test.ts +270 -0
  52. package/Tests/Server/Utils/Telemetry/TelemetryWriterShedMetrics.test.ts +127 -0
  53. package/Tests/Server/Utils/TelemetryImprovementTaskTrigger.test.ts +141 -0
  54. package/Tests/Types/AI/CodeFixTaskType.test.ts +3 -0
  55. package/Tests/Types/Monitor/MonitorCriteriaMetricVariables.test.ts +228 -0
  56. package/Tests/Types/Monitor/MonitorStepDefaultTelemetryConfig.test.ts +169 -0
  57. package/Tests/Types/Monitor/MonitorStepExceptionMonitor.test.ts +289 -0
  58. package/Tests/Types/Monitor/MonitorStepLogMonitor.test.ts +231 -0
  59. package/Tests/Types/Monitor/MonitorStepMetricViewConfigUtil.test.ts +245 -0
  60. package/Tests/Types/Monitor/MonitorStepNetworkDeviceMonitor.test.ts +155 -0
  61. package/Tests/Types/Monitor/MonitorStepTelemetrySerialization.test.ts +141 -0
  62. package/Tests/Types/Monitor/MonitorStepTraceMonitor.test.ts +137 -0
  63. package/Tests/Types/Workspace/NotificationRules/NotificationRuleCondition.test.ts +311 -0
  64. package/Tests/UI/Components/ModelTableSelectFromColumns.test.ts +259 -0
  65. package/Tests/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.test.ts +461 -0
  66. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +2852 -0
  67. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +758 -0
  68. package/Tests/UI/Utils/NotificationMethodUtil.test.ts +564 -0
  69. package/Tests/Utils/Array.test.ts +156 -0
  70. package/Tests/Utils/CronTab.test.ts +199 -0
  71. package/Tests/Utils/ModelImportExport.test.ts +101 -0
  72. package/Tests/Utils/Number.test.ts +179 -0
  73. package/Tests/Utils/VersionUtil.test.ts +348 -0
  74. package/Tests/jest.setup.ts +1 -0
  75. package/Types/AI/CodeFixTaskContext.ts +6 -0
  76. package/Types/AI/CodeFixTaskType.ts +38 -1
  77. package/Types/AI/ExceptionAIClassification.ts +27 -0
  78. package/Types/EnterpriseLicense/EnterpriseLicenseInstanceSummary.ts +6 -0
  79. package/Types/Log/LogScrubPatternType.ts +7 -0
  80. package/Types/Monitor/MonitorStep.ts +24 -4
  81. package/Types/Monitor/MonitorStepMetricViewConfigUtil.ts +105 -0
  82. package/Types/Trace/TraceScrubPatternType.ts +7 -0
  83. package/UI/Components/EditionLabel/EditionLabel.tsx +445 -12
  84. package/UI/Components/ModelTable/BaseModelTable.tsx +15 -46
  85. package/UI/Components/ModelTable/SelectFromColumns.ts +129 -0
  86. package/UI/Components/Workflow/ArgumentsForm.tsx +68 -30
  87. package/UI/Components/Workflow/CronScheduleField.tsx +411 -0
  88. package/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.ts +176 -0
  89. package/UI/Utils/NotificationMethodUtil.ts +310 -0
  90. package/Utils/CronTab.ts +823 -0
  91. package/Utils/ModelImportExport.ts +21 -6
  92. package/Utils/VersionUtil.ts +260 -0
  93. package/build/dist/Models/DatabaseModels/AIInsight.js +31 -0
  94. package/build/dist/Models/DatabaseModels/AIInsight.js.map +1 -1
  95. package/build/dist/Models/DatabaseModels/EnterpriseLicense.js +22 -0
  96. package/build/dist/Models/DatabaseModels/EnterpriseLicense.js.map +1 -1
  97. package/build/dist/Models/DatabaseModels/EnterpriseLicenseInstance.js +26 -0
  98. package/build/dist/Models/DatabaseModels/EnterpriseLicenseInstance.js.map +1 -1
  99. package/build/dist/Models/DatabaseModels/GlobalConfig.js +81 -0
  100. package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
  101. package/build/dist/Models/DatabaseModels/Project.js +31 -0
  102. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  103. package/build/dist/Models/DatabaseModels/TelemetryException.js +134 -0
  104. package/build/dist/Models/DatabaseModels/TelemetryException.js.map +1 -1
  105. package/build/dist/Server/API/AIAgentDataAPI.js +97 -8
  106. package/build/dist/Server/API/AIAgentDataAPI.js.map +1 -1
  107. package/build/dist/Server/API/AIInvestigationAPI.js +55 -0
  108. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  109. package/build/dist/Server/API/EnterpriseLicenseAPI.js +47 -0
  110. package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +1 -1
  111. package/build/dist/Server/API/GlobalConfigAPI.js +52 -1
  112. package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
  113. package/build/dist/Server/EnvironmentConfig.js +17 -0
  114. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  115. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784640000000-AddExceptionTriageAndPrivacyColumns.js +33 -0
  116. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784640000000-AddExceptionTriageAndPrivacyColumns.js.map +1 -0
  117. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784659816363-AddInstanceVersionAndLatestReleaseColumns.js +18 -0
  118. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784659816363-AddInstanceVersionAndLatestReleaseColumns.js.map +1 -0
  119. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784705487674-AddEnterpriseLicenseEvaluationColumns.js +14 -0
  120. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784705487674-AddEnterpriseLicenseEvaluationColumns.js.map +1 -0
  121. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  122. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  123. package/build/dist/Server/Services/AIRunService.js +9 -2
  124. package/build/dist/Server/Services/AIRunService.js.map +1 -1
  125. package/build/dist/Server/Services/AnalyticsDatabaseService.js +44 -16
  126. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  127. package/build/dist/Server/Services/DatabaseService.js +53 -1
  128. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  129. package/build/dist/Server/Services/TelemetryExceptionService.js +263 -22
  130. package/build/dist/Server/Services/TelemetryExceptionService.js.map +1 -1
  131. package/build/dist/Server/Services/WorkflowService.js +80 -30
  132. package/build/dist/Server/Services/WorkflowService.js.map +1 -1
  133. package/build/dist/Server/Types/Workflow/Components/Schedule.js +44 -19
  134. package/build/dist/Server/Types/Workflow/Components/Schedule.js.map +1 -1
  135. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js +12 -4
  136. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js.map +1 -1
  137. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js +12 -4
  138. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js.map +1 -1
  139. package/build/dist/Server/Utils/AI/SRE/Insights/InsightScanner.js +193 -22
  140. package/build/dist/Server/Utils/AI/SRE/Insights/InsightScanner.js.map +1 -1
  141. package/build/dist/Server/Utils/AI/SRE/Insights/InsightTriageRunner.js +178 -9
  142. package/build/dist/Server/Utils/AI/SRE/Insights/InsightTriageRunner.js.map +1 -1
  143. package/build/dist/Server/Utils/AI/SRE/SubjectCodeFixRun.js +37 -0
  144. package/build/dist/Server/Utils/AI/SRE/SubjectCodeFixRun.js.map +1 -1
  145. package/build/dist/Server/Utils/AI/SRE/TelemetryImprovementTaskTrigger.js +98 -0
  146. package/build/dist/Server/Utils/AI/SRE/TelemetryImprovementTaskTrigger.js.map +1 -0
  147. package/build/dist/Server/Utils/AnalyticsDatabase/InsertDedupContext.js +24 -0
  148. package/build/dist/Server/Utils/AnalyticsDatabase/InsertDedupContext.js.map +1 -0
  149. package/build/dist/Server/Utils/Express.js +12 -0
  150. package/build/dist/Server/Utils/Express.js.map +1 -1
  151. package/build/dist/Server/Utils/Telemetry/ExceptionSanitizer.js +149 -0
  152. package/build/dist/Server/Utils/Telemetry/ExceptionSanitizer.js.map +1 -0
  153. package/build/dist/Server/Utils/Telemetry/TelemetryFanInWriter.js +496 -0
  154. package/build/dist/Server/Utils/Telemetry/TelemetryFanInWriter.js.map +1 -0
  155. package/build/dist/Server/Utils/Telemetry/TelemetryWriterClient.js +165 -0
  156. package/build/dist/Server/Utils/Telemetry/TelemetryWriterClient.js.map +1 -0
  157. package/build/dist/Server/Utils/Telemetry/TelemetryWriterServer.js +136 -0
  158. package/build/dist/Server/Utils/Telemetry/TelemetryWriterServer.js.map +1 -0
  159. package/build/dist/Server/Utils/Telemetry/TelemetryWriterShedMetrics.js +76 -0
  160. package/build/dist/Server/Utils/Telemetry/TelemetryWriterShedMetrics.js.map +1 -0
  161. package/build/dist/Types/AI/CodeFixTaskType.js +38 -1
  162. package/build/dist/Types/AI/CodeFixTaskType.js.map +1 -1
  163. package/build/dist/Types/AI/ExceptionAIClassification.js +28 -0
  164. package/build/dist/Types/AI/ExceptionAIClassification.js.map +1 -0
  165. package/build/dist/Types/Log/LogScrubPatternType.js +7 -0
  166. package/build/dist/Types/Log/LogScrubPatternType.js.map +1 -1
  167. package/build/dist/Types/Monitor/MonitorStep.js +20 -4
  168. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  169. package/build/dist/Types/Monitor/MonitorStepMetricViewConfigUtil.js +73 -0
  170. package/build/dist/Types/Monitor/MonitorStepMetricViewConfigUtil.js.map +1 -0
  171. package/build/dist/Types/Trace/TraceScrubPatternType.js +7 -0
  172. package/build/dist/Types/Trace/TraceScrubPatternType.js.map +1 -1
  173. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +261 -13
  174. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  175. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +12 -37
  176. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  177. package/build/dist/UI/Components/ModelTable/SelectFromColumns.js +82 -0
  178. package/build/dist/UI/Components/ModelTable/SelectFromColumns.js.map +1 -0
  179. package/build/dist/UI/Components/Workflow/ArgumentsForm.js +30 -6
  180. package/build/dist/UI/Components/Workflow/ArgumentsForm.js.map +1 -1
  181. package/build/dist/UI/Components/Workflow/CronScheduleField.js +209 -0
  182. package/build/dist/UI/Components/Workflow/CronScheduleField.js.map +1 -0
  183. package/build/dist/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.js +129 -0
  184. package/build/dist/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.js.map +1 -0
  185. package/build/dist/UI/Utils/NotificationMethodUtil.js +189 -0
  186. package/build/dist/UI/Utils/NotificationMethodUtil.js.map +1 -0
  187. package/build/dist/Utils/CronTab.js +609 -0
  188. package/build/dist/Utils/CronTab.js.map +1 -0
  189. package/build/dist/Utils/ModelImportExport.js +20 -6
  190. package/build/dist/Utils/ModelImportExport.js.map +1 -1
  191. package/build/dist/Utils/VersionUtil.js +193 -0
  192. package/build/dist/Utils/VersionUtil.js.map +1 -0
  193. package/package.json +1 -1
@@ -0,0 +1,348 @@
1
+ import VersionUtil, { ParsedVersion } from "../../Utils/VersionUtil";
2
+
3
+ describe("VersionUtil", () => {
4
+ describe("normalize", () => {
5
+ it("strips a leading v from a GitHub tag name", () => {
6
+ expect(VersionUtil.normalize("v11.5.13")).toBe("11.5.13");
7
+ expect(VersionUtil.normalize("V11.5.13")).toBe("11.5.13");
8
+ });
9
+
10
+ it("trims surrounding whitespace", () => {
11
+ expect(VersionUtil.normalize(" 11.5.13\n")).toBe("11.5.13");
12
+ });
13
+
14
+ it("returns an empty string for non-strings", () => {
15
+ expect(VersionUtil.normalize(null)).toBe("");
16
+ expect(VersionUtil.normalize(undefined)).toBe("");
17
+ expect(VersionUtil.normalize(11)).toBe("");
18
+ expect(VersionUtil.normalize({ version: "1.0.0" })).toBe("");
19
+ });
20
+
21
+ it("caps absurdly long input", () => {
22
+ expect(VersionUtil.normalize("1".repeat(5000)).length).toBe(100);
23
+ });
24
+ });
25
+
26
+ describe("parse", () => {
27
+ it("parses a plain release", () => {
28
+ const parsed: ParsedVersion | null = VersionUtil.parse("11.5.13");
29
+
30
+ expect(parsed).toEqual({
31
+ major: 11,
32
+ minor: 5,
33
+ patch: 13,
34
+ prerelease: null,
35
+ });
36
+ });
37
+
38
+ it("parses a prerelease", () => {
39
+ const parsed: ParsedVersion | null = VersionUtil.parse("11.6.0-rc.1");
40
+
41
+ expect(parsed).toEqual({
42
+ major: 11,
43
+ minor: 6,
44
+ patch: 0,
45
+ prerelease: "rc.1",
46
+ });
47
+ });
48
+
49
+ it("discards build metadata, which takes no part in precedence", () => {
50
+ expect(VersionUtil.parse("11.5.13+abc123")?.prerelease).toBeNull();
51
+ expect(VersionUtil.compare("11.5.13+abc123", "11.5.13")).toBe(0);
52
+ });
53
+
54
+ it("returns null for partial, empty and garbage versions", () => {
55
+ const invalid: Array<unknown> = [
56
+ "11.5",
57
+ "11",
58
+ "",
59
+ " ",
60
+ "latest",
61
+ "unknown",
62
+ "v",
63
+ "1.0.0.0",
64
+ "-1.0.0",
65
+ "1.0.0-",
66
+ null,
67
+ undefined,
68
+ 11.5,
69
+ {},
70
+ [],
71
+ ];
72
+
73
+ for (const value of invalid) {
74
+ expect(VersionUtil.parse(value)).toBeNull();
75
+ }
76
+ });
77
+
78
+ it("rejects numbers too large to be a real version", () => {
79
+ expect(VersionUtil.parse("99999999999.0.0")).toBeNull();
80
+ });
81
+
82
+ it("reports validity", () => {
83
+ expect(VersionUtil.isValid("11.5.13")).toBe(true);
84
+ expect(VersionUtil.isValid("unknown")).toBe(false);
85
+ });
86
+ });
87
+
88
+ describe("canonicalize", () => {
89
+ it("rebuilds the version from its parts", () => {
90
+ expect(VersionUtil.canonicalize("11.5.13")).toBe("11.5.13");
91
+ expect(VersionUtil.canonicalize("v11.5.13")).toBe("11.5.13");
92
+ expect(VersionUtil.canonicalize(" 11.6.0-rc.1 ")).toBe("11.6.0-rc.1");
93
+ });
94
+
95
+ /*
96
+ * A caller that validates with isValid() and then stores the string it
97
+ * passed in can end up storing "v11.5.13" for the input "vv11.5.13",
98
+ * because normalize() strips one "v" per call and isValid() normalizes a
99
+ * second time. canonicalize is the fix: it rebuilds from a single parse,
100
+ * so a doubled prefix is simply not a version and the result never carries
101
+ * a "v" for the UIs to double up on.
102
+ */
103
+ it("never returns a value that still carries a v prefix", () => {
104
+ expect(VersionUtil.canonicalize("vv11.5.13")).toBeNull();
105
+ expect(VersionUtil.canonicalize("vV11.5.13")).toBeNull();
106
+
107
+ for (const value of ["11.5.13", "v11.5.13", " V11.5.13 "]) {
108
+ expect(VersionUtil.canonicalize(value)).toBe("11.5.13");
109
+ }
110
+ });
111
+
112
+ it("drops build metadata, which takes no part in precedence", () => {
113
+ expect(VersionUtil.canonicalize("11.5.13+abc123")).toBe("11.5.13");
114
+ });
115
+
116
+ it("is idempotent", () => {
117
+ const once: string | null = VersionUtil.canonicalize("v11.5.13");
118
+
119
+ expect(VersionUtil.canonicalize(once)).toBe(once);
120
+ });
121
+
122
+ it("returns null for anything that is not a version", () => {
123
+ expect(VersionUtil.canonicalize("unknown")).toBeNull();
124
+ expect(VersionUtil.canonicalize("")).toBeNull();
125
+ expect(VersionUtil.canonicalize(null)).toBeNull();
126
+ expect(VersionUtil.canonicalize("vvv1.2.3")).toBeNull();
127
+ });
128
+ });
129
+
130
+ describe("compare", () => {
131
+ it("orders by major, then minor, then patch", () => {
132
+ expect(VersionUtil.compare("10.0.0", "11.0.0")).toBe(-1);
133
+ expect(VersionUtil.compare("11.4.0", "11.5.0")).toBe(-1);
134
+ expect(VersionUtil.compare("11.5.12", "11.5.13")).toBe(-1);
135
+ expect(VersionUtil.compare("11.5.13", "11.5.12")).toBe(1);
136
+ expect(VersionUtil.compare("2.0.0", "10.0.0")).toBe(-1);
137
+ });
138
+
139
+ it("treats equal versions as equal regardless of a v prefix", () => {
140
+ expect(VersionUtil.compare("11.5.13", "v11.5.13")).toBe(0);
141
+ });
142
+
143
+ it("ranks a prerelease below the release of the same triple", () => {
144
+ expect(VersionUtil.compare("11.6.0-rc.1", "11.6.0")).toBe(-1);
145
+ expect(VersionUtil.compare("11.6.0", "11.6.0-rc.1")).toBe(1);
146
+ });
147
+
148
+ it("orders prerelease identifiers by semver precedence", () => {
149
+ expect(VersionUtil.compare("1.0.0-alpha", "1.0.0-alpha.1")).toBe(-1);
150
+ expect(VersionUtil.compare("1.0.0-alpha.1", "1.0.0-alpha.beta")).toBe(-1);
151
+ expect(VersionUtil.compare("1.0.0-alpha.beta", "1.0.0-beta")).toBe(-1);
152
+ expect(VersionUtil.compare("1.0.0-beta.2", "1.0.0-beta.11")).toBe(-1);
153
+ expect(VersionUtil.compare("1.0.0-rc.1", "1.0.0-rc.1")).toBe(0);
154
+ });
155
+
156
+ it("returns null when either side cannot be parsed", () => {
157
+ expect(VersionUtil.compare("unknown", "11.5.13")).toBeNull();
158
+ expect(VersionUtil.compare("11.5.13", "")).toBeNull();
159
+ expect(VersionUtil.compare(null, undefined)).toBeNull();
160
+ });
161
+
162
+ it("is antisymmetric across a range of pairs", () => {
163
+ const versions: Array<string> = [
164
+ "1.0.0",
165
+ "1.0.1",
166
+ "1.1.0",
167
+ "2.0.0",
168
+ "11.5.13",
169
+ ];
170
+
171
+ for (const a of versions) {
172
+ for (const b of versions) {
173
+ const forward: number = VersionUtil.compare(a, b) as number;
174
+ const reverse: number = VersionUtil.compare(b, a) as number;
175
+
176
+ // `|| 0` keeps the negation of 0 from producing -0, which toBe rejects.
177
+ expect(forward).toBe(-reverse || 0);
178
+ }
179
+ }
180
+ });
181
+ });
182
+
183
+ describe("isUpdateAvailable", () => {
184
+ it("is true only when the latest release is strictly newer", () => {
185
+ expect(
186
+ VersionUtil.isUpdateAvailable({
187
+ currentVersion: "11.5.13",
188
+ latestVersion: "11.6.0",
189
+ }),
190
+ ).toBe(true);
191
+
192
+ expect(
193
+ VersionUtil.isUpdateAvailable({
194
+ currentVersion: "11.5.13",
195
+ latestVersion: "v11.5.14",
196
+ }),
197
+ ).toBe(true);
198
+ });
199
+
200
+ it("is false when the installation is up to date", () => {
201
+ expect(
202
+ VersionUtil.isUpdateAvailable({
203
+ currentVersion: "11.5.13",
204
+ latestVersion: "11.5.13",
205
+ }),
206
+ ).toBe(false);
207
+ });
208
+
209
+ it("never suggests a downgrade for a build ahead of the release", () => {
210
+ expect(
211
+ VersionUtil.isUpdateAvailable({
212
+ currentVersion: "11.6.0",
213
+ latestVersion: "11.5.13",
214
+ }),
215
+ ).toBe(false);
216
+ });
217
+
218
+ it("never nudges a stable installation onto a prerelease", () => {
219
+ expect(
220
+ VersionUtil.isUpdateAvailable({
221
+ currentVersion: "11.5.13",
222
+ latestVersion: "11.6.0-beta.1",
223
+ }),
224
+ ).toBe(false);
225
+ });
226
+
227
+ it("does tell a release candidate about the matching stable release", () => {
228
+ expect(
229
+ VersionUtil.isUpdateAvailable({
230
+ currentVersion: "11.6.0-rc.1",
231
+ latestVersion: "11.6.0",
232
+ }),
233
+ ).toBe(true);
234
+ });
235
+
236
+ it("is false when the latest release is a prerelease of a new major", () => {
237
+ expect(
238
+ VersionUtil.isUpdateAvailable({
239
+ currentVersion: "11.5.13",
240
+ latestVersion: "12.0.0-rc.1",
241
+ }),
242
+ ).toBe(false);
243
+ });
244
+
245
+ it("is false whenever either version is unknown or missing", () => {
246
+ const unknownValues: Array<unknown> = [
247
+ "unknown",
248
+ "",
249
+ null,
250
+ undefined,
251
+ "latest",
252
+ 42,
253
+ ];
254
+
255
+ for (const value of unknownValues) {
256
+ expect(
257
+ VersionUtil.isUpdateAvailable({
258
+ currentVersion: value,
259
+ latestVersion: "11.6.0",
260
+ }),
261
+ ).toBe(false);
262
+
263
+ expect(
264
+ VersionUtil.isUpdateAvailable({
265
+ currentVersion: "11.5.13",
266
+ latestVersion: value,
267
+ }),
268
+ ).toBe(false);
269
+ }
270
+ });
271
+ });
272
+
273
+ describe("isMajorUpgrade", () => {
274
+ it("is true only when the update crosses a major version", () => {
275
+ expect(
276
+ VersionUtil.isMajorUpgrade({
277
+ currentVersion: "11.5.13",
278
+ latestVersion: "12.0.0",
279
+ }),
280
+ ).toBe(true);
281
+
282
+ expect(
283
+ VersionUtil.isMajorUpgrade({
284
+ currentVersion: "10.9.9",
285
+ latestVersion: "12.0.0",
286
+ }),
287
+ ).toBe(true);
288
+ });
289
+
290
+ it("is false for a minor or patch update", () => {
291
+ expect(
292
+ VersionUtil.isMajorUpgrade({
293
+ currentVersion: "11.5.13",
294
+ latestVersion: "11.6.0",
295
+ }),
296
+ ).toBe(false);
297
+
298
+ expect(
299
+ VersionUtil.isMajorUpgrade({
300
+ currentVersion: "11.5.13",
301
+ latestVersion: "11.5.14",
302
+ }),
303
+ ).toBe(false);
304
+ });
305
+
306
+ it("is false when there is no update to take at all", () => {
307
+ expect(
308
+ VersionUtil.isMajorUpgrade({
309
+ currentVersion: "12.0.0",
310
+ latestVersion: "12.0.0",
311
+ }),
312
+ ).toBe(false);
313
+
314
+ // Ahead of the release — a new major behind us is still not an upgrade.
315
+ expect(
316
+ VersionUtil.isMajorUpgrade({
317
+ currentVersion: "12.1.0",
318
+ latestVersion: "11.9.9",
319
+ }),
320
+ ).toBe(false);
321
+ });
322
+
323
+ it("is false when the new major is only a prerelease", () => {
324
+ expect(
325
+ VersionUtil.isMajorUpgrade({
326
+ currentVersion: "11.5.13",
327
+ latestVersion: "12.0.0-rc.1",
328
+ }),
329
+ ).toBe(false);
330
+ });
331
+
332
+ it("is false whenever either version is unknown or missing", () => {
333
+ expect(
334
+ VersionUtil.isMajorUpgrade({
335
+ currentVersion: "unknown",
336
+ latestVersion: "12.0.0",
337
+ }),
338
+ ).toBe(false);
339
+
340
+ expect(
341
+ VersionUtil.isMajorUpgrade({
342
+ currentVersion: "11.5.13",
343
+ latestVersion: null,
344
+ }),
345
+ ).toBe(false);
346
+ });
347
+ });
348
+ });
@@ -1,4 +1,5 @@
1
1
  import "@testing-library/jest-dom";
2
+ import { jest } from "@jest/globals";
2
3
  import { TextEncoder, TextDecoder } from "util";
3
4
  import { webcrypto, randomUUID } from "crypto";
4
5
 
@@ -83,6 +83,12 @@ export interface CodeFixTaskContext {
83
83
  performanceFindings?: Array<PerformanceFinding> | undefined;
84
84
  // code.* attribute locations for stack-trace-style repo resolution.
85
85
  codeLocations?: Array<PerformanceCodeLocation> | undefined;
86
+ /*
87
+ * ImproveLogging / ImproveTracing: the telemetry service whose
88
+ * instrumentation the recipe improves (serviceName above carries its
89
+ * name for repository resolution and PR wording).
90
+ */
91
+ telemetryServiceId?: string | undefined;
86
92
  }
87
93
 
88
94
  export default CodeFixTaskContext;
@@ -12,6 +12,16 @@ enum CodeFixTaskType {
12
12
  FixException = "FixException",
13
13
  // Write a failing-then-passing regression test that reproduces the exception.
14
14
  WriteRegressionTest = "WriteRegressionTest",
15
+ /*
16
+ * Improve how the code HANDLES and REPORTS this exception without
17
+ * changing business behavior: parameterize messages that interpolate
18
+ * user data (PII out of error text, stable fingerprints), validate bad
19
+ * input earlier with an actionable error, and mark expected/operational
20
+ * errors as handled in telemetry. The recipe for exceptions the triage
21
+ * classified as user errors or expected denials — where "fix the bug"
22
+ * is the wrong instruction because there is no bug.
23
+ */
24
+ ImproveExceptionHandling = "ImproveExceptionHandling",
15
25
  /*
16
26
  * Add the observability an INCONCLUSIVE AI investigation was missing
17
27
  * (structured logs, spans, metric counters on the implicated code paths).
@@ -32,6 +42,27 @@ enum CodeFixTaskType {
32
42
  * Common/Server/Utils/AI/SRE/FixPerformanceTaskTrigger.ts).
33
43
  */
34
44
  FixPerformance = "FixPerformance",
45
+ /*
46
+ * Improve a SERVICE's logging hygiene: structured/parameterized log
47
+ * messages (no user data interpolated into log text), correct severity
48
+ * levels, trace correlation, proper exception recording instead of raw
49
+ * stack dumps in log bodies, and less noise. Human-triggered from the
50
+ * service's Logs page (POST /ai-investigation/
51
+ * create-telemetry-improvement-task); its context (service id + name) is
52
+ * captured into AIRun.taskContext at trigger time. Instrumentation only —
53
+ * never a behavior change.
54
+ */
55
+ ImproveLogging = "ImproveLogging",
56
+ /*
57
+ * Improve a SERVICE's tracing instrumentation: spans on uninstrumented
58
+ * entry points and significant operations, low-cardinality span names,
59
+ * exceptions recorded on spans with correct status/escaped semantics,
60
+ * code.* and semantic-convention attributes, context propagation across
61
+ * async boundaries. Human-triggered from the service's Traces page (same
62
+ * endpoint as ImproveLogging); context captured into AIRun.taskContext.
63
+ * Instrumentation only — never a behavior change.
64
+ */
65
+ ImproveTracing = "ImproveTracing",
35
66
  /*
36
67
  * Fix the root cause a completed AI investigation identified. Human-
37
68
  * triggered from the investigation panel (POST /ai-investigation/
@@ -77,7 +108,11 @@ export class CodeFixTaskTypeHelper {
77
108
  * (POST /ai-investigation/create-performance-fix-task).
78
109
  */
79
110
  public static getUserTriggerableTaskTypes(): Array<CodeFixTaskType> {
80
- return [CodeFixTaskType.FixException, CodeFixTaskType.WriteRegressionTest];
111
+ return [
112
+ CodeFixTaskType.FixException,
113
+ CodeFixTaskType.WriteRegressionTest,
114
+ CodeFixTaskType.ImproveExceptionHandling,
115
+ ];
81
116
  }
82
117
 
83
118
  public static isUserTriggerable(taskType: CodeFixTaskType): boolean {
@@ -97,6 +132,8 @@ export class CodeFixTaskTypeHelper {
97
132
  case CodeFixTaskType.FixFromIncident:
98
133
  return CodeFixContextKind.IncidentOrAlertSubject;
99
134
  case CodeFixTaskType.FixPerformance:
135
+ case CodeFixTaskType.ImproveLogging:
136
+ case CodeFixTaskType.ImproveTracing:
100
137
  return CodeFixContextKind.TaskContext;
101
138
  default:
102
139
  return CodeFixContextKind.TelemetryException;
@@ -0,0 +1,27 @@
1
+ /*
2
+ * AI triage verdict for a telemetry exception group. Decides how the
3
+ * automatic fix lane treats the group:
4
+ *
5
+ * - CodeFault: a defect in the monitored code — the only class the
6
+ * automatic lane opens fix pull requests for.
7
+ * - UserError: expected consequence of invalid end-user input (bad
8
+ * parameters, malformed values). The right change, if any, is earlier
9
+ * validation and clearer error UX — routed to a human, never auto-fixed.
10
+ * - ExpectedDenial: an intentional check doing its job (auth failure,
11
+ * plan/paywall denial, scanner/fuzzer probe tripping validation).
12
+ * Never auto-fixed; optionally auto-archived.
13
+ * - Infrastructure: environmental conditions (timeouts, connection
14
+ * resets, resource exhaustion) where a code "fix" is usually tuning —
15
+ * routed to a human.
16
+ * - Unknown: triage could not decide — treated conservatively (no
17
+ * automatic fix).
18
+ */
19
+ enum ExceptionAIClassification {
20
+ CodeFault = "code-fault",
21
+ UserError = "user-error",
22
+ ExpectedDenial = "expected-denial",
23
+ Infrastructure = "infrastructure",
24
+ Unknown = "unknown",
25
+ }
26
+
27
+ export default ExceptionAIClassification;
@@ -13,6 +13,12 @@ type EnterpriseLicenseInstanceSummary = {
13
13
  userCount: number | null;
14
14
  // ISO date string of the most recent usage report from this instance.
15
15
  lastReportedAt: string | null;
16
+ /*
17
+ * OneUptime version this instance last reported, for example "11.5.13".
18
+ * Null for instances that have not reported since upgrading to a build
19
+ * that sends its version, and for dev builds with no APP_VERSION baked in.
20
+ */
21
+ version: string | null;
16
22
  };
17
23
 
18
24
  export default EnterpriseLicenseInstanceSummary;
@@ -4,6 +4,13 @@ enum LogScrubPatternType {
4
4
  SSN = "ssn",
5
5
  PhoneNumber = "phoneNumber",
6
6
  IPAddress = "ipAddress",
7
+ /*
8
+ * Key-targeted: scrubs the WHOLE value of any attribute whose KEY looks
9
+ * sensitive (password, token, apiKey, authorization, cookie, ...) —
10
+ * catches secrets whose values match no shape-based pattern. Applies to
11
+ * attributes only; body text has no keys to match.
12
+ */
13
+ SensitiveKeys = "sensitiveKeys",
7
14
  Custom = "custom",
8
15
  }
9
16
 
@@ -302,10 +302,30 @@ export default class MonitorStep extends DatabaseProperty {
302
302
  retryCountOnError: undefined,
303
303
  requestTimeoutInMs: undefined,
304
304
  retryCount: undefined,
305
- logMonitor: undefined,
306
- traceMonitor: undefined,
307
- metricMonitor: undefined,
308
- exceptionMonitor: undefined,
305
+ /*
306
+ * Seed the telemetry sub-config for the monitor's OWN type so a
307
+ * monitor saved on defaults (without the user touching the telemetry
308
+ * sub-form) still persists a usable query config. Without this the
309
+ * sub-config stayed undefined and the worker threw "<type> query/config
310
+ * is missing" on every evaluation, so the monitor never ran. Only the
311
+ * matching type is seeded; every other type stays undefined.
312
+ */
313
+ logMonitor:
314
+ arg.monitorType === MonitorType.Logs
315
+ ? MonitorStepLogMonitorUtil.getDefault()
316
+ : undefined,
317
+ traceMonitor:
318
+ arg.monitorType === MonitorType.Traces
319
+ ? MonitorStepTraceMonitorUtil.getDefault()
320
+ : undefined,
321
+ metricMonitor:
322
+ arg.monitorType === MonitorType.Metrics
323
+ ? MonitorStepMetricMonitorUtil.getDefault()
324
+ : undefined,
325
+ exceptionMonitor:
326
+ arg.monitorType === MonitorType.Exceptions
327
+ ? MonitorStepExceptionMonitorUtil.getDefault()
328
+ : undefined,
309
329
  profileMonitor: undefined,
310
330
  snmpMonitor: undefined,
311
331
  networkDeviceMonitor: undefined,
@@ -0,0 +1,105 @@
1
+ import MetricFormulaConfigData from "../Metrics/MetricFormulaConfigData";
2
+ import MetricQueryConfigData from "../Metrics/MetricQueryConfigData";
3
+ import MetricsViewConfig from "../Metrics/MetricsViewConfig";
4
+ import { MonitorStepType } from "./MonitorStep";
5
+
6
+ /**
7
+ * Every monitor-step data key that carries a `metricViewConfig`. These are the
8
+ * "metric-shaped" monitor types — the ones whose alert criteria evaluate a
9
+ * metric query (CheckOn.MetricValue). The order here is not significant; a
10
+ * given monitor step only ever populates one of these keys.
11
+ *
12
+ * Keeping this list in one place is what prevents the class of bug where a new
13
+ * metric-shaped monitor type (e.g. Host, Podman, DockerSwarm, IoT) is wired
14
+ * into the step form but forgotten in the criteria UI, leaving the "Which
15
+ * metric query should this alert rule check?" dropdown blank ("No options").
16
+ */
17
+ const METRIC_VIEW_CONFIG_KEYS: Array<keyof MonitorStepType> = [
18
+ "metricMonitor",
19
+ "hostMonitor",
20
+ "kubernetesMonitor",
21
+ "dockerMonitor",
22
+ "dockerSwarmMonitor",
23
+ "podmanMonitor",
24
+ "proxmoxMonitor",
25
+ "cephMonitor",
26
+ "iotMonitor",
27
+ ];
28
+
29
+ export default class MonitorStepMetricViewConfigUtil {
30
+ /**
31
+ * Resolve the `metricViewConfig` from whichever metric-shaped monitor sub-config
32
+ * is populated on this monitor step. Returns undefined when the step is not
33
+ * metric-shaped (or none of the metric configs is present).
34
+ */
35
+ public static getMetricViewConfig(
36
+ monitorStepData: MonitorStepType | undefined,
37
+ ): MetricsViewConfig | undefined {
38
+ if (!monitorStepData) {
39
+ return undefined;
40
+ }
41
+
42
+ /*
43
+ * Each metric-shaped sub-config is an object with an optional
44
+ * `metricViewConfig`. Index through a record view so the lookup stays
45
+ * data-driven (see METRIC_VIEW_CONFIG_KEYS) without fighting the wide
46
+ * union of MonitorStepType's value types.
47
+ */
48
+ const stepRecord: Record<
49
+ string,
50
+ { metricViewConfig?: MetricsViewConfig } | undefined
51
+ > = monitorStepData as unknown as Record<
52
+ string,
53
+ { metricViewConfig?: MetricsViewConfig } | undefined
54
+ >;
55
+
56
+ for (const key of METRIC_VIEW_CONFIG_KEYS) {
57
+ const subConfig: { metricViewConfig?: MetricsViewConfig } | undefined =
58
+ stepRecord[key];
59
+
60
+ if (subConfig?.metricViewConfig) {
61
+ return subConfig.metricViewConfig;
62
+ }
63
+ }
64
+
65
+ return undefined;
66
+ }
67
+
68
+ /**
69
+ * Collect the distinct, non-empty metric variables (aliases) from the step's
70
+ * query configs and formula configs. These become the options for the
71
+ * criteria "Metric" dropdown. Empty variables are dropped and duplicates are
72
+ * removed while preserving first-seen order.
73
+ */
74
+ public static getMetricVariables(
75
+ monitorStepData: MonitorStepType | undefined,
76
+ ): Array<string> {
77
+ const metricViewConfig: MetricsViewConfig | undefined =
78
+ this.getMetricViewConfig(monitorStepData);
79
+
80
+ if (!metricViewConfig) {
81
+ return [];
82
+ }
83
+
84
+ const variables: Array<string> = [];
85
+
86
+ for (const queryConfig of metricViewConfig.queryConfigs || []) {
87
+ variables.push(
88
+ (queryConfig as MetricQueryConfigData).metricAliasData
89
+ ?.metricVariable || "",
90
+ );
91
+ }
92
+
93
+ for (const formulaConfig of metricViewConfig.formulaConfigs || []) {
94
+ variables.push(
95
+ (formulaConfig as MetricFormulaConfigData).metricAliasData
96
+ ?.metricVariable || "",
97
+ );
98
+ }
99
+
100
+ // Remove duplicates (first-seen order) and drop empty strings.
101
+ return variables.filter((item: string, index: number): boolean => {
102
+ return variables.indexOf(item) === index && item !== "";
103
+ });
104
+ }
105
+ }
@@ -4,6 +4,13 @@ enum TraceScrubPatternType {
4
4
  SSN = "ssn",
5
5
  PhoneNumber = "phoneNumber",
6
6
  IPAddress = "ipAddress",
7
+ /*
8
+ * Key-targeted: scrubs the WHOLE value of any attribute whose KEY looks
9
+ * sensitive (password, token, apiKey, authorization, cookie, ...) —
10
+ * catches secrets whose values match no shape-based pattern. Applies to
11
+ * span/event attributes only; span names have no keys to match.
12
+ */
13
+ SensitiveKeys = "sensitiveKeys",
7
14
  Custom = "custom",
8
15
  }
9
16