@postgres.ai/shared 4.2.0-pr-1203.1 → 4.2.0-pr-1201.1

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 (51) hide show
  1. package/components/Button/index.js +0 -1
  2. package/components/Button2/index.js +0 -1
  3. package/components/Status/index.js +0 -1
  4. package/components/TextField/index.js +1 -1
  5. package/config/index.js +1 -1
  6. package/helpers/getEntropy.js +2 -2
  7. package/helpers/request.js +1 -1
  8. package/icons/ArrowDropDown/index.js +3 -1
  9. package/icons/Renewable/index.js +0 -1
  10. package/icons/Shield/index.js +0 -1
  11. package/icons/Warning/index.js +3 -1
  12. package/package.json +1 -1
  13. package/pages/Branches/index.js +2 -4
  14. package/pages/Clone/Status/index.js +0 -1
  15. package/pages/Clone/index.js +3 -3
  16. package/pages/CreateClone/index.js +1 -1
  17. package/pages/CreateClone/utils/index.js +1 -1
  18. package/pages/Instance/Configuration/Header/index.js +2 -2
  19. package/pages/Instance/Configuration/PhysicalMode/EnvsEditor/index.d.ts +0 -2
  20. package/pages/Instance/Configuration/PhysicalMode/EnvsEditor/index.js +1 -8
  21. package/pages/Instance/Configuration/index.js +15 -22
  22. package/pages/Instance/Configuration/tooltipText.js +4 -4
  23. package/pages/Instance/Configuration/useForm.js +1 -1
  24. package/pages/Instance/Configuration/utils/index.d.ts +1 -1
  25. package/pages/Instance/Configuration/utils/index.js +16 -16
  26. package/pages/Instance/Tabs/PlatformTabs.d.ts +2 -2
  27. package/pages/Instance/Tabs/PlatformTabs.js +1 -1
  28. package/pages/Instance/Tabs/index.d.ts +1 -1
  29. package/pages/Instance/index.js +2 -2
  30. package/pages/Instance/stores/Main.js +5 -5
  31. package/pages/Logs/hooks/useWsScroll.js +14 -22
  32. package/pages/Logs/index.js +18 -14
  33. package/pages/Logs/utils/index.d.ts +0 -2
  34. package/pages/Logs/utils/index.js +1 -20
  35. package/pages/Logs/wsLogs.d.ts +0 -1
  36. package/pages/Logs/wsLogs.js +5 -32
  37. package/pages/Snapshots/Snapshot/stores/Main.js +1 -2
  38. package/styles/icons.js +1 -1
  39. package/styles/styles.js +0 -3
  40. package/styles/theme.js +2 -1
  41. package/types/api/endpoints/getFullConfig.d.ts +1 -1
  42. package/types/api/endpoints/testDbSource.js +2 -2
  43. package/types/api/entities/clone.d.ts +0 -9
  44. package/types/api/entities/clone.js +0 -1
  45. package/types/api/entities/config.d.ts +1 -1
  46. package/types/api/entities/instance.d.ts +0 -5
  47. package/types/api/entities/instanceRetrieval.d.ts +2 -2
  48. package/types/api/entities/instanceState.d.ts +0 -5
  49. package/utils/api.js +2 -2
  50. package/components/ErrorBoundary/index.d.ts +0 -15
  51. package/components/ErrorBoundary/index.js +0 -24
@@ -38,4 +38,3 @@ export const Button = forwardRef((props, ref) => {
38
38
  const classes = useStyles();
39
39
  return (_jsx(ButtonBase, { ...buttonProps, size: size, ref: ref, disabled: isDisabled, className: clsx(classes.root, className), variant: VARIANT_MAP[variant], color: "primary" }));
40
40
  });
41
- Button.displayName = 'Button';
@@ -8,4 +8,3 @@ export const Button = React.forwardRef((props, ref) => {
8
8
  const isDisabled = props.isDisabled || props.isLoading;
9
9
  return (_jsxs("button", { ref: ref, className: cn(styles.root, styles[size], styles[theme], props.className), type: type, onClick: props.onClick, disabled: isDisabled, children: [props.children, props.isLoading && _jsx(Spinner, { size: "sm", className: styles.spinner })] }));
10
10
  });
11
- Button.displayName = 'Button2';
@@ -21,4 +21,3 @@ export const Status = React.forwardRef((props, ref) => {
21
21
  const { type = 'ok', children = type, icon = TYPE_TO_ICON[type], className, classNameIcon, disableColor, ...hiddenProps } = props;
22
22
  return (_jsxs("span", { ...hiddenProps, className: clsx(styles.root, !disableColor && styles[type], className), ref: ref, children: [icon && (_jsxs("span", { className: clsx(styles.iconContainer, classNameIcon), children: [icon, "\u2009"] })), children] }));
23
23
  });
24
- Status.displayName = 'Status';
@@ -39,5 +39,5 @@ export const TextField = (props) => {
39
39
  classes: {
40
40
  root: classes.helperText
41
41
  }
42
- }, onChange: props.onChange, select: props.select, type: props.type, error: props.error, placeholder: props.placeholder, onBlur: props.onBlur, onFocus: props.onFocus, name: props.name, helperText: props.helperText, style: props.style, children: props.children }));
42
+ }, onChange: props.onChange, children: props.children, select: props.select, type: props.type, error: props.error, placeholder: props.placeholder, onBlur: props.onBlur, onFocus: props.onFocus, name: props.name, helperText: props.helperText, style: props.style }));
43
43
  };
package/config/index.js CHANGED
@@ -14,7 +14,7 @@ const loadDateFnsLocale = async () => {
14
14
  config.dateFnsLocale = locale.default;
15
15
  return;
16
16
  }
17
- catch {
17
+ catch (e) {
18
18
  // Unavailable locale.
19
19
  }
20
20
  };
@@ -6,7 +6,7 @@ const upperChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
6
6
  const digitsChars = '0123456789';
7
7
  export const MIN_ENTROPY = 60;
