@oneuptime/common 8.0.5438 → 8.0.5462

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 (56) hide show
  1. package/Models/DatabaseModels/StatusPage.ts +80 -0
  2. package/Server/API/StatusPageAPI.ts +138 -52
  3. package/Server/EnvironmentConfig.ts +34 -0
  4. package/Server/Infrastructure/Postgres/SchemaMigrations/1761232578396-MigrationName.ts +29 -0
  5. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
  6. package/Server/Services/OpenTelemetryIngestService.ts +1 -39
  7. package/Server/Services/StatusPageService.ts +117 -0
  8. package/Server/Services/TelemetryUsageBillingService.ts +208 -15
  9. package/Server/Types/Billing/MeteredPlan/TelemetryMeteredPlan.ts +5 -0
  10. package/Server/Utils/Telemetry/Telemetry.ts +129 -81
  11. package/Server/Utils/VM/VMRunner.ts +3 -4
  12. package/UI/Components/Dictionary/Dictionary.tsx +3 -0
  13. package/UI/Components/Forms/Fields/FieldLabel.tsx +7 -3
  14. package/UI/Components/LogsViewer/LogItem.tsx +12 -4
  15. package/UI/Components/LogsViewer/LogsViewer.tsx +131 -29
  16. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +2 -2
  17. package/UI/Components/ModelFilter/Filter.ts +1 -0
  18. package/UI/Components/ModelTable/BaseModelTable.tsx +2 -1
  19. package/UI/Components/Table/TableRow.tsx +89 -77
  20. package/UI/esbuild-config.js +32 -1
  21. package/build/dist/Models/DatabaseModels/StatusPage.js +82 -0
  22. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  23. package/build/dist/Server/API/StatusPageAPI.js +157 -74
  24. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  25. package/build/dist/Server/EnvironmentConfig.js +14 -0
  26. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  27. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1761232578396-MigrationName.js +16 -0
  28. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1761232578396-MigrationName.js.map +1 -0
  29. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  30. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  31. package/build/dist/Server/Services/OpenTelemetryIngestService.js +0 -30
  32. package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
  33. package/build/dist/Server/Services/StatusPageService.js +95 -0
  34. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  35. package/build/dist/Server/Services/TelemetryUsageBillingService.js +168 -8
  36. package/build/dist/Server/Services/TelemetryUsageBillingService.js.map +1 -1
  37. package/build/dist/Server/Types/Billing/MeteredPlan/TelemetryMeteredPlan.js +4 -0
  38. package/build/dist/Server/Types/Billing/MeteredPlan/TelemetryMeteredPlan.js.map +1 -1
  39. package/build/dist/Server/Utils/Telemetry/Telemetry.js +84 -60
  40. package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
  41. package/build/dist/Server/Utils/VM/VMRunner.js +2 -2
  42. package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
  43. package/build/dist/UI/Components/Dictionary/Dictionary.js +3 -3
  44. package/build/dist/UI/Components/Dictionary/Dictionary.js.map +1 -1
  45. package/build/dist/UI/Components/Forms/Fields/FieldLabel.js +2 -1
  46. package/build/dist/UI/Components/Forms/Fields/FieldLabel.js.map +1 -1
  47. package/build/dist/UI/Components/LogsViewer/LogItem.js +5 -3
  48. package/build/dist/UI/Components/LogsViewer/LogItem.js.map +1 -1
  49. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +73 -22
  50. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  51. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +2 -2
  52. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +2 -1
  53. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  54. package/build/dist/UI/Components/Table/TableRow.js +18 -6
  55. package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
  56. package/package.json +4 -4
@@ -15,9 +15,11 @@ import React, {
15
15
  ReactElement,
16
16
  Ref,
17
17
  useCallback,
18
+ useMemo,
18
19
  } from "react";
19
20
  import Toggle from "../Toggle/Toggle";
20
21
  import Card from "../Card/Card";
22
+ import Icon from "../Icon/Icon";
21
23
  import Button, { ButtonSize, ButtonStyleType } from "../Button/Button";
22
24
  import IconProp from "../../../Types/Icon/IconProp";
23
25
  import ModelAPI from "../../Utils/ModelAPI/ModelAPI";
