@oneuptime/common 10.0.7 → 10.0.8

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 (44) hide show
  1. package/Server/Services/MonitorService.ts +9 -0
  2. package/Server/Types/Markdown.ts +17 -4
  3. package/Server/Utils/Monitor/Criteria/ExternalStatusPageMonitorCriteria.ts +158 -0
  4. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +15 -0
  5. package/Server/Utils/Monitor/MonitorTemplateUtil.ts +32 -0
  6. package/Server/Utils/VM/VMRunner.ts +131 -62
  7. package/Types/Monitor/CriteriaFilter.ts +12 -2
  8. package/Types/Monitor/ExternalStatusPageMonitor/ExternalStatusPageMonitorResponse.ts +16 -0
  9. package/Types/Monitor/ExternalStatusPageProviderType.ts +8 -0
  10. package/Types/Monitor/MonitorCriteriaInstance.ts +67 -0
  11. package/Types/Monitor/MonitorStep.ts +34 -0
  12. package/Types/Monitor/MonitorStepExternalStatusPageMonitor.ts +48 -0
  13. package/Types/Monitor/MonitorType.ts +16 -2
  14. package/Types/Probe/ProbeMonitorResponse.ts +2 -0
  15. package/Utils/Monitor/MonitorMetricType.ts +3 -1
  16. package/build/dist/Server/Services/MonitorService.js +7 -1
  17. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  18. package/build/dist/Server/Types/Markdown.js +16 -4
  19. package/build/dist/Server/Types/Markdown.js.map +1 -1
  20. package/build/dist/Server/Utils/Monitor/Criteria/ExternalStatusPageMonitorCriteria.js +119 -0
  21. package/build/dist/Server/Utils/Monitor/Criteria/ExternalStatusPageMonitorCriteria.js.map +1 -0
  22. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +10 -0
  23. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  24. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +22 -0
  25. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
  26. package/build/dist/Server/Utils/VM/VMRunner.js +111 -52
  27. package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
  28. package/build/dist/Types/Monitor/CriteriaFilter.js +11 -2
  29. package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
  30. package/build/dist/Types/Monitor/ExternalStatusPageMonitor/ExternalStatusPageMonitorResponse.js +2 -0
  31. package/build/dist/Types/Monitor/ExternalStatusPageMonitor/ExternalStatusPageMonitorResponse.js.map +1 -0
  32. package/build/dist/Types/Monitor/ExternalStatusPageProviderType.js +9 -0
  33. package/build/dist/Types/Monitor/ExternalStatusPageProviderType.js.map +1 -0
  34. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +62 -0
  35. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
  36. package/build/dist/Types/Monitor/MonitorStep.js +22 -0
  37. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  38. package/build/dist/Types/Monitor/MonitorStepExternalStatusPageMonitor.js +32 -0
  39. package/build/dist/Types/Monitor/MonitorStepExternalStatusPageMonitor.js.map +1 -0
  40. package/build/dist/Types/Monitor/MonitorType.js +14 -2
  41. package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
  42. package/build/dist/Utils/Monitor/MonitorMetricType.js +3 -1
  43. package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
  44. package/package.json +1 -1
@@ -135,6 +135,15 @@ export class Service extends DatabaseService<Model> {
135
135
  monitorDestination = `${monitorDestination} @${firstStep.data.dnsMonitor.hostname}`;
136
136
  }
137
137
  }
138
+
139
+ // For External Status Page monitors, use the statusPageUrl
140
+ if (
141
+ monitorType === MonitorType.ExternalStatusPage &&
142
+ firstStep?.data?.externalStatusPageMonitor
143
+ ) {
144
+ monitorDestination =
145
+ firstStep.data.externalStatusPageMonitor.statusPageUrl || "";
146
+ }
138
147
  }
139
148
  }
140
149
 
@@ -103,6 +103,15 @@ export default class Markdown {
103
103
  return htmlBody;
104
104
  }
105
105
 