8
8
  function getBase(password) {
9
- const uniqueChars = [];
9
+ let uniqueChars = [];
10
10
  for (const c of password) {
11
11
  if (!uniqueChars.includes(c)) {
12
12
  uniqueChars.push(c);
@@ -71,7 +71,7 @@ const seqKeyboard2 = 'zxcvbnm';
71
71
  const seqAlphabet = 'abcdefghijklmnopqrstuvwxyz';
72
72
  function removeMoreThanTwoFromSequence(s, seq) {
73
73
  const seqRunes = Array.from(seq);
74
- const runes = Array.from(s);
74
+ let runes = Array.from(s);
75
75
  let matches = 0;
76
76
  for (let i = 0; i < runes.length; i++) {
77
77
  for (let j = 0; j < seqRunes.length; j++) {
@@ -26,7 +26,7 @@ export const request = async (path, options) => {
26
26
  },
27
27
  });
28
28
  }
29
- catch {
29
+ catch (e) {
30
30
  return new Response(null, {
31
31
  status: 500,
32
32
  statusText: `Unknown error`,
@@ -1,4 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  export const ArrowDropDownIcon = (props) => {
3
- return (_jsx("svg", { className: props.className, viewBox: "0 0 8 6", fill: "none", xmlns: "http://www.w3.org/2000/svg", onClick: props.onClick, children: _jsx("path", { d: "M7.8515 0.898419C7.75261 0.799446 7.63538 0.75 7.49994 0.75H0.500038C0.364534 0.75 0.247392 0.799446 0.148419 0.898419C0.0494455 0.997501 0 1.11464 0 1.25006C0 1.38546 0.0494455 1.5026 0.148419 1.6016L3.64838 5.10156C3.74746 5.20054 3.86461 5.25009 4 5.25009C4.13539 5.25009 4.25265 5.20054 4.35154 5.10156L7.8515 1.60157C7.95036 1.5026 8 1.38546 8 1.25004C8 1.11464 7.95036 0.997501 7.8515 0.898419Z", fill: "currentColor" }) }));
3
+ return (_jsx("svg", { className: props.className, viewBox: "0 0 8 6", fill: "none", xmlns: "http://www.w3.org/2000/svg", onClick: props.onClick, children: _jsx("path", {
4
+ // eslint-disable-next-line max-len
5
+ d: "M7.8515 0.898419C7.75261 0.799446 7.63538 0.75 7.49994 0.75H0.500038C0.364534 0.75 0.247392 0.799446 0.148419 0.898419C0.0494455 0.997501 0 1.11464 0 1.25006C0 1.38546 0.0494455 1.5026 0.148419 1.6016L3.64838 5.10156C3.74746 5.20054 3.86461 5.25009 4 5.25009C4.13539 5.25009 4.25265 5.20054 4.35154 5.10156L7.8515 1.60157C7.95036 1.5026 8 1.38546 8 1.25004C8 1.11464 7.95036 0.997501 7.8515 0.898419Z", fill: "currentColor" }) }));
4
6
  };
@@ -10,4 +10,3 @@ export const RenewableIcon = React.forwardRef((props, ref) => {
10
10
  const { className, ...hiddenProps } = props;
11
11
  return (_jsxs("svg", { ...hiddenProps, ref: ref, className: className, viewBox: "0 0 10 10", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [_jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M6.14337 0L8.19809 2.9755L4.59387 3.26718L5.16139 2.07055C4.22847 2.10806 3.3161 2.48386 2.66614 3.18795L2.20594 2.76312C3.05679 1.84141 4.26977 1.40592 5.45883 1.44339L6.14337 0Z", fill: "currentColor" }), _jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M9.7343 9.13905L6.12728 9.39371L7.71025 6.14261L8.45158 7.24009C8.89401 6.4179 9.03479 5.44126 8.75942 4.52346L9.35931 4.34347C9.71979 5.54495 9.47743 6.81074 8.8401 7.81527L9.7343 9.13905Z", fill: "currentColor" }), _jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M0 7.57495L1.61898 4.34163L3.60962 7.36037L2.28757 7.43896C2.76953 8.23862 3.53811 8.85742 4.46813 9.08819L4.3173 9.69607C3.09983 9.39397 2.13422 8.54041 1.59468 7.48015L0 7.57495Z", fill: "currentColor" }), _jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M6.26612 2.51078C5.07868 2.17637 3.73747 2.48899 2.89616 3.40036L1.97575 2.55072C3.17071 1.25624 5.01514 0.857134 6.60567 1.30506L6.26612 2.51078Z", fill: "currentColor" }), _jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M7.33938 8.11969C8.34588 7.28131 8.83847 5.87691 8.45939 4.61345L9.65917 4.25348C10.1969 6.04581 9.49312 7.95595 8.14107 9.08215L7.33938 8.11969Z", fill: "currentColor" }), _jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M2.07072 5.82736C2.20416 7.20026 3.19328 8.44922 4.54348 8.78425L4.24181 10C2.32911 9.5254 1.00387 7.79934 0.823976 5.94853L2.07072 5.82736Z", fill: "currentColor" })] }));
12
12
  });
13
- RenewableIcon.displayName = 'RenewableIcon';
@@ -10,4 +10,3 @@ export const ShieldIcon = React.forwardRef((props, ref) => {
10
10
  const { className, ...hiddenProps } = props;
11
11
  return (_jsx("svg", { ...hiddenProps, ref: ref, className: className, viewBox: "0 0 8 10", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { d: "M4.00195 0L0.251953 1.66667V4.16667C0.251953 6.47917 1.85195 8.64167 4.00195 9.16667C6.15195 8.64167 7.75195 6.47917 7.75195 4.16667V1.66667L4.00195 0ZM3.16862 6.66667L1.50195 5L2.08945 4.4125L3.16862 5.4875L5.91445 2.74167L6.50195 3.33333L3.16862 6.66667Z", fill: "currentColor" }) }));
12
12
  });
13
- ShieldIcon.displayName = 'ShieldIcon';
@@ -1,4 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  export const WarningIcon = (props) => {
3
- return (_jsx("svg", { className: props.className, viewBox: "0 0 10 10", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { d: "M9.79222 7.49885L6.2597 1.00518C5.69214 0.04969 4.30861 0.0484205 3.74029 1.00518L0.207947 7.49885C-0.372248 8.4752 0.330193 9.71157 1.46736 9.71157H8.53251C9.66873 9.71157 10.3724 8.47619 9.79222 7.49885ZM5 8.53969C4.67699 8.53969 4.41406 8.27676 4.41406 7.95375C4.41406 7.63074 4.67699 7.36782 5 7.36782C5.323 7.36782 5.58593 7.63074 5.58593 7.95375C5.58593 8.27676 5.323 8.53969 5 8.53969ZM5.58593 6.19594C5.58593 6.51895 5.323 6.78188 5 6.78188C4.67699 6.78188 4.41406 6.51895 4.41406 6.19594V3.26625C4.41406 2.94324 4.67699 2.68032 5 2.68032C5.323 2.68032 5.58593 2.94324 5.58593 3.26625V6.19594Z", fill: "currentColor" }) }));
3
+ return (_jsx("svg", { className: props.className, viewBox: "0 0 10 10", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", {
4
+ // eslint-disable-next-line max-len
5
+ d: "M9.79222 7.49885L6.2597 1.00518C5.69214 0.04969 4.30861 0.0484205 3.74029 1.00518L0.207947 7.49885C-0.372248 8.4752 0.330193 9.71157 1.46736 9.71157H8.53251C9.66873 9.71157 10.3724 8.47619 9.79222 7.49885ZM5 8.53969C4.67699 8.53969 4.41406 8.27676 4.41406 7.95375C4.41406 7.63074 4.67699 7.36782 5 7.36782C5.323 7.36782 5.58593 7.63074 5.58593 7.95375C5.58593 8.27676 5.323 8.53969 5 8.53969ZM5.58593 6.19594C5.58593 6.51895 5.323 6.78188 5 6.78188C4.67699 6.78188 4.41406 6.51895 4.41406 6.19594V3.26625C4.41406 2.94324 4.67699 2.68032 5 2.68032C5.323 2.68032 5.58593 2.94324 5.58593 3.26625V6.19594Z", fill: "currentColor" }) }));
4
6
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@postgres.ai/shared",
3
- "version": "4.2.0-pr-1203.1",
3
+ "version": "4.2.0-pr-1201.1",
4
4
  "main": "index.js",
5
5
  "types": "index.d.ts",