@@ -66,8 +68,9 @@ const LogsViewer: FunctionComponent<ComponentProps> = (
66
68
  typeof window !== "undefined" ? window.innerHeight : 900,
67
69
  );
68
70
  const [autoScroll, setAutoScroll] = React.useState<boolean>(true);
69
- const [showScrollToBottom, setShowScrollToBottom] =
71
+ const [showScrollToLatest, setShowScrollToLatest] =
70
72
  React.useState<boolean>(false);
73
+ const [isDescending, setIsDescending] = React.useState<boolean>(false);
71
74
  // removed wrapLines toggle for a cleaner toolbar
72
75
  const logsViewerRef: Ref<HTMLDivElement> = React.useRef<HTMLDivElement>(null);
73
76
  const scrollContainerRef: Ref<HTMLDivElement> =
@@ -89,6 +92,14 @@ const LogsViewer: FunctionComponent<ComponentProps> = (
89
92
  Dictionary<TelemetryService>
90
93
  >({});
91
94
 
95
+ const displayLogs: Array<Log> = useMemo(() => {
96
+ if (isDescending) {
97
+ return [...props.logs].reverse();
98
+ }
99
+
100
+ return props.logs;
101
+ }, [props.logs, isDescending]);
102
+
92
103
  const loadTelemetryServices: PromiseVoidFunction =
93
104
  useCallback(async (): Promise<void> => {
94
105
  try {
@@ -178,33 +189,73 @@ const LogsViewer: FunctionComponent<ComponentProps> = (
178
189
  };
179
190
  }, [loadTelemetryServices]);
180
191
 
181
- // Keep scroll to the bottom of the log
192
+ // Keep scroll aligned with the latest log entry
182
193
 
183
- const scrollToBottom: VoidFunction = (): void => {
194
+ const scrollToLatest: VoidFunction = (): void => {
184
195
  const scrollContainer: HTMLDivElement | null = scrollContainerRef.current;
185
196
 
186
- if (scrollContainer) {
187
- scrollContainer.scrollTop = scrollContainer.scrollHeight;
197
+ if (!scrollContainer) {
198
+ return;
199
+ }
200
+
201
+ if (isDescending) {
202
+ scrollContainer.scrollTop = 0;
203
+ return;
188
204
  }
205
+
206
+ scrollContainer.scrollTop = scrollContainer.scrollHeight;
207
+ };
208
+
209
+ const applySortDirection: (nextDescending: boolean) => void = (
210
+ nextDescending: boolean,
211
+ ) => {
212
+ setShowScrollToLatest(false);
213
+ setIsDescending((previous: boolean) => {
214
+ if (previous === nextDescending) {
215
+ return previous;
216
+ }
217
+
218
+ // Apply scroll alignment after the DOM reorders log entries.
219
+ setTimeout(() => {
220
+ const scrollContainer: HTMLDivElement | null =
221
+ scrollContainerRef.current;
222
+
223
+ if (!scrollContainer) {
224
+ return;
225
+ }
226
+
227
+ if (nextDescending) {
228
+ scrollContainer.scrollTop = 0;
229
+ return;
230
+ }
231
+
232
+ scrollContainer.scrollTop = scrollContainer.scrollHeight;
233
+ }, 0);
234
+
235
+ return nextDescending;
236
+ });
189
237
  };
190
238
 
191
- const handleScroll: VoidFunction = (): void => {
239
+ const handleScroll: VoidFunction = React.useCallback((): void => {
192
240
  const scrollContainer: HTMLDivElement | null = scrollContainerRef.current;
193
- if (scrollContainer) {
194
- const { scrollTop, scrollHeight, clientHeight } = scrollContainer;
195
- const isNearBottom: boolean =
196
- scrollHeight - scrollTop - clientHeight < 100;
197
- setShowScrollToBottom(!isNearBottom && props.logs.length > 0);
241
+ if (!scrollContainer) {
242
+ return;
198
243
  }
199
- };
244
+
245
+ const { scrollTop, scrollHeight, clientHeight } = scrollContainer;
246
+ const isNearLatest: boolean = isDescending
247
+ ? scrollTop < 100
248
+ : scrollHeight - scrollTop - clientHeight < 100;
249
+ setShowScrollToLatest(!isNearLatest && displayLogs.length > 0);
250
+ }, [isDescending, displayLogs.length]);
200
251
 
201
252
  React.useEffect(() => {
202
253
  if (!autoScroll) {
203
254
  return;
204
255
  }
205
256
 
206
- scrollToBottom();
207
- }, [props.logs]);
257
+ scrollToLatest();
258
+ }, [props.logs, autoScroll, isDescending]);
208
259
 
209
260
  React.useEffect(() => {
210
261
  const scrollContainer: HTMLDivElement | null = scrollContainerRef.current;
@@ -215,7 +266,7 @@ const LogsViewer: FunctionComponent<ComponentProps> = (
215
266
  };
216
267
  }
217
268
  return () => {}; // Return empty cleanup function if no scrollContainer
218
- }, []);
269
+ }, [handleScroll]);
219
270
 
