@oneuptime/common 9.5.10 → 9.5.13

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 (29) hide show
  1. package/Models/DatabaseModels/GlobalConfig.ts +19 -0
  2. package/Server/DatabaseConfig.ts +7 -0
  3. package/Server/Infrastructure/Postgres/SchemaMigrations/1770834237091-MigrationName.ts +23 -0
  4. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
  5. package/Server/Services/ProjectService.ts +14 -0
  6. package/Types/Monitor/MonitorType.ts +50 -0
  7. package/UI/Components/CardSelect/CardSelect.tsx +133 -67
  8. package/UI/Components/Forms/Types/Field.ts +7 -2
  9. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +56 -1
  10. package/UI/esbuild-config.js +52 -3
  11. package/UI/index.d.ts +26 -0
  12. package/build/dist/Models/DatabaseModels/GlobalConfig.js +21 -0
  13. package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
  14. package/build/dist/Server/DatabaseConfig.js +9 -0
  15. package/build/dist/Server/DatabaseConfig.js.map +1 -1
  16. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770834237091-MigrationName.js +14 -0
  17. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770834237091-MigrationName.js.map +1 -0
  18. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  19. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  20. package/build/dist/Server/Services/ProjectService.js +12 -2
  21. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  22. package/build/dist/Types/Monitor/MonitorType.js +44 -0
  23. package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
  24. package/build/dist/UI/Components/CardSelect/CardSelect.js +55 -20
  25. package/build/dist/UI/Components/CardSelect/CardSelect.js.map +1 -1
  26. package/build/dist/UI/Components/Forms/Types/Field.js.map +1 -1
  27. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +55 -1
  28. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  29. package/package.json +1 -1
@@ -58,6 +58,25 @@ export default class GlobalConfig extends GlobalConfigModel {
58
58
  })
59
59
  public disableSignup?: boolean = undefined;
60
60
 
61
+ @ColumnAccessControl({
62
+ create: [],
63
+ read: [],
64
+ update: [],
65
+ })
66
+ @TableColumn({
67
+ type: TableColumnType.Boolean,
68
+ title: "Disable User Project Creation",
69
+ description: "Only master admins can create projects when enabled.",
70
+ defaultValue: false,
71
+ })
72
+ @Column({
73
+ type: ColumnType.Boolean,
74
+ nullable: true,
75
+ default: false,
76
+ unique: true,
77
+ })
78
+ public disableUserProjectCreation?: boolean = undefined;
79
+
61
80
  // SMTP Settings.
62
81
 
63
82
  @ColumnAccessControl({
@@ -80,4 +80,11 @@ export default class DatabaseConfig {
80
80
  "disableSignup",
81
81
  )) as boolean;
82
82
  }
83
+
84
+ @CaptureSpan()
85
+ public static async shouldDisableUserProjectCreation(): Promise<boolean> {
86
+ return (await DatabaseConfig.getFromGlobalConfig(
87
+ "disableUserProjectCreation",
88
+ )) as boolean;
89
+ }
83
90
  }
@@ -0,0 +1,23 @@
1
+ import { MigrationInterface, QueryRunner } from "typeorm";
2
+
3
+ export class MigrationName1770834237091 implements MigrationInterface {
4
+ public name = "MigrationName1770834237091";
5
+
6
+ public async up(queryRunner: QueryRunner): Promise<void> {
7
+ await queryRunner.query(
8
+ `ALTER TABLE "GlobalConfig" ADD "disableUserProjectCreation" boolean DEFAULT false`,
9
+ );
10
+ await queryRunner.query(
11
+ `ALTER TABLE "GlobalConfig" ADD CONSTRAINT "UQ_disableUserProjectCreation" UNIQUE ("disableUserProjectCreation")`,
12
+ );
13
+ }
14
+
15
+ public async down(queryRunner: QueryRunner): Promise<void> {
16
+ await queryRunner.query(
17
+ `ALTER TABLE "GlobalConfig" DROP CONSTRAINT "UQ_disableUserProjectCreation"`,
18
+ );
19
+ await queryRunner.query(
20
+ `ALTER TABLE "GlobalConfig" DROP COLUMN "disableUserProjectCreation"`,
21
+ );
22
+ }
23
+ }
@@ -258,6 +258,7 @@ import { MigrationName1770728946893 } from "./1770728946893-MigrationName";
258
258
  import { MigrationName1770732721195 } from "./1770732721195-MigrationName";