106
+ private static escapeHtml(text: string): string {
107
+ return text
108
+ .replace(/&/g, "&amp;")
109
+ .replace(/</g, "&lt;")
110
+ .replace(/>/g, "&gt;")
111
+ .replace(/"/g, "&quot;")
112
+ .replace(/'/g, "&#39;");
113
+ }
114
+
106
115
  private static getEmailRenderer(): Renderer {
107
116
  if (this.emailRenderer !== null) {
108
117
  return this.emailRenderer;
@@ -140,7 +149,8 @@ export default class Markdown {
140
149
  if (language === "mermaid") {
141
150
  return `<div class="mermaid">${code}</div>`;
142
151
  }
143
- return `<pre><code class="language-${language}">${code}</code></pre>`;
152
+ const escaped: string = Markdown.escapeHtml(code);
153
+ return `<pre><code class="language-${language}">${escaped}</code></pre>`;
144
154
  };
145
155
 
146
156
  renderer.heading = function (text, level) {
@@ -160,7 +170,8 @@ export default class Markdown {
160
170
 
161
171
  // Inline code
162
172
  renderer.codespan = function (code) {
163
- return `<code class="rounded-md bg-slate-100 px-1.5 py-0.5 text-sm text-slate-700 font-mono">${code}</code>`;
173
+ const escaped: string = Markdown.escapeHtml(code);
174
+ return `<code class="rounded-md bg-slate-100 px-1.5 py-0.5 text-sm text-slate-700 font-mono">${escaped}</code>`;
164
175
  };
165
176
 
166
177
  this.docsRenderer = renderer;
@@ -186,7 +197,8 @@ export default class Markdown {
186
197
  };
187
198
 
188
199
  renderer.code = function (code, language) {
189
- return `<pre class="language-${language} rounded-md"><code class="language-${language} rounded-md">${code}</code></pre>`;
200
+ const escaped: string = Markdown.escapeHtml(code);
201
+ return `<pre class="language-${language} rounded-md"><code class="language-${language} rounded-md">${escaped}</code></pre>`;
190
202
  };
191
203
 
192
204
  renderer.heading = function (text, level) {
@@ -237,7 +249,8 @@ export default class Markdown {
237
249
 
238
250
  // Inline code
239
251
  renderer.codespan = function (code) {
240
- return `<code class="rounded-md bg-gray-100 px-1.5 py-0.5 text-sm text-pink-600">${code}</code>`;
252
+ const escaped: string = Markdown.escapeHtml(code);
253
+ return `<code class="rounded-md bg-gray-100 px-1.5 py-0.5 text-sm text-pink-600">${escaped}</code>`;
241
254
  };
242
255
 
243
256
  // Horizontal rule
@@ -0,0 +1,158 @@
1
+ import DataToProcess from "../DataToProcess";
2
+ import CompareCriteria from "./CompareCriteria";
3
+ import {
4
+ CheckOn,
5
+ CriteriaFilter,
6
+ } from "../../../../Types/Monitor/CriteriaFilter";
7
+ import ExternalStatusPageMonitorResponse from "../../../../Types/Monitor/ExternalStatusPageMonitor/ExternalStatusPageMonitorResponse";
8
+ import ProbeMonitorResponse from "../../../../Types/Probe/ProbeMonitorResponse";
9
+ import EvaluateOverTime from "./EvaluateOverTime";
10
+ import CaptureSpan from "../../Telemetry/CaptureSpan";
11
+ import logger from "../../Logger";
12
+
13
+ export default class ExternalStatusPageMonitorCriteria {
14
+ @CaptureSpan()
15
+ public static async isMonitorInstanceCriteriaFilterMet(input: {
16
+ dataToProcess: DataToProcess;
17
+ criteriaFilter: CriteriaFilter;
18
+ }): Promise<string | null> {
19
+ let threshold: number | string | undefined | null =
20
+ input.criteriaFilter.value;
21
+
22
+ const dataToProcess: ProbeMonitorResponse =
23
+ input.dataToProcess as ProbeMonitorResponse;
24
+
25
+ const externalStatusPageResponse:
26
+ | ExternalStatusPageMonitorResponse
27
+ | undefined = dataToProcess.externalStatusPageResponse;
28
+
29
+ let overTimeValue: Array<number | boolean> | number | boolean | undefined =
30
+ undefined;
31
+
32
+ if (
33
+ input.criteriaFilter.evaluateOverTime &&
34
+ input.criteriaFilter.evaluateOverTimeOptions
35
+ ) {
36
+ try {
37
+ overTimeValue = await EvaluateOverTime.getValueOverTime({
38
+ projectId: (input.dataToProcess as ProbeMonitorResponse).projectId,
39
+ monitorId: input.dataToProcess.monitorId!,
40
+ evaluateOverTimeOptions: input.criteriaFilter.evaluateOverTimeOptions,
41
+ metricType: input.criteriaFilter.checkOn,
42
+ });
43
+
44
+ if (Array.isArray(overTimeValue) && overTimeValue.length === 0) {
45
+ overTimeValue = undefined;
46
+ }
47
+ } catch (err) {
48
+ logger.error(
49
+ `Error in getting over time value for ${input.criteriaFilter.checkOn}`,
50
+ );
51
+ logger.error(err);
52
+ overTimeValue = undefined;
53
+ }
54
+ }
55
+
56
+ // Check if external status page is online
57
+ if (input.criteriaFilter.checkOn === CheckOn.ExternalStatusPageIsOnline) {
58
+ const currentIsOnline: boolean | Array<boolean> =
59
+ (overTimeValue as Array<boolean>) ||
60
+ (input.dataToProcess as ProbeMonitorResponse).isOnline;
61
+
62
+ return CompareCriteria.compareCriteriaBoolean({
63
+ value: currentIsOnline,
64
+ criteriaFilter: input.criteriaFilter,
65
+ });
66
+ }
67
+
68
+ // Check external status page response time
69
+ if (
70
+ input.criteriaFilter.checkOn === CheckOn.ExternalStatusPageResponseTime
71
+ ) {
72
+ threshold = CompareCriteria.convertToNumber(threshold);
73
+
74
+ if (threshold === null || threshold === undefined) {
75
+ return null;
76
+ }
77
+
78
+ const currentResponseTime: number | Array<number> =
79
+ (overTimeValue as Array<number>) ||
80
+ externalStatusPageResponse?.responseTimeInMs ||
81
+ (input.dataToProcess as ProbeMonitorResponse).responseTimeInMs;
82
+
83
+ if (currentResponseTime === null || currentResponseTime === undefined) {
84
+ return null;
85
+ }
86
+
87
+ return CompareCriteria.compareCriteriaNumbers({
88
+ value: currentResponseTime,
89
+ threshold: threshold as number,
90
+ criteriaFilter: input.criteriaFilter,
91
+ });
92
+ }
93
+
94
+ // Check overall status
95
+ if (
96
+ input.criteriaFilter.checkOn === CheckOn.ExternalStatusPageOverallStatus
97
+ ) {
98
+ if (!externalStatusPageResponse?.overallStatus) {
99
+ return null;
100
+ }
101
+
102
+ return CompareCriteria.compareCriteriaStrings({
103
+ value: externalStatusPageResponse.overallStatus,
104
+ threshold: String(threshold),
105
+ criteriaFilter: input.criteriaFilter,
106
+ });
107
+ }
108
+
109
+ // Check component status
110
+ if (
111
+ input.criteriaFilter.checkOn === CheckOn.ExternalStatusPageComponentStatus
112
+ ) {
113
+ if (
114
+ !externalStatusPageResponse?.componentStatuses ||
115
+ externalStatusPageResponse.componentStatuses.length === 0
116
+ ) {
117
+ return null;
118
+ }
119
+
120
+ // Check if any component status matches the criteria
121
+ for (const component of externalStatusPageResponse.componentStatuses) {
122
+ const result: string | null = CompareCriteria.compareCriteriaStrings({
123
+ value: component.status,
124
+ threshold: String(threshold),
125
+ criteriaFilter: input.criteriaFilter,
126
+ });
127
+
128
+ if (result) {
129
+ return `Component "${component.name}": ${result}`;
130
+ }
131
+ }
132
+
133
+ return null;
134
+ }
135
+
136
+ // Check active incidents count
137
+ if (
138
+ input.criteriaFilter.checkOn === CheckOn.ExternalStatusPageActiveIncidents
139
+ ) {
140
+ threshold = CompareCriteria.convertToNumber(threshold);
141
+
142
+ if (threshold === null || threshold === undefined) {
143
+ return null;
144
+ }
145
+
146
+ const activeIncidents: number =
147
+ externalStatusPageResponse?.activeIncidentCount || 0;
148
+
149
+ return CompareCriteria.compareCriteriaNumbers({
150
+ value: activeIncidents,
151
+ threshold: threshold as number,
152
+ criteriaFilter: input.criteriaFilter,
153
+ });
154
+ }
155
+
156
+ return null;
157
+ }
158
+ }
@@ -14,6 +14,7 @@ import ExceptionMonitorCriteria from "./Criteria/ExceptionMonitorCriteria";
14
14
  import SnmpMonitorCriteria from "./Criteria/SnmpMonitorCriteria";
15
15
  import DnsMonitorCriteria from "./Criteria/DnsMonitorCriteria";
16
16
  import DomainMonitorCriteria from "./Criteria/DomainMonitorCriteria";
17
+ import ExternalStatusPageMonitorCriteria from "./Criteria/ExternalStatusPageMonitorCriteria";
17
18
  import MonitorCriteriaMessageBuilder from "./MonitorCriteriaMessageBuilder";
18
19
  import MonitorCriteriaDataExtractor from "./MonitorCriteriaDataExtractor";
19
20
  import MonitorCriteriaMessageFormatter from "./MonitorCriteriaMessageFormatter";
@@ -519,6 +520,20 @@ ${contextBlock}
519
520
  }
520
521
  }
521
522
 
523
+ if (input.monitor.monitorType === MonitorType.ExternalStatusPage) {
524
+ const externalStatusPageResult: string | null =
525
+ await ExternalStatusPageMonitorCriteria.isMonitorInstanceCriteriaFilterMet(
526
+ {
527
+ dataToProcess: input.dataToProcess,
528
+ criteriaFilter: input.criteriaFilter,
529
+ },
530
+ );
531
+
532
+ if (externalStatusPageResult) {
533
+ return externalStatusPageResult;
534
+ }
535
+ }
536
+
522
537
  return null;
523
538
  }
524
539
 
@@ -18,6 +18,9 @@ import DnsMonitorResponse, {
18
18
  DnsRecordResponse,
19
19
  } from "../../../Types/Monitor/DnsMonitor/DnsMonitorResponse";
20
20
  import DomainMonitorResponse from "../../../Types/Monitor/DomainMonitor/DomainMonitorResponse";
21
+ import ExternalStatusPageMonitorResponse, {
22
+ ExternalStatusPageComponentStatus,
23
+ } from "../../../Types/Monitor/ExternalStatusPageMonitor/ExternalStatusPageMonitorResponse";
21
24
  import Typeof from "../../../Types/Typeof";
22
25
  import VMUtil from "../VM/VMAPI";
23
26
  import DataToProcess from "./DataToProcess";
@@ -298,6 +301,35 @@ export default class MonitorTemplateUtil {
298
301
  dnssec: domainResponse?.dnssec,
299
302
  } as JSONObject;
300
303
  }
304
+
305
+ if (data.monitorType === MonitorType.ExternalStatusPage) {
306
+ const externalStatusPageResponse:
307
+ | ExternalStatusPageMonitorResponse
308
+ | undefined = (data.dataToProcess as ProbeMonitorResponse)
309
+ .externalStatusPageResponse;
310
+
311
+ storageMap = {
312
+ isOnline: (data.dataToProcess as ProbeMonitorResponse).isOnline,
313
+ responseTimeInMs: externalStatusPageResponse?.responseTimeInMs,
314
+ failureCause: externalStatusPageResponse?.failureCause,
315
+ overallStatus: externalStatusPageResponse?.overallStatus,
316
+ activeIncidentCount: externalStatusPageResponse?.activeIncidentCount,
317
+ } as JSONObject;
318
+
319
+ // Add component statuses
320
+ if (externalStatusPageResponse?.componentStatuses) {
321
+ storageMap["componentStatuses"] =
322
+ externalStatusPageResponse.componentStatuses.map(
323
+ (component: ExternalStatusPageComponentStatus) => {
324
+ return {
325
+ name: component.name,
326
+ status: component.status,
327
+ description: component.description,
328
+ };
329
+ },
330
+ );
331
+ }
332
+ }
301
333
  } catch (err) {
302
334
  logger.error(err);
303
335
  }
@@ -143,62 +143,100 @@ export default class VMRunner {
143
143
  }
144
144
  }
145
145
 
146
- let response: AxiosResponse;
147
-
148
- switch (method) {
149
- case "get":
150
- response = await axios.get(url, config);
151
- break;
152
- case "head":
153
- response = await axios.head(url, config);
154
- break;
155
- case "options":
156
- response = await axios.options(url, config);
157
- break;
158
- case "post":
159
- response = await axios.post(url, body, config);
160
- break;
161
- case "put":
162
- response = await axios.put(url, body, config);
163
- break;
164
- case "patch":
165
- response = await axios.patch(url, body, config);
166
- break;
167
- case "delete":
168
- response = await axios.delete(url, config);
169
- break;
170
- case "request":
171
- response = await axios.request(
172
- config as Parameters<typeof axios.request>[0],
173
- );
174
- break;
175
- default:
176
- throw new Error(`Unsupported HTTP method: ${method}`);
177
- }
178
-
179
- /*
180
- * Convert AxiosHeaders to a plain object before serializing.
181
- * JSON.stringify calls AxiosHeaders.toJSON(key) with a truthy key,
182
- * which makes it join array headers (like set-cookie) with commas.
183
- * This produces invalid Cookie headers when user code forwards them.
146
+ /**
147
+ * Helper: convert AxiosHeaders (or any header-like object) to a
148
+ * plain record so it can be safely JSON-serialised.
184
149
  */
185
- const plainHeaders: Record<string, unknown> = {};
186
-
187
- if (response.headers) {
188
- for (const key of Object.keys(
189
- response.headers as Record<string, unknown>,
190
- )) {
191
- plainHeaders[key] = (response.headers as Record<string, unknown>)[
192
- key
193
- ];
150
+ const toPlainHeaders: (
151
+ headers: unknown,
152
+ ) => Record<string, unknown> = (
153
+ headers: unknown,
154
+ ): Record<string, unknown> => {
155
+ const plain: Record<string, unknown> = {};
156
+ if (headers) {
157
+ for (const hKey of Object.keys(
158
+ headers as Record<string, unknown>,
159
+ )) {
160
+ plain[hKey] = (headers as Record<string, unknown>)[hKey];
161
+ }
194
162
  }
195
- }
163
+ return plain;
164
+ };
196
165
 
197
- return JSON.stringify({
198
- status: response.status,
199
- headers: plainHeaders,
200
- data: response.data,
201
- });
166
+ try {
167
+ let response: AxiosResponse;
168
+
169
+ switch (method) {
170
+ case "get":
171
+ response = await axios.get(url, config);
172
+ break;
173
+ case "head":
174
+ response = await axios.head(url, config);
175
+ break;
176
+ case "options":
177
+ response = await axios.options(url, config);
178
+ break;
179
+ case "post":
180
+ response = await axios.post(url, body, config);
181
+ break;
182
+ case "put":
183
+ response = await axios.put(url, body, config);
184
+ break;
185
+ case "patch":
186
+ response = await axios.patch(url, body, config);
187
+ break;
188
+ case "delete":
189
+ response = await axios.delete(url, config);
190
+ break;
191
+ case "request":
192
+ response = await axios.request(
193
+ config as Parameters<typeof axios.request>[0],
194
+ );
195
+ break;
196
+ default:
197
+ throw new Error(`Unsupported HTTP method: ${method}`);
198
+ }
199
+
200
+ /*
201
+ * Convert AxiosHeaders to a plain object before serializing.
202
+ * JSON.stringify calls AxiosHeaders.toJSON(key) with a truthy key,
203
+ * which makes it join array headers (like set-cookie) with commas.
204
+ * This produces invalid Cookie headers when user code forwards them.
205
+ */
206
+ return JSON.stringify({
207
+ status: response.status,
208
+ headers: toPlainHeaders(response.headers),
209
+ data: response.data,
210
+ });
211
+ } catch (err: unknown) {
212
+ /*
213
+ * If this is an axios error with a response (4xx, 5xx, etc.),
214
+ * return the error details as JSON so the sandbox-side axios
215
+ * wrapper can reconstruct error.response for user code.
216
+ */
217
+ const axiosErr: {
218
+ isAxiosError?: boolean;
219
+ response?: AxiosResponse<any, any, Record<string, unknown>>;
220
+ message?: string;
221
+ } = err as {
222
+ isAxiosError?: boolean;
223
+ response?: AxiosResponse;
224
+ message?: string;
225
+ };
226
+
227
+ if (axiosErr.isAxiosError && axiosErr.response) {
228
+ return JSON.stringify({
229
+ __isAxiosError: true,
230
+ message: axiosErr.message || "Request failed",
231
+ status: axiosErr.response.status,
232
+ statusText: axiosErr.response.statusText,
233
+ headers: toPlainHeaders(axiosErr.response.headers),
234
+ data: axiosErr.response.data,
235
+ });
236
+ }
237
+
238
+ throw err;
239
+ }
202
240
  },
203
241
  );
204
242
 
@@ -236,6 +274,23 @@ export default class VMRunner {
236
274
  }
237
275
  }