6
6
  "peerDependencies": {
@@ -48,8 +48,7 @@ export const Branches = observer(({ instanceId }) => {
48
48
  const loadBranches = () => {
49
49
  getBranches(instanceId)
50
50
  .then((response) => {
51
- if (response)
52
- setBranches(response);
51
+ response && setBranches(response);
53
52
  })
54
53
  .finally(() => setIsLoading(false));
55
54
  };
@@ -57,8 +56,7 @@ export const Branches = observer(({ instanceId }) => {
57
56
  loadBranches();
58
57
  const intervalId = setInterval(() => {
59
58
  getBranches(instanceId, true).then((response) => {
60
- if (response)
61
- setBranches(response);
59
+ response && setBranches(response);
62
60
  });
63
61
  }, listRefreshIntervalMs);
64
62
  return () => clearInterval(intervalId);
@@ -39,4 +39,3 @@ export const Status = React.memo((props) => {
39
39
  const isError = statusType === 'error';
40
40
  return (_jsxs("div", { className: clsx(classes.root, className), children: [_jsx(StatusBase, { type: statusType, className: classes.status, children: statusText }), !isError && _jsx("p", { className: classes.message, children: message }), isError && (_jsx(FormattedText, { value: message, className: classes.errorMessage }))] }));
41
41
  });
42
- Status.displayName = 'CloneStatus';
@@ -274,21 +274,21 @@ export const Clone = observer((props) => {
274
274
  : '-'] }), _jsxs("p", { className: classes.text, children: [_jsx("span", { className: classes.paramTitle, children: "Clone creation time:" }), clone.metadata.cloningTime
275
275
  ? `${round(clone.metadata.cloningTime, 2)} s`
276
276
  : '-'] })] }), _jsx("br", {}), hasConnectionInfo && (_jsxs(_Fragment, { children: [_jsx("p", { children: _jsx("strong", { children: "Connection info" }) }), sshPortForwardingUrl && (_jsxs("div", { className: classes.fieldBlock, children: ["In a separate console, set up SSH port forwarding (and keep it running):", _jsxs("div", { className: classes.copyFieldContainer, children: [_jsx(TextField, { variant: "outlined", label: "SSH port forwarding", value: sshPortForwardingUrl, className: classes.textField, margin: "normal", fullWidth: true,
277
- // @ts-expect-error TextField forwards readOnly to the input but does not declare it
277
+ // @ts-ignore
278
278
  readOnly: true, InputLabelProps: {
279
279
  shrink: true,
280
280
  style: styles.inputFieldLabel,
281
281
  }, FormHelperTextProps: {
282
282
  style: styles.inputFieldHelper,
283
283
  } }), _jsx(IconButton, { className: classes.copyButton, "aria-label": "Copy", onClick: () => copyToClipboard(sshPortForwardingUrl), children: icons.copyIcon })] })] })), psqlConnectionStr && (_jsxs("div", { className: classes.fieldBlock, children: [_jsxs("div", { className: classes.copyFieldContainer, children: [_jsx(TextField, { variant: "outlined", id: "psqlConnStr", label: "psql connection string", value: psqlConnectionStr, className: classes.textField, margin: "normal", fullWidth: true,
284
- // @ts-expect-error TextField forwards readOnly to the input but does not declare it
284
+ // @ts-ignore
285
285
  readOnly: true, InputLabelProps: {
286
286
  shrink: true,
287
287
  style: styles.inputFieldLabel,
288
288
  }, FormHelperTextProps: {
289
289
  style: styles.inputFieldHelper,
290
290
  } }), _jsx(IconButton, { className: classes.copyButton, "aria-label": "Copy", onClick: () => copyToClipboard(psqlConnectionStr), children: icons.copyIcon })] }), "\u00A0", _jsx(Tooltip, { content: _jsx(_Fragment, { children: "Used to connect to PostgreSQL using psql. Change DBNAME to the name of the database you want to connect to. Use the PGPASSWORD environment variable to set the database password or type it when prompted." }), children: _jsx("span", { className: classes.textFieldInfo, children: icons.infoIcon }) })] })), jdbcConnectionStr && (_jsxs("div", { className: classes.fieldBlock, children: [_jsxs("div", { className: classes.copyFieldContainer, children: [_jsx(TextField, { variant: "outlined", label: "JDBC connection string", value: jdbcConnectionStr, className: classes.textField, margin: "normal", fullWidth: true,
291
- // @ts-expect-error TextField forwards readOnly to the input but does not declare it
291
+ // @ts-ignore
292
292
  readOnly: true, InputLabelProps: {
293
293
  shrink: true,
294
294
  style: styles.inputFieldLabel,
@@ -96,7 +96,7 @@ export const CreateClone = observer((props) => {
96
96
  const allSnapshots = (_f = (_e = (_d = stores.main) === null || _d === void 0 ? void 0 : _d.snapshots) === null || _e === void 0 ? void 0 : _e.data) !== null && _f !== void 0 ? _f : [];
97
97
  const sortedSnapshots = allSnapshots.slice().sort(compareSnapshotsDesc);
98
98
  setSnapshots(sortedSnapshots);
99
- const selectedSnapshot = allSnapshots.find(s => s.id === initialSnapshotId) || allSnapshots[0];
99
+ let selectedSnapshot = allSnapshots.find(s => s.id === initialSnapshotId) || allSnapshots[0];
100
100
  formik.setFieldValue('snapshotId', selectedSnapshot === null || selectedSnapshot === void 0 ? void 0 : selectedSnapshot.id);
101
101
  }
102
102
  }
@@ -21,7 +21,7 @@ export const getCliCreateCloneCommand = (values, showPassword) => {
21
21
 
22
22
  ${protectedFlag} \
23
23
 
24
- --id ${cloneIdDisplay}`;
24
+ --id ${cloneIdDisplay} \ `;
25
25
  };
26
26
  export const getCliCloneStatus = (cloneId) => {
27
27
  const cloneIdDisplay = cloneId ? shellEscape(cloneId) : `<CLONE_ID>`;
@@ -13,5 +13,5 @@ import { ExternalIcon } from '@postgres.ai/shared/icons/External';
13
13
  import styles from '../styles.module.scss';
14
14
  export const ConfigSectionTitle = ({ tag }) => (_jsx(SectionTitle, { level: 2, tag: "h2", text: _jsxs("div", { className: styles.sectionTitle, children: [_jsx("p", { children: "Section" }), _jsxs("p", { children: ["\"", tag, "\""] })] }) }));
15
15
  const DOCS_URL = 'https://postgres.ai/docs/reference-guides/database-lab-engine-configuration-reference';
16
- export const Header = (props) => (_jsxs("div", { className: styles.root, children: [_jsxs(Box, { mb: 3, children: [_jsx(Typography, { paragraph: true, children: "Only select parameters can be changed here." }), _jsxs(Typography, { paragraph: true, children: ["However, you can still see", ' ', _jsx(Link, { href: "#", underline: "always", onClick: props.setOpen, className: styles.externalLink, children: "the full config" }), ". For details, read", ' ', _jsxs("a", { href: DOCS_URL, target: "_blank", rel: "noreferrer", className: styles.externalLink, children: ["the docs", _jsx(ExternalIcon, { className: styles.externalIcon })] }), "."] }), _jsxs(Typography, { paragraph: true, children: [_jsx("strong", { children: "Data retrieval mode" }), ": ", props.retrievalMode] })] }), _jsx(ConfigSectionTitle, { tag: "global" })] }));
17
- export const ModalTitle = () => (_jsxs("div", { children: [_jsx(Typography, { className: styles.modalTitle, children: "Full configuration file (view only)" }), _jsxs(Typography, { variant: "h3", children: ["Sensitive values are masked. For details, read", ' ', _jsxs("a", { href: DOCS_URL, target: "_blank", rel: "noreferrer", className: styles.externalLink, children: ["the docs", _jsx(ExternalIcon, { className: classNames(styles.externalIcon, styles.largeIcon) })] }), "."] })] }));
16
+ export const Header = (props) => (_jsxs("div", { className: styles.root, children: [_jsxs(Box, { mb: 3, children: [_jsx(Typography, { paragraph: true, children: "Only select parameters can be changed here." }), _jsxs(Typography, { paragraph: true, children: ["However, you can still see", ' ', _jsx(Link, { href: "#", underline: "always", onClick: props.setOpen, className: styles.externalLink, children: "the full config" }), ". For details, read", ' ', _jsxs("a", { href: DOCS_URL, target: "_blank", className: styles.externalLink, children: ["the docs", _jsx(ExternalIcon, { className: styles.externalIcon })] }), "."] }), _jsxs(Typography, { paragraph: true, children: [_jsx("strong", { children: "Data retrieval mode" }), ": ", props.retrievalMode] })] }), _jsx(ConfigSectionTitle, { tag: "global" })] }));
17
+ export const ModalTitle = () => (_jsxs("div", { children: [_jsx(Typography, { className: styles.modalTitle, children: "Full configuration file (view only)" }), _jsxs(Typography, { variant: "h3", children: ["Sensitive values are masked. For details, read", ' ', _jsxs("a", { href: DOCS_URL, target: "_blank", className: styles.externalLink, children: ["the docs", _jsx(ExternalIcon, { className: classNames(styles.externalIcon, styles.largeIcon) })] }), "."] })] }));
@@ -7,7 +7,5 @@ declare type Props = {
7
7
  disabled?: boolean;
8
8
  keyErrors?: (string | undefined)[];
9
9
  };
10
- export declare const MASKED_ENV_VALUE = "****";
11
- export declare const isMaskedEnvValue: (value: string) => boolean;
12
10
  export declare const EnvsEditor: ({ envs, onChange, suggestions, disabled, keyErrors, }: Props) => JSX.Element;
13
11
  export {};
@@ -1,12 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Button, IconButton, TextField, Typography } from '@material-ui/core';
3
3
  import Box from '@mui/material/Box';
4
- // MASKED_ENV_VALUE is what the engine returns instead of a stored env value:
5
- // envs carry WAL-G and pgBackRest credentials, so GET /admin/config lists
6
- // the keys only. Posting the mask back for a key keeps the stored value.
7
- export const MASKED_ENV_VALUE = '****';
8
- export const isMaskedEnvValue = (value) => value === MASKED_ENV_VALUE;
9
- const MASKED_HINT = 'Stored on the server; kept unless you change it';
10
4
  // EnvsEditor renders a rows-of-key/value editor with add/remove and a
11
5
  // click-to-add suggestion list. Engine consumes envs as a free-form map
12
6
  // (physical.go:76, CopyOptions.Envs map[string]string); the UI is a thin
@@ -26,6 +20,5 @@ export const EnvsEditor = ({ envs, onChange, suggestions = [], disabled, keyErro
26
20
  onChange([...envs, { key, value: '' }]);
27
21
  };
28
22
  const usedKeys = new Set(envs.map((e) => e.key));
29
- const hasMasked = envs.some((e) => isMaskedEnvValue(e.value));
30
- return (_jsxs(Box, { mt: 1, "data-testid": "envs-editor", children: [_jsx(Typography, { variant: "subtitle2", children: "Environment variables" }), hasMasked && (_jsx(Box, { mt: 0.5, children: _jsxs(Typography, { variant: "caption", color: "textSecondary", "data-testid": "envs-masked-hint", children: ["Values shown as ", MASKED_ENV_VALUE, " are stored on the server and are kept unless you change them. Renaming such a variable requires entering its value again."] }) })), envs.length === 0 ? (_jsx(Box, { mt: 1, mb: 1, children: _jsx(Typography, { variant: "caption", color: "textSecondary", children: "No environment variables set. Use suggestions below or click \"Add\"." }) })) : (envs.map((env, i) => (_jsxs(Box, { display: "flex", alignItems: "center", mt: 1, "data-testid": `envs-row-${i}`, children: [_jsx(TextField, { size: "small", label: "Name", value: env.key, disabled: disabled, error: Boolean(keyErrors[i]), helperText: keyErrors[i], onChange: (e) => updateRow(i, { key: e.target.value }), inputProps: { 'data-testid': `envs-key-${i}` } }), _jsx(Box, { mx: 1, children: _jsx(TextField, { size: "small", label: "Value", value: env.value, disabled: disabled, helperText: isMaskedEnvValue(env.value) ? MASKED_HINT : undefined, onChange: (e) => updateRow(i, { value: e.target.value }), inputProps: { 'data-testid': `envs-value-${i}` } }) }), _jsx(IconButton, { size: "small", "aria-label": "remove env", disabled: disabled, onClick: () => removeRow(i), "data-testid": `envs-remove-${i}`, children: "\u00D7" })] }, i)))), _jsx(Box, { mt: 1, children: _jsx(Button, { size: "small", variant: "outlined", disabled: disabled, onClick: () => addRow(), "data-testid": "envs-add", children: "+ Add" }) }), suggestions.length > 0 && (_jsxs(Box, { mt: 1, children: [_jsx(Typography, { variant: "caption", color: "textSecondary", children: "Suggestions:" }), _jsx(Box, { mt: 0.5, children: suggestions.map((s) => (_jsx(Button, { size: "small", variant: "text", disabled: disabled || usedKeys.has(s), onClick: () => addRow(s), "data-testid": `envs-suggest-${s}`, children: s }, s))) })] }))] }));
23
+ return (_jsxs(Box, { mt: 1, "data-testid": "envs-editor", children: [_jsx(Typography, { variant: "subtitle2", children: "Environment variables" }), envs.length === 0 ? (_jsx(Box, { mt: 1, mb: 1, children: _jsx(Typography, { variant: "caption", color: "textSecondary", children: "No environment variables set. Use suggestions below or click \"Add\"." }) })) : (envs.map((env, i) => (_jsxs(Box, { display: "flex", alignItems: "center", mt: 1, "data-testid": `envs-row-${i}`, children: [_jsx(TextField, { size: "small", label: "Name", value: env.key, disabled: disabled, error: Boolean(keyErrors[i]), helperText: keyErrors[i], onChange: (e) => updateRow(i, { key: e.target.value }), inputProps: { 'data-testid': `envs-key-${i}` } }), _jsx(Box, { mx: 1, children: _jsx(TextField, { size: "small", label: "Value", value: env.value, disabled: disabled, onChange: (e) => updateRow(i, { value: e.target.value }), inputProps: { 'data-testid': `envs-value-${i}` } }) }), _jsx(IconButton, { size: "small", "aria-label": "remove env", disabled: disabled, onClick: () => removeRow(i), "data-testid": `envs-remove-${i}`, children: "\u00D7" })] }, i)))), _jsx(Box, { mt: 1, children: _jsx(Button, { size: "small", variant: "outlined", disabled: disabled, onClick: () => addRow(), "data-testid": "envs-add", children: "+ Add" }) }), suggestions.length > 0 && (_jsxs(Box, { mt: 1, children: [_jsx(Typography, { variant: "caption", color: "textSecondary", children: "Suggestions:" }), _jsx(Box, { mt: 0.5, children: suggestions.map((s) => (_jsx(Button, { size: "small", variant: "text", disabled: disabled || usedKeys.has(s), onClick: () => addRow(s), "data-testid": `envs-suggest-${s}`, children: s }, s))) })] }))] }));
31
24
  };
@@ -42,11 +42,7 @@ export const Configuration = observer(({ instanceId, switchActiveTab, reload, di
42
42
  const classes = useStyles();
43
43
  const stores = useStores();
44
44
  const { config, isConfigurationLoading, updateConfig, getSeImages, fullConfig, testDbSource, configError, getFullConfig, getFullConfigError, getEngine, } = stores.main;
45
- // The form mutates its working copy, so the store's config is cloned rather than read
46
- // directly. The store replaces the whole object on every fetch, so keying the clone on
47
- // its identity keeps the copy — and everything that depends on it — stable between
48
- // fetches instead of changing on every render.
49
- const configData = useMemo(() => config && JSON.parse(JSON.stringify(config)), [config]);
45
+ const configData = config && JSON.parse(JSON.stringify(config));
50
46
  const isConfigurationDisabled = disableConfigModification;
51
47
  const [dleEdition, setDledition] = useState('');
52
48
  const isCeEdition = dleEdition === 'community';
@@ -58,14 +54,11 @@ export const Configuration = observer(({ instanceId, switchActiveTab, reload, di
58
54
  // Prevents the Simple/Expert default from flipping mid-session when an
59
55
  // Apply triggers a refetch and the recomputed default would differ.
60
56
  const [initialMode, setInitialMode] = useState(null);
61
- const defaultMode = configData
62
- ? getInitialConfigMode(configData.host, configData.retrievalMode)
63
- : null;
64
57
  useEffect(() => {
65
- if (initialMode !== null || defaultMode === null)
66
- return;
67
- setInitialMode(defaultMode);
68
- }, [defaultMode, initialMode]);
58
+ if (initialMode === null && configData) {
59
+ setInitialMode(getInitialConfigMode(configData.host, configData.retrievalMode));
60
+ }
61
+ }, [configData, initialMode]);
69
62
  const configMode = (_a = userPickedMode !== null && userPickedMode !== void 0 ? userPickedMode : initialMode) !== null && _a !== void 0 ? _a : 'simple';
70
63
  const setConfigMode = setUserPickedMode;
71
64
  const portInitFromConfig = useRef(false);
@@ -258,7 +251,7 @@ export const Configuration = observer(({ instanceId, switchActiveTab, reload, di
258
251
  const currentValues = uniqueChipValue(String(formik.values[id]));
259
252
  const splitValues = currentValues.split(' ');
260
253
  const curDividers = String(formik.values[id]).match(/[,(\s)(\n)(\r)(\t)(\r\n)]/gm);
261
- for (const i in splitValues) {
254
+ for (let i in splitValues) {
262
255
  if (curDividers && splitValues[i] !== uniqueValue) {
263
256
  newValues =
264
257
  newValues +
@@ -514,14 +507,14 @@ export const Configuration = observer(({ instanceId, switchActiveTab, reload, di
514
507
  isCeEdition,
515
508
  ]);
516
509
  return (_jsxs("div", { className: styles.root, children: [_jsx(Snackbar, { onClick: () => {
517
- if (!dockerState.error)
518
- return;
519
- setDockerState({
520
- ...dockerState,
521
- error: '',
522
- });
510
+ Boolean(dockerState.error)
511
+ ? setDockerState({
512
+ ...dockerState,
513
+ error: '',
514
+ })
515
+ : undefined;
523
516
  }, anchorOrigin: { vertical: 'bottom', horizontal: 'right' }, open: (isConfigurationDisabled || Boolean(dockerState.error)) &&
524
- !isModalOpen, message: dockerState.error
517
+ !isModalOpen, message: Boolean(dockerState.error)
525
518
  ? dockerState.error
526
519
  : PREVENT_MODIFYING_MESSAGE, className: styles.snackbar }), !config && isConfigurationLoading ? (_jsx("div", { className: styles.spinnerContainer, children: _jsx(Spinner, { size: "lg", className: styles.spinner }) })) : (_jsxs(Box, { children: [_jsx(Header, { retrievalMode: formik.values.retrievalMode, setOpen: handleModalClick }), _jsxs(Tabs, { value: configMode, onChange: (_, value) => setConfigMode(value), indicatorColor: "primary", textColor: "primary", "aria-label": "Configuration mode", children: [_jsx(Tab, { value: "simple", label: "Simple" }), _jsx(Tab, { value: "expert", label: "Expert" })] }), configMode === 'simple' ? (_jsx(SimpleMode, { instanceId: instanceId, disabled: isConfigurationDisabled, onApplied: switchTab, onEdit: (proposed, password) => {
527
520
  const projection = buildProjectionFromProposed(proposed, password);
@@ -593,7 +586,7 @@ export const Configuration = observer(({ instanceId, switchActiveTab, reload, di
593
586
  value: image,
594
587
  children: image,
595
588
  };
596
- }) })] })), _jsxs(Typography, { paragraph: true, children: ["Cannot find your image? Reach out to support:", ' ', _jsxs("a", { href: 'https://postgres.ai/contact', target: "_blank", className: styles.externalLink, rel: "noreferrer", children: ["https://postgres.ai/contact", _jsx(ExternalIcon, { className: styles.externalIcon })] })] })] })] }), _jsxs(Box, { mb: 3, children: [_jsx(ConfigSectionTitle, { tag: "databaseConfigs" }), _jsx("span", { className: classes.grayText, style: { marginTop: '0.5rem', display: 'block' }, children: "Default PostgreSQL configuration used for all PostgreSQL instances running in containers managed by DBLab." }), _jsx(InputWithTooltip, { type: "textarea", label: "shared_buffers parameter", value: formik.values.sharedBuffers, tooltipText: tooltipText.sharedBuffers, disabled: isConfigurationDisabled, onChange: (e) => formik.setFieldValue('sharedBuffers', e.target.value) }), _jsx(InputWithTooltip, { type: "textarea", label: "shared_preload_libraries", value: formik.values.sharedPreloadLibraries, tooltipText: tooltipText.sharedPreloadLibraries, disabled: isConfigurationDisabled, onChange: (e) => formik.setFieldValue('sharedPreloadLibraries', e.target.value) }), _jsx(InputWithTooltip, { type: "textarea", label: "Query tuning parameters", value: typeof formik.values.tuningParams === 'object'
589
+ }) })] })), _jsxs(Typography, { paragraph: true, children: ["Cannot find your image? Reach out to support:", ' ', _jsxs("a", { href: 'https://postgres.ai/contact', target: "_blank", className: styles.externalLink, children: ["https://postgres.ai/contact", _jsx(ExternalIcon, { className: styles.externalIcon })] })] })] })] }), _jsxs(Box, { mb: 3, children: [_jsx(ConfigSectionTitle, { tag: "databaseConfigs" }), _jsx("span", { className: classes.grayText, style: { marginTop: '0.5rem', display: 'block' }, children: "Default PostgreSQL configuration used for all PostgreSQL instances running in containers managed by DBLab." }), _jsx(InputWithTooltip, { type: "textarea", label: "shared_buffers parameter", value: formik.values.sharedBuffers, tooltipText: tooltipText.sharedBuffers, disabled: isConfigurationDisabled, onChange: (e) => formik.setFieldValue('sharedBuffers', e.target.value) }), _jsx(InputWithTooltip, { type: "textarea", label: "shared_preload_libraries", value: formik.values.sharedPreloadLibraries, tooltipText: tooltipText.sharedPreloadLibraries, disabled: isConfigurationDisabled, onChange: (e) => formik.setFieldValue('sharedPreloadLibraries', e.target.value) }), _jsx(InputWithTooltip, { type: "textarea", label: "Query tuning parameters", value: typeof formik.values.tuningParams === 'object'
597
590
  ? Object.entries(formik.values.tuningParams)
598
591
  .map(([key, value]) => `${key}=${value}`)
599
592
  .join('\n')
@@ -609,7 +602,7 @@ export const Configuration = observer(({ instanceId, switchActiveTab, reload, di
609
602
  : '', message: testConnectionState.fetchTuning.error ||
610
603
  testConnectionState.fetchTuning.message.message })) : null] }), formik.values.retrievalMode === 'logical' && (_jsxs(Box, { children: [_jsxs(Box, { children: [_jsx(Typography, { className: styles.subsection, children: "Subsection \"retrieval.spec.logicalRestore\"" }), _jsx("span", { className: classes.grayText, children: "Restoring options." })] }), _jsx(InputWithTooltip, { label: "pg_restore jobs", value: formik.values.restoreParallelJobs, tooltipText: tooltipText.restoreParallelJobs, disabled: isConfigurationDisabled, onChange: (e) => formik.setFieldValue('restoreParallelJobs', e.target.value) }), _jsx(InputWithChip, { value: formik.values.pgRestoreCustomOptions, label: "pg_restore customOptions", id: "pgRestoreCustomOptions", tooltipText: tooltipText.pgRestoreCustomOptions, handleDeleteChip: handleDeleteChip, disabled: isConfigurationDisabled, onChange: (e) => formik.setFieldValue('pgRestoreCustomOptions', e.target.value) }), _jsx(InputWithTooltip, { type: "textarea", label: "Restore PostgreSQL configs", value: formik.values.restoreConfigs, tooltipText: tooltipText.restoreConfigs, disabled: isConfigurationDisabled, onChange: (e) => formik.setFieldValue('restoreConfigs', e.target.value) }), _jsx(FormControlLabel, { style: { maxWidth: 'max-content' }, control: _jsx(Checkbox, { name: "restoreIgnoreErrors", checked: formik.values.restoreIgnoreErrors, disabled: isConfigurationDisabled, onChange: (e) => formik.setFieldValue('restoreIgnoreErrors', e.target.checked), classes: {
611
604
  root: classes.checkboxRoot,
612
- } }), label: 'Ignore errors during logical data restore' })] })), _jsx(Box, { mt: 1, children: _jsx(Typography, { className: styles.subsection, children: "Subsection \"retrieval.refresh\"" }) }), _jsxs("span", { className: classes.grayText, children: ["Define full data refresh on schedule. The process requires at least one additional filesystem mount point. The schedule is to be specified using", ' ', _jsxs("a", { href: "https://en.wikipedia.org/wiki/Cron#Overview", target: "_blank", className: styles.externalLink, rel: "noreferrer", children: ["crontab format", _jsx(ExternalIcon, { className: styles.externalIcon })] }), "."] }), _jsx(InputWithTooltip, { label: "timetable", value: formik.values.timetable, tooltipText: tooltipText.timetable, disabled: isConfigurationDisabled, onChange: (e) => formik.setFieldValue('timetable', e.target.value) })] }), _jsxs(Box, { mt: 2, mb: 2, sx: {
605
+ } }), label: 'Ignore errors during logical data restore' })] })), _jsx(Box, { mt: 1, children: _jsx(Typography, { className: styles.subsection, children: "Subsection \"retrieval.refresh\"" }) }), _jsxs("span", { className: classes.grayText, children: ["Define full data refresh on schedule. The process requires at least one additional filesystem mount point. The schedule is to be specified using", ' ', _jsxs("a", { href: "https://en.wikipedia.org/wiki/Cron#Overview", target: "_blank", className: styles.externalLink, children: ["crontab format", _jsx(ExternalIcon, { className: styles.externalIcon })] }), "."] }), _jsx(InputWithTooltip, { label: "timetable", value: formik.values.timetable, tooltipText: tooltipText.timetable, disabled: isConfigurationDisabled, onChange: (e) => formik.setFieldValue('timetable', e.target.value) })] }), _jsxs(Box, { mt: 2, mb: 2, sx: {
613
606
  display: 'flex',
614
607
  alignItems: 'center',
615
608
  }, children: [_jsxs(Button, { variant: "contained", color: "secondary", onClick: () => {
@@ -3,9 +3,9 @@ import styles from './styles.module.scss';
3
3
  export const tooltipText = {
4
4
  dockerTag: () => (_jsx("div", { children: "Docker image version \u2014 the latest ones are listed first. If you are unsure, pick the first one." })),
5
5
  dockerImage: () => (_jsxs("div", { children: ["Major PostgreSQL version (e.g., \"9.6\", \"15\"). For logical provisioning mode, the version used by DBLab does not need to match the version on the source, although matching versions is recommended. ", _jsx("br", {}), "If you need a version that is not listed here, contact support."] })),
6
- dockerImageType: () => (_jsxs("div", { children: ["Docker image used to run all database containers \u2014 clones, snapshot preparation containers, and sync containers. Although such images are based on traditional Docker images for PostgreSQL, DBLab expects slightly different behavior: for example, PostgreSQL is not the first process used to start the container, so PostgreSQL restarts do not trigger a container state change. For details, see", ' ', _jsx("a", { target: '_blank', href: 'https://postgres.ai/docs/database-lab/supported-databases', className: styles.externalLink, rel: "noreferrer", children: "the docs" }), "."] })),
6
+ dockerImageType: () => (_jsxs("div", { children: ["Docker image used to run all database containers \u2014 clones, snapshot preparation containers, and sync containers. Although such images are based on traditional Docker images for PostgreSQL, DBLab expects slightly different behavior: for example, PostgreSQL is not the first process used to start the container, so PostgreSQL restarts do not trigger a container state change. For details, see", ' ', _jsx("a", { target: '_blank', href: 'https://postgres.ai/docs/database-lab/supported-databases', className: styles.externalLink, children: "the docs" }), "."] })),
7
7
  sharedBuffers: () => (_jsxs("div", { children: ["Defines the default buffer pool size for each PostgreSQL instance managed by DBLab. Note that this amount of RAM is immediately allocated at PostgreSQL startup time. For example, if the machine running DBLab has 32 GiB of RAM and the value used here is '1GB', then the theoretical limit of clones is 32. Practically, this limit is even lower because some memory is consumed by other processes. If you need more clones, reduce the value of", ' ', _jsx("span", { className: styles.firaCodeFont, children: "configs.shared_buffers" }), "."] })),
8
- sharedPreloadLibraries: () => (_jsxs("div", { children: ["Specifies one or more shared libraries (comma-separated list) to be preloaded at PostgreSQL server start (", _jsx("a", { target: '_blank', href: 'https://postgresqlco.nf/doc/en/param/shared_preload_libraries/', className: styles.externalLink, rel: "noreferrer", children: "details" }), "). If some libraries or extensions are missing, PostgreSQL fails to start, so make sure that ", _jsx("span", { className: styles.firaCodeFont, children: "dockerImage" }), ' ', "used above contains all required extensions."] })),
8
+ sharedPreloadLibraries: () => (_jsxs("div", { children: ["Specifies one or more shared libraries (comma-separated list) to be preloaded at PostgreSQL server start (", _jsx("a", { target: '_blank', href: 'https://postgresqlco.nf/doc/en/param/shared_preload_libraries/', className: styles.externalLink, children: "details" }), "). If some libraries or extensions are missing, PostgreSQL fails to start, so make sure that ", _jsx("span", { className: styles.firaCodeFont, children: "dockerImage" }), ' ', "used above contains all required extensions."] })),
9
9
  host: () => (_jsx("div", { children: "Hostname or IP of the database that will be used as the source for data retrieval (full data refresh)." })),
10
10
  port: () => (_jsx("div", { children: "Port of the database that will be used as the source for data retrieval (full data refresh)." })),
11
11
  username: () => (_jsx("div", { children: "Username used to connect to the database that will be used as the source for data retrieval (full data refresh)." })),
@@ -17,6 +17,6 @@ export const tooltipText = {
17
17
  restoreParallelJobs: () => (_jsx("div", { children: "Number of parallel workers used to restore databases from dump to PostgreSQL managed by DBLab. For initial data retrieval (the first data refresh), it is recommended to match the number of available vCPUs on the machine running DBLab. This yields faster restore times but can increase CPU and disk I/O usage on that machine (up to temporary resource saturation). For subsequent refreshes, if DBLab is in continuous use, it is recommended to reduce this value by 50% to reserve capacity for normal DBLab operations (such as working with clones)." })),
18
18
  pgRestoreCustomOptions: () => (_jsx("div", { children: "pg_restore options to be used to restore from a database dump, for example: '--exclude-schema=repack --exclude-schema=\"camelStyleSchemaName\"'. Note that due to security reasons, the current implementation supports only letters, numbers, hyphen, underscore, equal sign, and double quotes." })),
19
19
  restoreConfigs: () => (_jsxs("div", { children: ["PostgreSQL configuration parameters applied during logical restore (one", ' ', _jsx("span", { className: styles.firaCodeFont, children: "parameter=value" }), " per line). These settings are written to", ' ', _jsx("span", { className: styles.firaCodeFont, children: "postgresql.conf" }), " before restore starts and do not affect clones. Useful for tuning restore performance, for example:", _jsx("br", {}), _jsx("span", { className: styles.firaCodeFont, children: "maintenance_work_mem=8GB" }), _jsx("br", {}), _jsx("span", { className: styles.firaCodeFont, children: "max_parallel_maintenance_workers=7" }), _jsx("br", {}), _jsx("span", { className: styles.firaCodeFont, children: "shared_preload_libraries=" }), _jsx("br", {}), _jsx("span", { className: styles.firaCodeFont, children: "fsync=off" })] })),
20
- timetable: () => (_jsxs("div", { children: ["Schedule for full data refreshes, in", ' ', _jsx("a", { target: '_blank', href: 'https://en.wikipedia.org/wiki/Cron#Overview', className: styles.externalLink, rel: "noreferrer", children: "crontab format" }), "."] })),
21
- tuningParams: () => (_jsxs("div", { children: ["Query tuning parameters. These are essential to ensure that cloned PostgreSQL instances generate the same plans as the source (specifically, they are crucial for query performance troubleshooting and optimization, including working with EXPLAIN plans). For details, see the", ' ', _jsx("a", { target: '_blank', href: 'https://postgres.ai/docs/how-to-guides/administration/postgresql-configuration#postgresql-configuration-in-clones', className: styles.externalLink, rel: "noreferrer", children: "docs" }), "."] })),
20
+ timetable: () => (_jsxs("div", { children: ["Schedule for full data refreshes, in", ' ', _jsx("a", { target: '_blank', href: 'https://en.wikipedia.org/wiki/Cron#Overview', className: styles.externalLink, children: "crontab format" }), "."] })),
21
+ tuningParams: () => (_jsxs("div", { children: ["Query tuning parameters. These are essential to ensure that cloned PostgreSQL instances generate the same plans as the source (specifically, they are crucial for query performance troubleshooting and optimization, including working with EXPLAIN plans). For details, see the", ' ', _jsx("a", { target: '_blank', href: 'https://postgres.ai/docs/how-to-guides/administration/postgresql-configuration#postgresql-configuration-in-clones', className: styles.externalLink, children: "docs" }), "."] })),
22
22
  };
@@ -154,7 +154,7 @@ export const useForm = (onSubmit) => {
154
154
  const markPortDirty = () => setPortDirty(true);
155
155
  const omitPortOnSubmit = originalPortWasUnset && !portDirty;
156
156
  const formatDatabaseArray = (database) => {
157
- const databases = [];
157
+ let databases = [];
158
158
  const splitDatabaseArray = database.split(/[,(\s)(\n)(\r)(\t)(\r\n)]/);
159
159
  for (let i = 0; i < splitDatabaseArray.length; i++) {
160
160
  if (splitDatabaseArray[i] !== '') {
@@ -9,7 +9,7 @@ interface DockerImage {
9
9
  }
10
10
  export declare const uniqueChipValue: (values: string) => string;
11
11
  export declare const postUniqueDatabases: (values: string) => {
12
- [k: string]: string | object;
12
+ [k: string]: string | Object;
13
13
  } | null;
14
14
  export declare const genericDockerImages: DockerImage[];
15
15
  export declare const isSeDockerImage: (dockerImage: string | undefined) => boolean;
@@ -3,8 +3,8 @@ import { dockerImagesConfig, genericImagePrefix } from '../dockerCatalog';
3
3
  const seContainerRegistry = 'se-images';
4
4
  export const uniqueChipValue = (values) => {
5
5
  const splitChipArray = values.split(/[,(\s)(\n)(\r)(\t)(\r\n)]/);
6
- const databaseArray = [];
7
- for (const i in splitChipArray) {
6
+ let databaseArray = [];
7
+ for (let i in splitChipArray) {
8
8
  if (splitChipArray[i] !== '' &&
9
9
  databaseArray.indexOf(splitChipArray[i]) === -1) {
10
10
  databaseArray.push(splitChipArray[i]);
@@ -24,18 +24,18 @@ export const postUniqueDatabases = (values) => {
24
24
  const createDockerImages = (dockerImagesConfig) => {
25
25
  const dockerImages = [];
26
26
  for (const pg_major_version in dockerImagesConfig) {
27
- const hasVersion = Object.prototype.hasOwnProperty.call(dockerImagesConfig, pg_major_version);
28
- if (!hasVersion)
29
- continue;
30
- dockerImagesConfig[pg_major_version].forEach((tag) => {
31
- const image = {
32
- package_group: 'postgresai',
33
- pg_major_version,
34
- tag: `${pg_major_version}-${tag}`,
35
- location: `${genericImagePrefix}:${pg_major_version}-${tag}`,
36
- };
37
- dockerImages.push(image);
38
- });
27
+ if (dockerImagesConfig.hasOwnProperty(pg_major_version)) {
28
+ const customTags = dockerImagesConfig[pg_major_version];
29
+ customTags.forEach((tag) => {
30
+ const image = {
31
+ package_group: 'postgresai',
32
+ pg_major_version,
33
+ tag: `${pg_major_version}-${tag}`,
34
+ location: `${genericImagePrefix}:${pg_major_version}-${tag}`,
35
+ };
36
+ dockerImages.push(image);
37
+ });
38
+ }
39
39
  }
40
40
  return dockerImages;
41
41
  };
@@ -74,7 +74,7 @@ export const getImageMajorVersion = (pgImage) => {
74
74
  ? pgServerVersion.split('.')[0]
75
75
  : pgServerVersion;
76
76
  }
77
- catch {
77
+ catch (error) {
78
78
  // Return undefined for malformed image strings
79
79
  return undefined;
80
80
  }
@@ -114,7 +114,7 @@ export const createFallbackDockerImage = (dockerPath, dockerTag) => {
114
114
  };
115
115
  // Creates enhanced list of Docker images, including image from configuration
116
116
  export const createEnhancedDockerImages = (configDockerPath, configDockerTag) => {
117
- const enhancedImages = [...genericDockerImages];
117
+ let enhancedImages = [...genericDockerImages];
118
118
  // If there's an image in config, check if we need to add it
119
119
  if (configDockerPath && configDockerTag) {
120
120
  const existingImage = genericDockerImages.find((image) => image.location === configDockerPath || image.tag === configDockerTag);
@@ -1,10 +1,10 @@
1
1
  import React from 'react';
2
2
  declare type Props = {
3
3
  value: number;
4
- handleChange: (event: React.ChangeEvent<object>, newValue: number) => void;
4
+ handleChange: (event: React.ChangeEvent<{}>, newValue: number) => void;
5
5
  hasLogs: boolean;
6
6
  isPlatform?: boolean;
7
7
  hideInstanceTabs?: boolean;
8
8
  };
9
- export declare const PlatformTabs: ({ value, handleChange, hideInstanceTabs, }: Props) => JSX.Element;
9
+ export declare const PlatformTabs: ({ value, handleChange, hasLogs, hideInstanceTabs, }: Props) => JSX.Element;
10
10
  export {};
@@ -4,7 +4,7 @@ import { Tab as TabComponent, Tabs as TabsComponent } from '@material-ui/core';
4
4
  import { TABS_INDEX } from '.';
5
5
  import { useTabsStyles } from './styles';
6
6
  import { PostgresSQLIcon } from '@postgres.ai/shared/icons/PostgresSQL';
7
- export const PlatformTabs = ({ value, handleChange, hideInstanceTabs, }) => {
7
+ export const PlatformTabs = ({ value, handleChange, hasLogs, hideInstanceTabs, }) => {
8
8
  const classes = useTabsStyles();
9
9
  const { org, instanceId } = useParams();
10
10
  const tabs = [
@@ -9,7 +9,7 @@ export declare const TABS_INDEX: {
9
9
  };
10
10
  export interface TabsProps {
11
11
  value: number;
12
- handleChange: (event: React.ChangeEvent<object>, newValue: number) => void;
12
+ handleChange: (event: React.ChangeEvent<{}>, newValue: number) => void;
13
13
  hasLogs: boolean;
14
14
  isPlatform?: boolean;
15
15
  hideInstanceTabs?: boolean;
@@ -50,7 +50,7 @@ const useStyles = makeStyles((theme) => ({
50
50
  },
51
51
  }), { index: 1 });
52
52
  export const Instance = observer((props) => {
53
- var _a, _b, _c, _d, _e;
53
+ var _a, _b, _c, _d;
54
54
  const classes = useStyles();
55
55
  const { instanceId, api, isPlatform } = props;
56
56
  const [activeTab, setActiveTab] = React.useState((props === null || props === void 0 ? void 0 : props.renderCurrentTab) || TABS_INDEX.OVERVIEW);
@@ -84,7 +84,7 @@ export const Instance = observer((props) => {
84
84
  }
85
85
  }, [instance, hasBeenRedirected]);
86
86
  return (_jsx(HostProvider, { value: props, children: _jsxs(StoresProvider, { value: stores, children: [props.elements.breadcrumbs, _jsx(SectionTitle, { text: props.title, level: 1, tag: "h1", className: classes.title, rightContent: _jsx(Button, { onClick: () => load(props.instanceId, isPlatform), isDisabled: !instance && !instanceError, className: classes.reloadButton, children: "Reload info" }), children: isInstanceIntegrated && (_jsx(InstanceTabs, { instanceId: props.instanceId, tab: activeTab, onTabChange: (tabID) => setActiveTab(tabID), isPlatform: isPlatform, hasLogs: api.initWS !== undefined, hideInstanceTabs: props.hideBranchingFeatures })) }), instanceError && (_jsx(ErrorStub, { ...instanceError, className: classes.errorStub })), isInstanceIntegrated ? (_jsxs(_Fragment, { children: [_jsxs(TabPanel, { value: activeTab, index: TABS_INDEX.OVERVIEW, children: [!instanceError && (_jsx("div", { className: classes.content, children: instance && ((_b = (_a = instance.state) === null || _a === void 0 ? void 0 : _a.retrieving) === null || _b === void 0 ? void 0 : _b.status) ? (_jsxs(_Fragment, { children: [_jsx(Clones, {}), _jsx(Info, { hideBranchingFeatures: props.hideBranchingFeatures })] })) : (_jsx(StubSpinner, {})) })), _jsx(ClonesModal, {}), _jsx(SnapshotsModal, {})] }), _jsx(TabPanel, { value: activeTab, index: TABS_INDEX.CLONES, children: activeTab === TABS_INDEX.CLONES && (_jsx("div", { className: classes.content, children: !instanceError &&
87
- (instance ? _jsx(Clones, { onlyRenderList: true }) : _jsx(StubSpinner, {})) })) }), _jsx(TabPanel, { value: activeTab, index: TABS_INDEX.LOGS, children: activeTab === TABS_INDEX.LOGS && (_jsx(Logs, { api: api, instanceId: props.instanceId })) }), _jsx(TabPanel, { value: activeTab, index: TABS_INDEX.CONFIGURATION, children: activeTab === TABS_INDEX.CONFIGURATION && (_jsx(Configuration, { instanceId: instanceId, switchActiveTab: switchTab, reload: () => load(props.instanceId), disableConfigModification: (_c = instance === null || instance === void 0 ? void 0 : instance.state) === null || _c === void 0 ? void 0 : _c.engine.disableConfigModification })) }), _jsx(TabPanel, { value: activeTab, index: TABS_INDEX.SNAPSHOTS, children: activeTab === TABS_INDEX.SNAPSHOTS && (_jsx(Snapshots, { instanceId: instanceId })) }), _jsx(TabPanel, { value: activeTab, index: TABS_INDEX.BRANCHES, children: activeTab === TABS_INDEX.BRANCHES && (_jsx(Branches, { instanceId: instanceId })) })] })) : !isLoadingInstance && !isLoadingInstanceRetrieval && !instanceError ? (_jsx(TabPanel, { value: activeTab, index: activeTab, children: _jsx(InactiveInstance, { instance: instance, org: (_e = (_d = props.elements.breadcrumbs) === null || _d === void 0 ? void 0 : _d.props.org) !== null && _e !== void 0 ? _e : '' }) })) : (!instanceError && (_jsx(TabPanel, { value: activeTab, index: activeTab, children: _jsx("div", { className: classes.content, children: _jsx(StubSpinner, {}) }) })))] }) }));
87
+ (instance ? _jsx(Clones, { onlyRenderList: true }) : _jsx(StubSpinner, {})) })) }), _jsx(TabPanel, { value: activeTab, index: TABS_INDEX.LOGS, children: activeTab === TABS_INDEX.LOGS && (_jsx(Logs, { api: api, instanceId: props.instanceId })) }), _jsx(TabPanel, { value: activeTab, index: TABS_INDEX.CONFIGURATION, children: activeTab === TABS_INDEX.CONFIGURATION && (_jsx(Configuration, { instanceId: instanceId, switchActiveTab: switchTab, reload: () => load(props.instanceId), disableConfigModification: (_c = instance === null || instance === void 0 ? void 0 : instance.state) === null || _c === void 0 ? void 0 : _c.engine.disableConfigModification })) }), _jsx(TabPanel, { value: activeTab, index: TABS_INDEX.SNAPSHOTS, children: activeTab === TABS_INDEX.SNAPSHOTS && (_jsx(Snapshots, { instanceId: instanceId })) }), _jsx(TabPanel, { value: activeTab, index: TABS_INDEX.BRANCHES, children: activeTab === TABS_INDEX.BRANCHES && (_jsx(Branches, { instanceId: instanceId })) })] })) : !isLoadingInstance && !isLoadingInstanceRetrieval && !instanceError ? (_jsx(TabPanel, { value: activeTab, index: activeTab, children: _jsx(InactiveInstance, { instance: instance, org: (_d = props.elements.breadcrumbs) === null || _d === void 0 ? void 0 : _d.props.org }) })) : (!instanceError && (_jsx(TabPanel, { value: activeTab, index: activeTab, children: _jsx("div", { className: classes.content, children: _jsx(StubSpinner, {}) }) })))] }) }));
88
88
  });
89
89
  function TabPanel(props) {
90
90
  const { children, value, index, ...other } = props;
@@ -164,7 +164,7 @@ export class MainStore {
164
164
  return response;
165
165
  };
166
166
  this.getFullConfig = async (instanceId) => {
167
- var _a, _b, _c, _d, _e, _f, _g;
167
+ var _a, _b, _c, _d, _e, _f;
168
168
  if (!this.api.getFullConfig)
169
169
  return;
170
170
  const { response, error } = await this.api.getFullConfig(instanceId);
@@ -176,10 +176,10 @@ export class MainStore {
176
176
  this.platformUrl = rawPlatformUrl === null || rawPlatformUrl === void 0 ? void 0 : rawPlatformUrl.replace(/['"]+/g, '').trim().split(/\s+/)[0];
177
177
  this.uiVersion = (_f = (_e = (_d = splitYML[0]) === null || _d === void 0 ? void 0 : _d.split('dockerImage: "postgresai/ce-ui:')[2]) === null || _e === void 0 ? void 0 : _e.split('\n')[0]) === null || _f === void 0 ? void 0 : _f.replace(/['"]+/g, '');
178
178
  }
179
- if (error) {
180
- const failure = (await error.json());
181
- this.getFullConfigError = (_g = failure.message) !== null && _g !== void 0 ? _g : null;
182
- }
179
+ if (error)
180
+ this.getFullConfigError = await error
181
+ .json()
182
+ .then((err) => err.message);
183
183
  return response;
184
184
  };
185
185
  this.getSeImages = async (values) => {
@@ -5,41 +5,33 @@ export const useWsScroll = (isLoading, simpleInstall) => {
5
5
  const [isAtBottom, setIsAtBottom] = useState(true);
6
6
  useEffect(() => {
7
7
  var _a;
8
- if (!isLoading) {
9
- wsSnackbar(isAtBottom, isNewData);
10
- }
8
+ !isLoading && wsSnackbar(isAtBottom, isNewData);
11
9
  const contentElement = document.getElementById('content-container');
12
10
  const targetNode = simpleInstall
13
11
  ? (_a = document.getElementById('logs-container')) === null || _a === void 0 ? void 0 : _a.parentElement
14
12
  : document.getElementById('logs-container');
15
13
  const clientAtBottom = (element) => element.scrollHeight - element.scrollTop - 50 < element.clientHeight;
16
14
  const handleScroll = (e) => {
17
- if (!clientAtBottom(e.target)) {
15
+ if (clientAtBottom(e.target)) {
16
+ setIsAtBottom(true);
17
+ setIsNewData(false);
18
+ }
19
+ else {
18
20
  setIsAtBottom(false);
19
- return;
20
21
  }
21
- setIsAtBottom(true);
22
- setIsNewData(false);
23
22
  };
24
- // Log lines arrive as <p> nodes; a <div> is page chrome. The filter keeps the tagName
25
- // guard the removed DOMNodeInserted listener used.
26
- const handleInsert = (mutations) => {
27
- const hasNewLine = mutations.some((mutation) => Array.from(mutation.addedNodes).some((node) => node.nodeName !== 'DIV'));
28
- if (!hasNewLine)
29
- return;
30
- if (isAtBottom) {
31
- targetNode === null || targetNode === void 0 ? void 0 : targetNode.scrollIntoView(false);
23
+ const handleInsert = (e) => {
24
+ var _a;
25
+ if (((_a = e.srcElement) === null || _a === void 0 ? void 0 : _a.tagName) !== 'DIV') {
26
+ isAtBottom && (targetNode === null || targetNode === void 0 ? void 0 : targetNode.scrollIntoView(false));
27
+ setIsNewData(true);
32
28
  }
33
- setIsNewData(true);
34
29
  };
35
- const observer = new MutationObserver(handleInsert);
36
30
  contentElement === null || contentElement === void 0 ? void 0 : contentElement.addEventListener('scroll', handleScroll, false);
37
- if (contentElement) {
38
- observer.observe(contentElement, { childList: true, subtree: true });
39
- }
31
+ contentElement === null || contentElement === void 0 ? void 0 : contentElement.addEventListener('DOMNodeInserted', handleInsert, false);
40
32
  return () => {
41
33
  contentElement === null || contentElement === void 0 ? void 0 : contentElement.removeEventListener('scroll', handleScroll, false);
42
- observer.disconnect();
34
+ contentElement === null || contentElement === void 0 ? void 0 : contentElement.removeEventListener('DOMNodeInserted', handleInsert, false);
43
35
  };
44
- }, [isAtBottom, isNewData, isLoading, simpleInstall]);
36
+ }, [isAtBottom, isNewData, isLoading]);
45
37
  };