220
271
  if (isPageLoading) {
221
272
  return <PageLoader isVisible={true} />;
@@ -305,12 +356,54 @@ const LogsViewer: FunctionComponent<ComponentProps> = (
305
356
  </div>
306
357
  <span className="hidden sm:block h-4 w-px bg-slate-200" />
307
358
  <span className="text-xs text-slate-500">
308
- {props.logs.length} result
309
- {props.logs.length !== 1 ? "s" : ""}
359
+ {displayLogs.length} result
360
+ {displayLogs.length !== 1 ? "s" : ""}
310
361
  </span>
311
362
  </div>
312
363
 
313
364
  <div className="flex items-center gap-2">
365
+ <div className="inline-flex items-center rounded-full border border-slate-200 bg-white/80 p-1 shadow-sm ring-1 ring-slate-200/60">
366
+ <button
367
+ type="button"
368
+ aria-pressed={isDescending}
369
+ className={`flex items-center gap-2 rounded-full px-3 py-1 text-xs font-semibold tracking-wide transition-all duration-200 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-500 ${
370
+ isDescending
371
+ ? "bg-indigo-600 text-white shadow-sm ring-1 ring-indigo-500/40"
372
+ : "text-slate-500 hover:text-indigo-600"
373
+ }`}
374
+ onClick={() => {
375
+ applySortDirection(true);
376
+ }}
377
+ >
378
+ <Icon
379
+ icon={IconProp.BarsArrowDown}
380
+ className={`h-4 w-4 ${
381
+ isDescending ? "text-white/90" : "text-slate-400"
382
+ }`}
383
+ />
384
+ <span>Newest first</span>
385
+ </button>
386
+ <button
387
+ type="button"
388
+ aria-pressed={!isDescending}
389
+ className={`flex items-center gap-2 rounded-full px-3 py-1 text-xs font-semibold tracking-wide transition-all duration-200 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-500 ${
390
+ !isDescending
391
+ ? "bg-indigo-600 text-white shadow-sm ring-1 ring-indigo-500/40"
392
+ : "text-slate-500 hover:text-indigo-600"
393
+ }`}
394
+ onClick={() => {
395
+ applySortDirection(false);
396
+ }}
397
+ >
398
+ <Icon
399
+ icon={IconProp.BarsArrowUp}
400
+ className={`h-4 w-4 ${
401
+ !isDescending ? "text-white/90" : "text-slate-400"
402
+ }`}
403
+ />
404
+ <span>Oldest first</span>
405
+ </button>
406
+ </div>
314
407
  <Button
315
408
  title="Apply Filters"
316
409
  icon={IconProp.Search}
@@ -342,7 +435,7 @@ const LogsViewer: FunctionComponent<ComponentProps> = (
342
435
  onScroll={handleScroll}
343
436
  >
344
437
  <ul role="list" className="divide-y divide-slate-800">
345
- {props.logs.map((log: Log, i: number) => {
438
+ {displayLogs.map((log: Log, i: number) => {
346
439
  const traceRouteProps: OptionalTraceRouteProps =
347
440
  props.getTraceRoute
348
441
  ? { getTraceRoute: props.getTraceRoute }
@@ -364,7 +457,7 @@ const LogsViewer: FunctionComponent<ComponentProps> = (
364
457
  })}
365
458
  </ul>
366
459
 
367
- {props.logs.length === 0 && (
460
+ {displayLogs.length === 0 && (
368
461
  <div className="flex items-center justify-center h-full px-4">
369
462
  <div className="text-center">
370
463
  <h3 className="text-sm font-medium text-slate-300 mb-1">
@@ -380,12 +473,12 @@ const LogsViewer: FunctionComponent<ComponentProps> = (
380
473
  </div>
381
474
  </div>
382
475
 
383
- {/* Floating Scroll to Bottom Button */}
384
- {showScrollToBottom && (
476
+ {/* Floating Scroll to Latest Button */}
477
+ {showScrollToLatest && (
385
478
  <button
386
- onClick={scrollToBottom}
479
+ onClick={scrollToLatest}
387
480
  className="absolute bottom-3 right-3 bg-slate-700 hover:bg-slate-600 text-white p-2 rounded-md shadow transition-all"
388
- title="Scroll to bottom"
481
+ title={isDescending ? "Scroll to top" : "Scroll to bottom"}
389
482
  >
390
483
  <svg
391
484
  className="w-5 h-5"
@@ -393,12 +486,21 @@ const LogsViewer: FunctionComponent<ComponentProps> = (
393
486
  stroke="currentColor"
394
487
  viewBox="0 0 24 24"
395
488
  >
396
- <path
397
- strokeLinecap="round"
398
- strokeLinejoin="round"
399
- strokeWidth={2}
400
- d="M19 14l-7 7m0 0l-7-7m7 7V3"
401
- />
489
+ {isDescending ? (
490
+ <path
491
+ strokeLinecap="round"
492
+ strokeLinejoin="round"
493
+ strokeWidth={2}
494
+ d="M5 14l7-7 7 7m-7-7v18"
495
+ />
496
+ ) : (
497
+ <path
498
+ strokeLinecap="round"
499
+ strokeLinejoin="round"
500
+ strokeWidth={2}
501
+ d="M19 10l-7 7-7-7m7 7V3"
502
+ />
503
+ )}
402
504
  </svg>
403
505
  </button>
404
506
  )}
@@ -98,7 +98,7 @@ const MarkdownViewer: FunctionComponent<ComponentProps> = (
98
98
 
99
99
  const baseClass: string = isSyntaxHighlighter
100
100
  ? "mt-4 mb-4 rounded-lg overflow-hidden"
101
- : "bg-gray-900 text-gray-100 mt-4 mb-4 p-4 rounded-lg text-sm overflow-x-auto border border-gray-700";
101
+ : "bg-gray-900 text-gray-100 mt-4 mb-4 p-2 rounded-lg text-sm overflow-x-auto border border-gray-700";
102
102
 
103
103
  return (
104
104
  <pre className={baseClass} {...rest}>
@@ -201,7 +201,7 @@ const MarkdownViewer: FunctionComponent<ComponentProps> = (
201
201
  children={content}
202
202
  language={match[1]}
203
203
  style={a11yDark}
204
- className="rounded-lg mt-4 mb-4 !bg-gray-900 !p-4 text-sm"
204
+ className="rounded-lg mt-4 mb-4 !bg-gray-900 !p-2 text-sm"
205
205
  codeTagProps={{ className: "font-mono" }}
206
206
  />
207
207
  ) : (
@@ -26,4 +26,5 @@ export default interface Filter<
26
26
  filterDropdownOptions?: Array<DropdownOption> | undefined;
27
27
  fetchFilterDropdownOptions?: () => Promise<Array<DropdownOption>> | undefined;
28
28
  jsonKeys?: Array<string> | undefined;
29
+ isAdvancedFilter?: boolean | undefined;
29
30
  }
@@ -682,6 +682,7 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
682
682
  key: key,
683
683
  type: filter.type,
684
684
  jsonKeys: filter.jsonKeys,
685
+ isAdvancedFilter: filter.isAdvancedFilter,
685
686
  };
686
687
  })
687
688
  .filter((filter: ClassicFilterType<TBaseModel> | null) => {
@@ -782,7 +783,7 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
782
783
  setTableFilterError(API.getFriendlyMessage(err));
783
784
  });
784
785
  }
785
- }, [showFilterModal]);
786
+ }, [showFilterModal, props.filters]);
786
787
 
787
788
  type GetSelectFunction = () => Select<TBaseModel>;
788
789
 
@@ -350,6 +350,90 @@ const TableRow: TableRowFunction = <T extends GenericObject>(
350
350
  className =
351
351
  "whitespace-nowrap py-4 pl-4 pr-6 text-sm font-medium text-gray-500 sm:pl-6 align-top";
352
352
  }
353
+
354
+ let columnContent: React.ReactNode = null;
355
+
356
+ if (column.key && !column.getElement) {
357
+ columnContent =
358
+ column.type === FieldType.Date ? (
359
+ props.item[column.key] ? (
360
+ OneUptimeDate.getDateAsUserFriendlyLocalFormattedString(
361
+ props.item[column.key] as string,
362
+ true,
363
+ )
364
+ ) : (
365
+ column.noValueMessage || ""
366
+ )
367
+ ) : column.type === FieldType.DateTime ? (
368
+ props.item[column.key] ? (
369
+ OneUptimeDate.getDateAsUserFriendlyLocalFormattedString(
370
+ props.item[column.key] as string,
371
+ false,
372
+ )
373
+ ) : (
374
+ column.noValueMessage || ""
375
+ )
376
+ ) : column.type === FieldType.USDCents ? (
377
+ props.item[column.key] ? (
378
+ ((props.item[column.key] as number) || 0) / 100 + " USD"
379
+ ) : (
380
+ column.noValueMessage || "0 USD"
381
+ )
382
+ ) : column.type === FieldType.Percent ? (
383
+ props.item[column.key] ? (
384
+ props.item[column.key] + "%"
385
+ ) : (
386
+ column.noValueMessage || "0%"
387
+ )
388
+ ) : column.type === FieldType.Color ? (
389
+ props.item[column.key] ? (
390
+ <ColorInput value={props.item[column.key] as Color} />
391
+ ) : (
392
+ column.noValueMessage || "0%"
393
+ )
394
+ ) : column.type === FieldType.LongText ? (
395
+ props.item[column.key] ? (
396
+ <LongTextViewer
397
+ text={props.item[column.key] as string}
398
+ />
399
+ ) : (
400
+ column.noValueMessage || ""
401
+ )
402
+ ) : column.type === FieldType.Boolean ? (
403
+ props.item[column.key] ? (
404
+ <Icon
405
+ icon={IconProp.Check}
406
+ className={"h-5 w-5 text-gray-500"}
407
+ thick={ThickProp.Thick}
408
+ />
409
+ ) : (
410
+ <Icon
411
+ icon={IconProp.False}
412
+ className={"h-5 w-5 text-gray-500"}
413
+ thick={ThickProp.Thick}
414
+ />
415
+ )
416
+ ) : (
417
+ getNestedValue(
418
+ props.item,
419
+ String(column.key),
420
+ )?.toString() ||
421
+ column.noValueMessage ||
422
+ ""
423
+ );
424
+ } else if (column.key && column.getElement) {
425
+ columnContent = column.getElement(props.item);
426
+ }
427
+
428
+ const contentWrapperClassName: string = column.contentClassName
429
+ ? column.contentClassName
430
+ : "";
431
+
432
+ const actionsContainerClassName: string =
433
+ column.contentClassName
434
+ ? `flex justify-end ${column.contentClassName}`
435
+ : "flex justify-end";
436
+
353
437
  return (
354
438
  <td
355
439
  key={i}
@@ -364,85 +448,13 @@ const TableRow: TableRowFunction = <T extends GenericObject>(
364
448
  }
365
449
  }}
366
450
  >
367
- {column.key && !column.getElement ? (
368
- column.type === FieldType.Date ? (
369
- props.item[column.key] ? (
370
- OneUptimeDate.getDateAsUserFriendlyLocalFormattedString(
371
- props.item[column.key] as string,
372
- true,
373
- )
374
- ) : (
375
- column.noValueMessage || ""
376
- )
377
- ) : column.type === FieldType.DateTime ? (
378
- props.item[column.key] ? (
379
- OneUptimeDate.getDateAsUserFriendlyLocalFormattedString(
380
- props.item[column.key] as string,
381
- false,
382
- )
383
- ) : (
384
- column.noValueMessage || ""
385
- )
386
- ) : column.type === FieldType.USDCents ? (
387
- props.item[column.key] ? (
388
- ((props.item[column.key] as number) || 0) / 100 +
389
- " USD"
390
- ) : (
391
- column.noValueMessage || "0 USD"
392
- )
393
- ) : column.type === FieldType.Percent ? (
394
- props.item[column.key] ? (
395
- props.item[column.key] + "%"
396
- ) : (
397
- column.noValueMessage || "0%"
398
- )
399
- ) : column.type === FieldType.Color ? (
400
- props.item[column.key] ? (
401
- <ColorInput value={props.item[column.key] as Color} />
402
- ) : (
403
- column.noValueMessage || "0%"
404
- )
405
- ) : column.type === FieldType.LongText ? (
406
- props.item[column.key] ? (
407
- <LongTextViewer
408
- text={props.item[column.key] as string}
409
- />
410
- ) : (
411
- column.noValueMessage || ""
412
- )
413
- ) : column.type === FieldType.Boolean ? (
414
- props.item[column.key] ? (
415
- <Icon
416
- icon={IconProp.Check}
417
- className={"h-5 w-5 text-gray-500"}
418
- thick={ThickProp.Thick}
419
- />
420
- ) : (
421
- <Icon
422
- icon={IconProp.False}
423
- className={"h-5 w-5 text-gray-500"}
424
- thick={ThickProp.Thick}
425
- />
426
- )
427
- ) : (
428
- getNestedValue(
429
- props.item,
430
- String(column.key),
431
- )?.toString() ||
432
- column.noValueMessage ||
433
- ""
434
- )
435
- ) : (
436
- <></>
437
- )}
438
-
439
- {column.key && column.getElement ? (
440
- column.getElement(props.item)
441
- ) : (
442
- <></>
451
+ {columnContent !== null && columnContent !== undefined && (
452
+ <div className={contentWrapperClassName}>
453
+ {columnContent}
454
+ </div>
443
455
  )}
444
456
  {column.type === FieldType.Actions && (
445
- <div className="flex justify-end">
457
+ <div className={actionsContainerClassName}>
446
458
  {error && (
447
459
  <div className="text-align-left">
448
460
  <ConfirmModal
@@ -8,6 +8,37 @@ const path = require('path');
8
8
  const fs = require('fs');
9
9
  const dotenv = require('dotenv');
10
10
 
11
+ function createRefractorCompatibilityPlugin() {
12
+ const candidateRoots = [
13
+ path.resolve(__dirname, '../node_modules/refractor'),
14
+ path.resolve(__dirname, '../../node_modules/refractor'),
15
+ ];
16
+
17
+ const refractorRoot = candidateRoots.find((packagePath) => fs.existsSync(packagePath));
18
+
19
+ if (!refractorRoot) {
20
+ throw new Error('Unable to locate refractor package for esbuild compatibility plugin.');
21
+ }
22
+
23
+ return {
24
+ name: 'refractor-compatibility',
25
+ setup(build) {
26
+ build.onResolve({ filter: /^refractor\/lib\// }, (args) => {
27
+ const relativePath = args.path.replace(/^refractor\/lib\//, '');
28
+ const candidatePath = path.join(refractorRoot, 'lib', `${relativePath}.js`);
29
+ return { path: candidatePath };
30
+ });
31
+
32
+ build.onResolve({ filter: /^refractor\/lang\// }, (args) => {
33
+ const relativePath = args.path.replace(/^refractor\/lang\//, '');
34
+ const filename = relativePath.endsWith('.js') ? relativePath : `${relativePath}.js`;
35
+ const candidatePath = path.join(refractorRoot, 'lang', filename);
36
+ return { path: candidatePath };
37
+ });
38
+ },
39
+ };
40
+ }
41
+
11
42
  // CSS Plugin to handle CSS/SCSS files
12
43
  function createCSSPlugin() {
13
44
  return {
@@ -146,7 +177,7 @@ function createConfig(options) {
146
177
  'react': path.resolve('./node_modules/react'),
147
178
  ...additionalAlias,
148
179
  },
149
- plugins: [createCSSPlugin(), createFileLoaderPlugin()],
180
+ plugins: [createRefractorCompatibilityPlugin(), createCSSPlugin(), createFileLoaderPlugin()],
150
181
  loader: {
151
182
  '.tsx': 'tsx',
152
183
  '.ts': 'ts',
@@ -107,6 +107,8 @@ let StatusPage = class StatusPage extends BaseModel {
107
107
  this.showScheduledMaintenanceEventsOnStatusPage = undefined;
108
108
  this.showSubscriberPageOnStatusPage = undefined;
109
109
  this.ipWhitelist = undefined;
110
+ this.enableEmbeddedOverallStatus = undefined;
111
+ this.embeddedOverallStatusToken = undefined;
110
112
  }
111
113
  };
112
114
  __decorate([
@@ -2361,6 +2363,86 @@ __decorate([
2361
2363
  }),
2362
2364
  __metadata("design:type", String)
2363
2365
  ], StatusPage.prototype, "ipWhitelist", void 0);
2366
+ __decorate([
2367
+ ColumnAccessControl({
2368
+ create: [
2369
+ Permission.ProjectOwner,
2370
+ Permission.ProjectAdmin,
2371
+ Permission.ProjectMember,
2372
+ Permission.CreateProjectStatusPage,
2373
+ ],
2374
+ read: [
2375
+ Permission.ProjectOwner,
2376
+ Permission.ProjectAdmin,
2377
+ Permission.ProjectMember,
2378
+ Permission.ReadProjectStatusPage,
2379
+ ],
2380
+ update: [
2381
+ Permission.ProjectOwner,
2382
+ Permission.ProjectAdmin,
2383
+ Permission.ProjectMember,
2384
+ Permission.EditProjectStatusPage,
2385
+ ],
2386
+ }),
2387
+ TableColumn({
2388
+ isDefaultValueColumn: true,
2389
+ type: TableColumnType.Boolean,
2390
+ title: "Enable Embedded Overall Status Badge",
2391
+ description: "Enable embedded overall status badge that can be displayed on external websites?",
2392
+ defaultValue: false,
2393
+ }),
2394
+ Column({
2395
+ type: ColumnType.Boolean,
2396
+ default: false,
2397
+ nullable: false,
2398
+ }),
2399
+ ColumnBillingAccessControl({
2400
+ read: PlanType.Free,
2401
+ update: PlanType.Growth,
2402
+ create: PlanType.Free,
2403
+ }),
2404
+ __metadata("design:type", Boolean)
2405
+ ], StatusPage.prototype, "enableEmbeddedOverallStatus", void 0);
2406
+ __decorate([
2407
+ ColumnAccessControl({
2408
+ create: [
2409
+ Permission.ProjectOwner,
2410
+ Permission.ProjectAdmin,
2411
+ Permission.ProjectMember,
2412
+ Permission.CreateProjectStatusPage,
2413
+ ],
2414
+ read: [
2415
+ Permission.ProjectOwner,
2416
+ Permission.ProjectAdmin,
2417
+ Permission.ProjectMember,
2418
+ Permission.ReadProjectStatusPage,
2419
+ ],
2420
+ update: [
2421
+ Permission.ProjectOwner,
2422
+ Permission.ProjectAdmin,
2423
+ Permission.ProjectMember,
2424
+ Permission.EditProjectStatusPage,
2425
+ ],
2426
+ }),
2427
+ Index(),
2428
+ TableColumn({
2429
+ type: TableColumnType.ShortText,
2430
+ required: false,
2431
+ title: "Embedded Overall Status Token",
2432
+ description: "Security token required to access the embedded overall status badge. This token must be provided in the URL.",
2433
+ }),
2434
+ Column({
2435
+ type: ColumnType.ShortText,
2436
+ length: ColumnLength.ShortText,
2437
+ nullable: true,
2438
+ }),
2439
+ ColumnBillingAccessControl({
2440
+ read: PlanType.Free,
2441
+ update: PlanType.Growth,
2442
+ create: PlanType.Free,
2443
+ }),
2444
+ __metadata("design:type", String)
2445
+ ], StatusPage.prototype, "embeddedOverallStatusToken", void 0);
2364
2446
  StatusPage = __decorate([
2365
2447
  EnableDocumentation(),
2366
2448
  AccessControlColumn("labels"),