238
276
 
277
+ function _parseAxiosResult(r) {
278
+ const parsed = JSON.parse(r);
279
+ if (parsed && parsed.__isAxiosError) {
280
+ const err = new Error(parsed.message);
281
+ err.response = {
282
+ status: parsed.status,
283
+ statusText: parsed.statusText,
284
+ headers: parsed.headers,
285
+ data: parsed.data,
286
+ };
287
+ err.isAxiosError = true;
288
+ err.status = parsed.status;
289
+ throw err;
290
+ }
291
+ return parsed;
292
+ }
293
+
239
294
  function _makeAxiosInstance(defaults) {
240
295
  function mergeConfig(overrides) {
241
296
  if (!defaults && !overrides) return undefined;
@@ -252,7 +307,7 @@ export default class VMRunner {
252
307
  const merged = mergeConfig(config);
253
308
  if (merged) _assertNoFunctions(merged, 'config');
254
309
  const r = await _axiosRef.applySyncPromise(undefined, ['request', '', merged ? JSON.stringify(merged) : undefined]);
255
- return JSON.parse(r);
310
+ return _parseAxiosResult(r);
256
311
  }
257
312
 
258
313
  // Make instance callable: axios(config) or axios(url, config)
@@ -268,46 +323,46 @@ export default class VMRunner {
268
323
  const merged = mergeConfig(config);
269
324
  if (merged) _assertNoFunctions(merged, 'config');
270
325
  const r = await _axiosRef.applySyncPromise(undefined, ['get', url, merged ? JSON.stringify(merged) : undefined]);
271
- return JSON.parse(r);
326
+ return _parseAxiosResult(r);
272
327
  };
273
328
  instance.head = async (url, config) => {
274
329
  const merged = mergeConfig(config);
275
330
  if (merged) _assertNoFunctions(merged, 'config');
276
331
  const r = await _axiosRef.applySyncPromise(undefined, ['head', url, merged ? JSON.stringify(merged) : undefined]);
277
- return JSON.parse(r);
332
+ return _parseAxiosResult(r);
278
333
  };
279
334
  instance.options = async (url, config) => {
280
335
  const merged = mergeConfig(config);
281
336
  if (merged) _assertNoFunctions(merged, 'config');
282
337
  const r = await _axiosRef.applySyncPromise(undefined, ['options', url, merged ? JSON.stringify(merged) : undefined]);
283
- return JSON.parse(r);
338
+ return _parseAxiosResult(r);
284
339
  };
285
340
  instance.post = async (url, data, config) => {
286
341
  const merged = mergeConfig(config);
287
342
  if (data) _assertNoFunctions(data, 'data');
288
343
  if (merged) _assertNoFunctions(merged, 'config');
289
344
  const r = await _axiosRef.applySyncPromise(undefined, ['post', url, data ? JSON.stringify(data) : undefined, merged ? JSON.stringify(merged) : undefined]);
290
- return JSON.parse(r);
345
+ return _parseAxiosResult(r);
291
346
  };
292
347
  instance.put = async (url, data, config) => {
293
348
  const merged = mergeConfig(config);
294
349
  if (data) _assertNoFunctions(data, 'data');
295
350
  if (merged) _assertNoFunctions(merged, 'config');
296
351
  const r = await _axiosRef.applySyncPromise(undefined, ['put', url, data ? JSON.stringify(data) : undefined, merged ? JSON.stringify(merged) : undefined]);
297
- return JSON.parse(r);
352
+ return _parseAxiosResult(r);
298
353
  };
299
354
  instance.patch = async (url, data, config) => {
300
355
  const merged = mergeConfig(config);
301
356
  if (data) _assertNoFunctions(data, 'data');
302
357
  if (merged) _assertNoFunctions(merged, 'config');
303
358
  const r = await _axiosRef.applySyncPromise(undefined, ['patch', url, data ? JSON.stringify(data) : undefined, merged ? JSON.stringify(merged) : undefined]);
304
- return JSON.parse(r);
359
+ return _parseAxiosResult(r);
305
360
  };
306
361
  instance.delete = async (url, config) => {
307
362
  const merged = mergeConfig(config);
308
363
  if (merged) _assertNoFunctions(merged, 'config');
309
364
  const r = await _axiosRef.applySyncPromise(undefined, ['delete', url, merged ? JSON.stringify(merged) : undefined]);
310
- return JSON.parse(r);
365
+ return _parseAxiosResult(r);
311
366
  };
312
367
  instance.create = (instanceDefaults) => {
313
368
  if (instanceDefaults) _assertNoFunctions(instanceDefaults, 'defaults');
@@ -321,7 +376,7 @@ export default class VMRunner {
321
376
  const axios = _makeAxiosInstance(null);
322
377
  `);
323
378
 
324
- // crypto (createHash, createHmac, randomBytes) - bridged via applySync
379
+ // crypto (createHash, createHmac, randomBytes, randomUUID, randomInt) - bridged via applySync
325
380
  const cryptoRef: ivm.Reference<
326
381
  (op: string, ...args: string[]) => string
327
382
  > = new ivm.Reference((op: string, ...args: string[]): string => {
@@ -344,6 +399,13 @@ export default class VMRunner {
344
399
  const [size] = args;
345
400
  return crypto.randomBytes(parseInt(size!)).toString("hex");
346
401
  }
402
+ case "randomUUID": {
403
+ return crypto.randomUUID();
404
+ }
405
+ case "randomInt": {
406
+ const [min, max] = args;
407
+ return String(crypto.randomInt(parseInt(min!), parseInt(max!)));
408
+ }
347
409
  default:
348
410
  throw new Error(`Unsupported crypto operation: ${op}`);
349
411
  }
@@ -366,6 +428,13 @@ export default class VMRunner {
366
428
  randomBytes: (size) => ({
367
429
  toString(enc) { return _cryptoRef.applySync(undefined, ['randomBytes', String(size)]); }
368
430
  }),
431
+ randomUUID: () => {
432
+ return _cryptoRef.applySync(undefined, ['randomUUID']);
433
+ },
434
+ randomInt: (minOrMax, max) => {
435
+ if (max === undefined) { max = minOrMax; minOrMax = 0; }
436
+ return Number(_cryptoRef.applySync(undefined, ['randomInt', String(minOrMax), String(max)]));
437
+ },
369
438
  };
370
439
  `);
371
440
 
@@ -74,6 +74,13 @@ export enum CheckOn {
74
74
  DomainNameServer = "Domain Name Server",
75
75
  DomainStatusCode = "Domain Status Code",
76
76
  DomainIsExpired = "Domain Is Expired",
77
+
78
+ // External Status Page monitors.
79
+ ExternalStatusPageIsOnline = "External Status Page Is Online",
80
+ ExternalStatusPageOverallStatus = "External Status Page Overall Status",
81
+ ExternalStatusPageComponentStatus = "External Status Page Component Status",
82
+ ExternalStatusPageActiveIncidents = "External Status Page Active Incidents",
83
+ ExternalStatusPageResponseTime = "External Status Page Response Time (in ms)",
77
84
  }
78
85
 
79
86
  export interface ServerMonitorOptions {
@@ -159,7 +166,8 @@ export class CriteriaFilterUtil {
159
166
  checkOn === CheckOn.IsOnline ||
160
167
  checkOn === CheckOn.SnmpIsOnline ||
161
168
  checkOn === CheckOn.DnsIsOnline ||
162
- checkOn === CheckOn.DomainIsExpired
169
+ checkOn === CheckOn.DomainIsExpired ||
170
+ checkOn === CheckOn.ExternalStatusPageIsOnline
163
171
  ) {
164
172
  return false;
165
173
  }
@@ -229,7 +237,9 @@ export class CriteriaFilterUtil {
229
237
  checkOn === CheckOn.SnmpResponseTime ||
230
238
  checkOn === CheckOn.SnmpIsOnline ||
231
239
  checkOn === CheckOn.DnsResponseTime ||
232
- checkOn === CheckOn.DnsIsOnline
240
+ checkOn === CheckOn.DnsIsOnline ||
241
+ checkOn === CheckOn.ExternalStatusPageResponseTime ||
242
+ checkOn === CheckOn.ExternalStatusPageIsOnline
233
243
  );
234
244
  }
235
245
  }
@@ -0,0 +1,16 @@
1
+ export interface ExternalStatusPageComponentStatus {
2
+ name: string;
3
+ status: string;
4
+ description?: string | undefined;
5
+ }
6
+
7
+ export default interface ExternalStatusPageMonitorResponse {
8
+ isOnline: boolean;
9
+ overallStatus: string;
10
+ componentStatuses: Array<ExternalStatusPageComponentStatus>;
11
+ activeIncidentCount: number;
12
+ responseTimeInMs: number;
13
+ failureCause: string;
14
+ rawBody?: string | undefined;
15
+ isTimeout?: boolean | undefined;
16
+ }
@@ -0,0 +1,8 @@
1
+ enum ExternalStatusPageProviderType {
2
+ AtlassianStatuspage = "Atlassian Statuspage",
3
+ RSS = "RSS",
4
+ Atom = "Atom",
5
+ Auto = "Auto",
6
+ }
7
+
8
+ export default ExternalStatusPageProviderType;