259
259
  import { MigrationName1770833704656 } from "./1770833704656-MigrationName";
260
260
  import { MigrationName1770834237090 } from "./1770834237090-MigrationName";
261
+ import { MigrationName1770834237091 } from "./1770834237091-MigrationName";
261
262
 
262
263
  export default [
263
264
  InitialMigration,
@@ -520,4 +521,5 @@ export default [
520
521
  MigrationName1770732721195,
521
522
  MigrationName1770833704656,
522
523
  MigrationName1770834237090,
524
+ MigrationName1770834237091,
523
525
  ];
@@ -124,6 +124,7 @@ export class ProjectService extends DatabaseService<Model> {
124
124
  select: {
125
125
  name: true,
126
126
  email: true,
127
+ isMasterAdmin: true,
127
128
  companyPhoneNumber: true,
128
129
  companyName: true,
129
130
  utmCampaign: true,
@@ -142,6 +143,15 @@ export class ProjectService extends DatabaseService<Model> {
142
143
  throw new BadDataException("User not found.");
143
144
  }
144
145
 
146
+ // Check if project creation is restricted to admins only
147
+ const shouldDisableProjectCreation: boolean =
148
+ await DatabaseConfig.shouldDisableUserProjectCreation();
149
+ if (shouldDisableProjectCreation && !user.isMasterAdmin) {
150
+ throw new NotAuthorizedException(
151
+ "Project creation is restricted to admin users only on this OneUptime Server. Please contact your server admin.",
152
+ );
153
+ }
154
+
145
155
  if (IsBillingEnabled) {
146
156
  if (!data.data.paymentProviderPlanId) {
147
157
  throw new BadDataException("Plan required to create the project.");
@@ -1313,6 +1323,8 @@ export class ProjectService extends DatabaseService<Model> {
1313
1323
  paymentProviderSubscriptionId: true,
1314
1324
  paymentProviderMeteredSubscriptionId: true,
1315
1325
  name: true,
1326
+ createdAt: true,
1327
+ planName: true,
1316
1328
  createdByUser: {
1317
1329
  name: true,
1318
1330
  email: true,
@@ -1341,6 +1353,8 @@ export class ProjectService extends DatabaseService<Model> {
1341
1353
  let slackMessage: string = `*Project Deleted:*
1342
1354
  *Project Name:* ${project.name?.toString() || "N/A"}
1343
1355
  *Project ID:* ${project._id?.toString() || "N/A"}
1356
+ *Project Created Date:* ${project.createdAt ? new Date(project.createdAt).toUTCString() : "N/A"}
1357
+ *Project Plan Name:* ${project.planName?.toString() || "N/A"}
1344
1358
  `;
1345
1359
 
1346
1360
  if (subscriptionStatus) {
@@ -40,7 +40,57 @@ export interface MonitorTypeProps {
40
40
  icon: IconProp;
41
41
  }
42
42
 
43
+ export interface MonitorTypeCategory {
44
+ label: string;
45
+ monitorTypes: Array<MonitorType>;
46
+ }
47
+
43
48
  export class MonitorTypeHelper {
49
+ public static getMonitorTypeCategories(): Array<MonitorTypeCategory> {
50
+ return [
51
+ {
52
+ label: "Basic Monitoring",
53
+ monitorTypes: [
54
+ MonitorType.Website,
55
+ MonitorType.API,
56
+ MonitorType.Ping,
57
+ MonitorType.IP,
58
+ MonitorType.Port,
59
+ MonitorType.DNS,
60
+ MonitorType.SSLCertificate,
61
+ ],
62
+ },
63
+ {
64
+ label: "Synthetic Monitoring",
65
+ monitorTypes: [
66
+ MonitorType.SyntheticMonitor,
67
+ MonitorType.CustomJavaScriptCode,
68
+ ],
69
+ },
70
+ {
71
+ label: "Inbound Monitoring",
72
+ monitorTypes: [MonitorType.IncomingRequest, MonitorType.IncomingEmail],
73
+ },
74
+ {
75
+ label: "Infrastructure",
76
+ monitorTypes: [MonitorType.Server, MonitorType.SNMP],
77
+ },
78
+ {
79
+ label: "Telemetry",
80
+ monitorTypes: [
81
+ MonitorType.Logs,
82
+ MonitorType.Metrics,
83
+ MonitorType.Traces,
84
+ MonitorType.Exceptions,
85
+ ],
86
+ },
87
+ {
88
+ label: "Other",
89
+ monitorTypes: [MonitorType.Manual],
90
+ },
91
+ ];
92
+ }
93
+
44
94
  public static isTelemetryMonitor(monitorType: MonitorType): boolean {
45
95
  return (
46
96
  monitorType === MonitorType.Logs ||
@@ -9,8 +9,22 @@ export interface CardSelectOption {
9
9
  icon: IconProp;
10
10
  }
11
11
 
12
- export interface ComponentProps {
12
+ export interface CardSelectOptionGroup {
13
+ label: string;
13
14
  options: Array<CardSelectOption>;
15
+ }
16
+
17
+ export function isCardSelectOptionGroup(
18
+ option: CardSelectOption | CardSelectOptionGroup,
19
+ ): option is CardSelectOptionGroup {
20
+ return (
21
+ (option as CardSelectOptionGroup).label !== undefined &&
22
+ Array.isArray((option as CardSelectOptionGroup).options)
23
+ );
24
+ }
25
+
26
+ export interface ComponentProps {
27
+ options: Array<CardSelectOption | CardSelectOptionGroup>;
14
28
  value?: string | undefined;
15
29
  onChange: (value: string) => void;
16
30
  error?: string | undefined;
@@ -18,80 +32,132 @@ export interface ComponentProps {
18
32
  dataTestId?: string | undefined;
19
33
  }
20
34
 
35
+ interface RenderGroup {
36
+ label: string | null;
37
+ options: Array<CardSelectOption>;
38
+ }
39
+
21
40
  const CardSelect: FunctionComponent<ComponentProps> = (
22
41
  props: ComponentProps,
23
42
  ): ReactElement => {
43
+ // Normalize options into render groups
44
+ const groups: Array<RenderGroup> = [];
45
+ let ungroupedOptions: Array<CardSelectOption> = [];
46
+
47
+ for (const option of props.options) {
48
+ if (isCardSelectOptionGroup(option)) {
49
+ // Flush any accumulated ungrouped options first
50
+ if (ungroupedOptions.length > 0) {
51
+ groups.push({ label: null, options: ungroupedOptions });
52
+ ungroupedOptions = [];
53
+ }
54
+ groups.push({ label: option.label, options: option.options });
55
+ } else {
56
+ ungroupedOptions.push(option);
57
+ }
58
+ }
59
+
60
+ if (ungroupedOptions.length > 0) {
61
+ groups.push({ label: null, options: ungroupedOptions });
62
+ }
63
+
64
+ let cardIndex: number = 0;
65
+
24
66
  return (
25
67
  <div data-testid={props.dataTestId}>
26
- <div
27
- role="radiogroup"
28
- aria-label="Select an option"
29
- className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3"
30
- >
31
- {props.options.map((option: CardSelectOption, index: number) => {
32
- const isSelected: boolean = props.value === option.value;
33
-
68
+ <div role="radiogroup" aria-label="Select an option">
69
+ {groups.map((group: RenderGroup, groupIndex: number) => {
34
70
  return (
35
- <div
36
- key={index}
37
- tabIndex={props.tabIndex ? props.tabIndex + index : index}
38
- onClick={() => {
39
- props.onChange(option.value);
40
- }}
41
- onKeyDown={(e: React.KeyboardEvent) => {
42
- if (e.key === "Enter" || e.key === " ") {
43
- e.preventDefault();
44
- props.onChange(option.value);
45
- }
46
- }}
47
- className={`relative flex cursor-pointer rounded-lg border p-4 shadow-sm focus:outline-none transition-all duration-200 hover:border-indigo-400 hover:shadow-md ${
48
- isSelected
49
- ? "border-indigo-500 bg-indigo-50/50"
50
- : "border-gray-200 bg-white"
51
- }`}
52
- role="radio"
53
- aria-checked={isSelected}
54
- data-testid={`card-select-option-${option.value}`}
55
- >
56
- <div className="flex w-full items-start">
57
- <div
58
- className={`flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg ${
59
- isSelected ? "bg-indigo-100" : "bg-gray-100"
60
- }`}
61
- >
62
- <Icon
63
- icon={option.icon}
64
- size={SizeProp.Large}
65
- className={`h-5 w-5 ${
66
- isSelected ? "text-indigo-600" : "text-gray-600"
67
- }`}
68
- />
69
- </div>
70
- <div className="ml-4 flex-1">
71
- <span
72
- className={`block text-sm font-semibold ${
73
- isSelected ? "text-gray-900" : "text-gray-900"
74
- }`}
71
+ <div key={groupIndex} className={groupIndex > 0 ? "mt-8" : ""}>
72
+ {group.label && (
73
+ <div className="relative mb-4">
74
+ <div
75
+ className="absolute inset-0 flex items-center"
76
+ aria-hidden="true"
75
77
  >
76
- {option.title}
77
- </span>
78
- <span
79
- className={`mt-1 block text-sm ${
80
- isSelected ? "text-gray-600" : "text-gray-500"
81
- }`}
82
- >
83
- {option.description}
84
- </span>
85
- </div>
86
- {isSelected && (
87
- <div className="flex-shrink-0 ml-2">
88
- <Icon
89
- icon={IconProp.CheckCircle}
90
- size={SizeProp.Large}
91
- className="h-5 w-5 text-indigo-500"
92
- />
78
+ <div className="w-full border-t border-gray-200"></div>
79
+ </div>
80
+ <div className="relative flex justify-start">
81
+ <span className="bg-white pr-3 text-xs font-semibold uppercase tracking-wider text-gray-400">
82
+ {group.label}
83
+ </span>
93
84
  </div>
94
- )}
85
+ </div>
86
+ )}
87
+ <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
88
+ {group.options.map((option: CardSelectOption) => {
89
+ const isSelected: boolean = props.value === option.value;
90
+ const currentIndex: number = cardIndex++;
91
+
92
+ return (
93
+ <div
94
+ key={option.value}
95
+ tabIndex={
96
+ props.tabIndex
97
+ ? props.tabIndex + currentIndex
98
+ : currentIndex
99
+ }
100
+ onClick={() => {
101
+ props.onChange(option.value);
102
+ }}
103
+ onKeyDown={(e: React.KeyboardEvent) => {
104
+ if (e.key === "Enter" || e.key === " ") {
105
+ e.preventDefault();
106
+ props.onChange(option.value);
107
+ }
108
+ }}
109
+ className={`relative flex cursor-pointer rounded-lg border p-4 shadow-sm focus:outline-none transition-all duration-200 hover:border-indigo-400 hover:shadow-md ${
110
+ isSelected
111
+ ? "border-indigo-500 bg-indigo-50/50"
112
+ : "border-gray-200 bg-white"
113
+ }`}
114
+ role="radio"
115
+ aria-checked={isSelected}
116
+ data-testid={`card-select-option-${option.value}`}
117
+ >
118
+ <div className="flex w-full items-start">
119
+ <div
120
+ className={`flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg ${
121
+ isSelected ? "bg-indigo-100" : "bg-gray-100"
122
+ }`}
123
+ >
124
+ <Icon
125
+ icon={option.icon}
126
+ size={SizeProp.Large}
127
+ className={`h-5 w-5 ${
128
+ isSelected ? "text-indigo-600" : "text-gray-600"
129
+ }`}
130
+ />
131
+ </div>
132
+ <div className="ml-4 flex-1">
133
+ <span
134
+ className={`block text-sm font-semibold ${
135
+ isSelected ? "text-gray-900" : "text-gray-900"
136
+ }`}
137
+ >
138
+ {option.title}
139
+ </span>
140
+ <span
141
+ className={`mt-1 block text-sm ${
142
+ isSelected ? "text-gray-600" : "text-gray-500"
143
+ }`}
144
+ >
145
+ {option.description}
146
+ </span>
147
+ </div>
148
+ {isSelected && (
149
+ <div className="flex-shrink-0 ml-2">
150
+ <Icon
151
+ icon={IconProp.CheckCircle}
152
+ size={SizeProp.Large}
153
+ className="h-5 w-5 text-indigo-500"
154
+ />
155
+ </div>
156
+ )}
157
+ </div>
158
+ </div>
159
+ );
160
+ })}
95
161
  </div>
96
162
  </div>
97
163
  );
@@ -3,7 +3,10 @@ import {
3
3
  CategoryCheckboxOption,
4
4
  CheckboxCategory,
5
5
  } from "../../CategoryCheckbox/CategoryCheckboxTypes";
6
- import { CardSelectOption } from "../../CardSelect/CardSelect";
6
+ import {
7
+ CardSelectOption,
8
+ CardSelectOptionGroup,
9
+ } from "../../CardSelect/CardSelect";
7
10
  import { DropdownOption, DropdownOptionGroup } from "../../Dropdown/Dropdown";
8
11
  import { RadioButton } from "../../RadioButtons/GroupRadioButtons";
9
12
  import FormFieldSchemaType from "./FormFieldSchemaType";
@@ -51,7 +54,9 @@ export default interface Field<TEntity> {
51
54
  stepId?: string | undefined;
52
55
  required?: boolean | ((item: FormValues<TEntity>) => boolean) | undefined;
53
56
  dropdownOptions?: Array<DropdownOption | DropdownOptionGroup> | undefined;
54
- cardSelectOptions?: Array<CardSelectOption> | undefined;
57
+ cardSelectOptions?:
58
+ | Array<CardSelectOption | CardSelectOptionGroup>
59
+ | undefined;
55
60
  fetchDropdownOptions?:
56
61
  | ((
57
62
  item: FormValues<TEntity>,
@@ -8,8 +8,63 @@ import React, {
8
8
  import ReactMarkdown from "react-markdown";
9
9
  // https://github.com/remarkjs/remark-gfm
10
10
  import remarkGfm from "remark-gfm";
11
- import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
11
+ import SyntaxHighlighter from "react-syntax-highlighter/dist/esm/prism-light";
12
12
  import { a11yDark } from "react-syntax-highlighter/dist/esm/styles/prism";
13
+ import javascript from "react-syntax-highlighter/dist/esm/languages/prism/javascript";
14
+ import typescript from "react-syntax-highlighter/dist/esm/languages/prism/typescript";
15
+ import jsx from "react-syntax-highlighter/dist/esm/languages/prism/jsx";
16
+ import tsx from "react-syntax-highlighter/dist/esm/languages/prism/tsx";
17
+ import python from "react-syntax-highlighter/dist/esm/languages/prism/python";
18
+ import bash from "react-syntax-highlighter/dist/esm/languages/prism/bash";
19
+ import json from "react-syntax-highlighter/dist/esm/languages/prism/json";
20
+ import yaml from "react-syntax-highlighter/dist/esm/languages/prism/yaml";
21
+ import sql from "react-syntax-highlighter/dist/esm/languages/prism/sql";
22
+ import go from "react-syntax-highlighter/dist/esm/languages/prism/go";
23
+ import java from "react-syntax-highlighter/dist/esm/languages/prism/java";
24
+ import css from "react-syntax-highlighter/dist/esm/languages/prism/css";
25
+ import markup from "react-syntax-highlighter/dist/esm/languages/prism/markup";
26
+ import markdown from "react-syntax-highlighter/dist/esm/languages/prism/markdown";
27
+ import docker from "react-syntax-highlighter/dist/esm/languages/prism/docker";
28
+ import rust from "react-syntax-highlighter/dist/esm/languages/prism/rust";
29
+ import c from "react-syntax-highlighter/dist/esm/languages/prism/c";
30
+ import cpp from "react-syntax-highlighter/dist/esm/languages/prism/cpp";
31
+ import csharp from "react-syntax-highlighter/dist/esm/languages/prism/csharp";
32
+ import ruby from "react-syntax-highlighter/dist/esm/languages/prism/ruby";
33
+ import php from "react-syntax-highlighter/dist/esm/languages/prism/php";
34
+ import graphql from "react-syntax-highlighter/dist/esm/languages/prism/graphql";
35
+ import http from "react-syntax-highlighter/dist/esm/languages/prism/http";
36
+
37
+ SyntaxHighlighter.registerLanguage("javascript", javascript);
38
+ SyntaxHighlighter.registerLanguage("js", javascript);
39
+ SyntaxHighlighter.registerLanguage("typescript", typescript);
40
+ SyntaxHighlighter.registerLanguage("ts", typescript);
41
+ SyntaxHighlighter.registerLanguage("jsx", jsx);
42
+ SyntaxHighlighter.registerLanguage("tsx", tsx);
43
+ SyntaxHighlighter.registerLanguage("python", python);
44
+ SyntaxHighlighter.registerLanguage("bash", bash);
45
+ SyntaxHighlighter.registerLanguage("shell", bash);
46
+ SyntaxHighlighter.registerLanguage("json", json);
47
+ SyntaxHighlighter.registerLanguage("yaml", yaml);
48
+ SyntaxHighlighter.registerLanguage("yml", yaml);
49
+ SyntaxHighlighter.registerLanguage("sql", sql);
50
+ SyntaxHighlighter.registerLanguage("go", go);
51
+ SyntaxHighlighter.registerLanguage("java", java);
52
+ SyntaxHighlighter.registerLanguage("css", css);
53
+ SyntaxHighlighter.registerLanguage("markup", markup);
54
+ SyntaxHighlighter.registerLanguage("html", markup);
55
+ SyntaxHighlighter.registerLanguage("xml", markup);
56
+ SyntaxHighlighter.registerLanguage("markdown", markdown);
57
+ SyntaxHighlighter.registerLanguage("md", markdown);
58
+ SyntaxHighlighter.registerLanguage("docker", docker);
59
+ SyntaxHighlighter.registerLanguage("dockerfile", docker);
60
+ SyntaxHighlighter.registerLanguage("rust", rust);
61
+ SyntaxHighlighter.registerLanguage("c", c);
62
+ SyntaxHighlighter.registerLanguage("cpp", cpp);
63
+ SyntaxHighlighter.registerLanguage("csharp", csharp);
64
+ SyntaxHighlighter.registerLanguage("ruby", ruby);
65
+ SyntaxHighlighter.registerLanguage("php", php);
66
+ SyntaxHighlighter.registerLanguage("graphql", graphql);
67
+ SyntaxHighlighter.registerLanguage("http", http);
13
68
  import mermaid from "mermaid";
14
69
 
15
70
  // Initialize mermaid
@@ -39,6 +39,54 @@ function createRefractorCompatibilityPlugin() {
39
39
  };
40
40
  }
41
41
 
42
+ // Plugin to force mermaid to use its pre-bundled CJS build (no dynamic imports)
43
+ function createMermaidPlugin() {
44
+ const candidateRoots = [
45
+ path.resolve(__dirname, '../node_modules/mermaid'),
46
+ path.resolve(__dirname, '../../node_modules/mermaid'),
47
+ ];
48
+ const mermaidRoot = candidateRoots.find((p) => fs.existsSync(p));
49
+
50
+ return {
51
+ name: 'mermaid-prebundled',
52
+ setup(build) {
53
+ if (!mermaidRoot) return;
54
+ const bundlePath = path.join(mermaidRoot, 'dist', 'mermaid.min.js');
55
+
56
+ // Intercept bare "mermaid" imports and serve the pre-bundled CJS file
57
+ // with an ESM export appended. The CJS file declares a local var
58
+ // __esbuild_esm_mermaid_nm and assigns .mermaid on it, so we inline
59
+ // the file contents and export from the same scope.
60
+ build.onResolve({ filter: /^mermaid$/ }, () => {
61
+ return { path: 'mermaid-wrapper', namespace: 'mermaid-ns' };
62
+ });
63
+
64
+ build.onLoad({ filter: /^mermaid-wrapper$/, namespace: 'mermaid-ns' }, () => {
65
+ let cjsSource = fs.readFileSync(bundlePath, 'utf8');
66
+ // The CJS bundle ends with a line that tries globalThis.__esbuild_esm_mermaid_nm
67
+ // which fails because the var is local-scoped when bundled. Strip it and
68
+ // expose the local var on globalThis ourselves before that line.
69
+ cjsSource = cjsSource.replace(
70
+ /globalThis\["mermaid"\]\s*=\s*globalThis\.__esbuild_esm_mermaid_nm\["mermaid"\]\.default;?\s*$/,
71
+ ''
72
+ );
73
+ const contents = cjsSource + `
74
+ ;globalThis.__esbuild_esm_mermaid_nm = typeof __esbuild_esm_mermaid_nm !== "undefined" ? __esbuild_esm_mermaid_nm : {};
75
+ var _mermaid_export = __esbuild_esm_mermaid_nm.mermaid;
76
+ if (_mermaid_export && _mermaid_export.default) { _mermaid_export = _mermaid_export.default; }
77
+ export default _mermaid_export;
78
+ export { _mermaid_export as mermaid };
79
+ `;
80
+ return {
81
+ contents,
82
+ loader: 'js',
83
+ resolveDir: path.dirname(bundlePath),
84
+ };
85
+ });
86
+ },
87
+ };
88
+ }
89
+
42
90
  // CSS Plugin to handle CSS/SCSS files
43
91
  function createCSSPlugin() {
44
92
  return {
@@ -161,12 +209,13 @@ function createConfig(options) {
161
209
  entryPoints: [entryPoint],
162
210
  bundle: true,
163
211
  outdir,
164
- format: 'esm', // Changed from 'iife' to 'esm' to support splitting
212
+ format: 'esm',
165
213
  platform: 'browser',
166
214
  target: 'es2017',
167
215
  sourcemap: isDev ? 'inline' : false,
168
216
  minify: false,
169
- splitting: true, // Now supported with ESM format
217
+ treeShaking: true,
218
+ splitting: true,
170
219
  publicPath,
171
220
  define: {
172
221
  'process.env.NODE_ENV': JSON.stringify(isDev ? 'development' : 'production'),
@@ -177,7 +226,7 @@ function createConfig(options) {
177
226
  'react': path.resolve('./node_modules/react'),
178
227
  ...additionalAlias,
179
228
  },
180
- plugins: [createRefractorCompatibilityPlugin(), createCSSPlugin(), createFileLoaderPlugin()],
229
+ plugins: [createMermaidPlugin(), createRefractorCompatibilityPlugin(), createCSSPlugin(), createFileLoaderPlugin()],
181
230
  loader: {
182
231
  '.tsx': 'tsx',
183
232
  '.ts': 'ts',
package/UI/index.d.ts CHANGED
@@ -2,3 +2,29 @@ declare module "*.png";
2
2
  declare module "*.svg";
3
3
  declare module "*.jpg";
4
4
  declare module "*.gif";
5
+
6
+ declare module "react-syntax-highlighter/dist/esm/prism-light";
7
+ declare module "react-syntax-highlighter/dist/esm/styles/prism";
8
+ declare module "react-syntax-highlighter/dist/esm/languages/prism/javascript";
9
+ declare module "react-syntax-highlighter/dist/esm/languages/prism/typescript";
10
+ declare module "react-syntax-highlighter/dist/esm/languages/prism/jsx";
11
+ declare module "react-syntax-highlighter/dist/esm/languages/prism/tsx";
12
+ declare module "react-syntax-highlighter/dist/esm/languages/prism/python";
13
+ declare module "react-syntax-highlighter/dist/esm/languages/prism/bash";
14
+ declare module "react-syntax-highlighter/dist/esm/languages/prism/json";
15
+ declare module "react-syntax-highlighter/dist/esm/languages/prism/yaml";
16
+ declare module "react-syntax-highlighter/dist/esm/languages/prism/sql";
17
+ declare module "react-syntax-highlighter/dist/esm/languages/prism/go";
18
+ declare module "react-syntax-highlighter/dist/esm/languages/prism/java";
19
+ declare module "react-syntax-highlighter/dist/esm/languages/prism/css";
20
+ declare module "react-syntax-highlighter/dist/esm/languages/prism/markup";
21
+ declare module "react-syntax-highlighter/dist/esm/languages/prism/markdown";
22
+ declare module "react-syntax-highlighter/dist/esm/languages/prism/docker";
23
+ declare module "react-syntax-highlighter/dist/esm/languages/prism/rust";
24
+ declare module "react-syntax-highlighter/dist/esm/languages/prism/c";
25
+ declare module "react-syntax-highlighter/dist/esm/languages/prism/cpp";
26
+ declare module "react-syntax-highlighter/dist/esm/languages/prism/csharp";
27
+ declare module "react-syntax-highlighter/dist/esm/languages/prism/ruby";
28
+ declare module "react-syntax-highlighter/dist/esm/languages/prism/php";
29
+ declare module "react-syntax-highlighter/dist/esm/languages/prism/graphql";
30
+ declare module "react-syntax-highlighter/dist/esm/languages/prism/http";
@@ -33,6 +33,7 @@ let GlobalConfig = class GlobalConfig extends GlobalConfigModel {
33
33
  constructor() {
34
34
  super(...arguments);
35
35
  this.disableSignup = undefined;
36
+ this.disableUserProjectCreation = undefined;
36
37
  // SMTP Settings.
37
38
  this.isSMTPSecure = undefined;
38
39
  this.smtpUsername = undefined;
@@ -86,6 +87,26 @@ __decorate([
86
87
  }),
87
88
  __metadata("design:type", Boolean)
88
89
  ], GlobalConfig.prototype, "disableSignup", void 0);
90
+ __decorate([
91
+ ColumnAccessControl({
92
+ create: [],
93
+ read: [],
94
+ update: [],
95
+ }),
96
+ TableColumn({
97
+ type: TableColumnType.Boolean,
98
+ title: "Disable User Project Creation",
99
+ description: "Only master admins can create projects when enabled.",
100
+ defaultValue: false,
101
+ }),
102
+ Column({
103
+ type: ColumnType.Boolean,
104
+ nullable: true,
105
+ default: false,
106
+ unique: true,
107
+ }),
108
+ __metadata("design:type", Boolean)
109
+ ], GlobalConfig.prototype, "disableUserProjectCreation", void 0);
89
110
  __decorate([
90
111
  ColumnAccessControl({
91
112
  create: [],