@postgres.ai/shared 4.0.0-pr-695.12 → 4.0.0-pr-1042

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@postgres.ai/shared",
3
- "version": "4.0.0-pr-695.12",
3
+ "version": "4.0.0-pr-1042",
4
4
  "main": "index.js",
5
5
  "types": "index.d.ts",
6
6
  "peerDependencies": {
@@ -116,7 +116,7 @@ export const CreateBranchPage = observer(({ instanceId, api, elements, routes, i
116
116
  }
117
117
  return (_jsxs(_Fragment, { children: [elements.breadcrumbs, _jsx(SectionTitle, { tag: "h1", level: 1, text: "Create branch", className: classes.title, children: _jsx(InstanceTabs, { tab: TABS_INDEX.BRANCHES, isPlatform: isPlatform, instanceId: instanceId, hasLogs: api.initWS !== undefined, hideInstanceTabs: hideBranchingFeatures }) }), _jsxs("div", { className: classes.wrapper, children: [_jsxs("div", { className: classes.container, children: [(snapshotsError || getBranchesError) && (_jsx("div", { className: classes.marginTop, children: _jsx(ErrorStub, { message: (snapshotsError === null || snapshotsError === void 0 ? void 0 : snapshotsError.message) || (getBranchesError === null || getBranchesError === void 0 ? void 0 : getBranchesError.message) }) })), _jsxs("div", { className: classes.form, children: [_jsx(TextField, { label: "Branch name", variant: "outlined", required: true, fullWidth: true, size: "small", InputLabelProps: {
118
118
  shrink: true,
119
- }, value: formik.values.branchName, error: Boolean(formik.errors.branchName), className: classes.marginBottom, onChange: (e) => formik.setFieldValue('branchName', e.target.value) }), _jsx("p", { className: cn(classes.marginTop, classes.marginBottom), children: "Choose an existing branch. The new branch will initially point at the same snapshot as the parent branch but going further, their evolution paths will be independent - new snapshots can be created for both branches." }), _jsx(Select, { fullWidth: true, label: "Parent branch", value: formik.values.baseBranch, disabled: !branchesList || formik.isSubmitting, onChange: handleParentBranchChange, error: Boolean(formik.errors.baseBranch), items: branchesList
119
+ }, value: formik.values.branchName, error: Boolean(formik.errors.branchName), helperText: formik.errors.branchName, className: classes.marginBottom, onChange: (e) => formik.setFieldValue('branchName', e.target.value) }), _jsx("p", { className: cn(classes.marginTop, classes.marginBottom), children: "Choose an existing branch. The new branch will initially point at the same snapshot as the parent branch but going further, their evolution paths will be independent - new snapshots can be created for both branches." }), _jsx(Select, { fullWidth: true, label: "Parent branch", value: formik.values.baseBranch, disabled: !branchesList || formik.isSubmitting, onChange: handleParentBranchChange, error: Boolean(formik.errors.baseBranch), items: branchesList
120
120
  ? branchesList.map((branch) => {
121
121
  return {
122
122
  value: branch.name,
@@ -7,7 +7,23 @@
7
7
  import { useFormik } from 'formik';
8
8
  import * as Yup from 'yup';
9
9
  const Schema = Yup.object().shape({
10
- branchName: Yup.string().required('Branch name is required'),
10
+ branchName: Yup.string()
11
+ .required('Branch name is required')
12
+ .max(255, 'Branch name cannot exceed 255 characters')
13
+ .test('valid-zfs-name', 'Branch name can only contain letters, numbers, and these characters: _ -', (value) => {
14
+ if (!value)
15
+ return true;
16
+ // ZFS allows only alphanumeric characters and: _ -
17
+ const validChars = /^[a-zA-Z0-9_-]*$/;
18
+ return validChars.test(value);
19
+ })
20
+ .test('valid-start-char', 'Branch name must start with a letter, number, or underscore', (value) => {
21
+ if (!value)
22
+ return true;
23
+ // Dataset names can begin with alphanumeric character or underscore
24
+ const validStartChar = /^[a-zA-Z0-9_]/;
25
+ return validStartChar.test(value);
26
+ }),
11
27
  });
12
28
  export const useForm = (onSubmit) => {
13
29
  const formik = useFormik({
@@ -128,13 +128,13 @@ export const CreateClone = observer((props) => {
128
128
  })) !== null && _g !== void 0 ? _g : [] })), _jsx(TextField, { fullWidth: true, label: "Clone ID", value: formik.values.cloneId, onChange: (e) => {
129
129
  const sanitizedCloneIdValue = e.target.value.replace(/\s/g, '');
130
130
  formik.setFieldValue('cloneId', sanitizedCloneIdValue);
131
- }, error: Boolean(formik.errors.cloneId), disabled: isCreatingClone }), _jsx(Select, { fullWidth: true, label: "Snapshot *", value: formik.values.snapshotId, disabled: !snapshots || isCreatingClone, onChange: (e) => formik.setFieldValue('snapshotId', e.target.value), error: Boolean(formik.errors.snapshotId), items: (_h = snapshots.map((snapshot, i) => {
131
+ }, error: Boolean(formik.errors.cloneId), helperText: formik.errors.cloneId, disabled: isCreatingClone }), _jsx(Select, { fullWidth: true, label: "Snapshot *", value: formik.values.snapshotId, disabled: !snapshots || isCreatingClone, onChange: (e) => formik.setFieldValue('snapshotId', e.target.value), error: Boolean(formik.errors.snapshotId), items: (_h = snapshots.map((snapshot, i) => {
132
132
  const isLatest = i === 0;
133
133
  return {
134
134
  value: snapshot.id,
135
135
  children: (_jsxs("div", { className: styles.snapshotItem, children: [_jsxs("strong", { className: styles.snapshotOverflow, children: [snapshot === null || snapshot === void 0 ? void 0 : snapshot.id, " ", isLatest && _jsx("span", { children: "Latest" })] }), (snapshot === null || snapshot === void 0 ? void 0 : snapshot.dataStateAt) && (_jsxs("p", { children: ["Data state at: ", snapshot === null || snapshot === void 0 ? void 0 : snapshot.dataStateAt] })), snapshot.message && (_jsxs("span", { children: ["Message: ", snapshot.message] }))] })),
136
136
  };
137
- })) !== null && _h !== void 0 ? _h : [] }), _jsx("p", { className: styles.remark, children: "By default latest snapshot of database is used. You can select\u00A0 different snapshots if earlier database state is needed." })] }), _jsxs("div", { className: styles.section, children: [_jsx("h2", { className: styles.title, children: "Database credentials *" }), _jsx("p", { className: styles.text, children: "Set custom credentials for the new clone. Save the password in reliable place, it can\u2019t be read later." }), _jsx(TextField, { fullWidth: true, label: "Database username *", value: formik.values.dbUser, onChange: (e) => formik.setFieldValue('dbUser', e.target.value), error: Boolean(formik.errors.dbUser), disabled: isCreatingClone }), _jsx(TextField, { fullWidth: true, label: "Database password *", type: "password", value: formik.values.dbPassword, onChange: (e) => {
137
+ })) !== null && _h !== void 0 ? _h : [] }), _jsx("p", { className: styles.remark, children: "By default latest snapshot of database is used. You can select\u00A0 different snapshots if earlier database state is needed." })] }), _jsxs("div", { className: styles.section, children: [_jsx("h2", { className: styles.title, children: "Database credentials *" }), _jsx("p", { className: styles.text, children: "Set custom credentials for the new clone. Save the password in reliable place, it can't be read later." }), _jsx(TextField, { fullWidth: true, label: "Database username *", value: formik.values.dbUser, onChange: (e) => formik.setFieldValue('dbUser', e.target.value), error: Boolean(formik.errors.dbUser), disabled: isCreatingClone }), _jsx(TextField, { fullWidth: true, label: "Database password *", type: "password", value: formik.values.dbPassword, onChange: (e) => {
138
138
  formik.setFieldValue('dbPassword', e.target.value);
139
139
  if (formik.errors.dbPassword) {
140
140
  formik.setFieldError('dbPassword', '');
@@ -1,7 +1,15 @@
1
1
  import { useFormik } from 'formik';
2
2
  import * as Yup from 'yup';
3
3
  const Schema = Yup.object().shape({
4
- cloneId: Yup.string(),
4
+ cloneId: Yup.string()
5
+ .max(63, 'Clone ID cannot exceed 63 characters')
6
+ .test('valid-docker-name', 'Clone ID must start with a letter or number and can only contain letters, numbers, underscores, periods, and hyphens', (value) => {
7
+ if (!value)
8
+ return true;
9
+ // Docker container name requirements: start with letter/number, contain only ASCII [a-zA-Z0-9_.-]
10
+ const validDockerName = /^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/;
11
+ return validDockerName.test(value);
12
+ }),
5
13
  snapshotId: Yup.string().required('Date state time is required'),
6
14
  dbUser: Yup.string().required('Database username is required'),
7
15
  dbPassword: Yup.string().required('Database password is required'),
@@ -56,7 +56,7 @@ export const Instance = observer((props) => {
56
56
  const [activeTab, setActiveTab] = React.useState((props === null || props === void 0 ? void 0 : props.renderCurrentTab) || TABS_INDEX.OVERVIEW);
57
57
  const [hasBeenRedirected, setHasBeenRedirected] = React.useState(false);
58
58
  const stores = useCreatedStores(props);
59
- const { instance, instanceError, instanceRetrieval, isLoadingInstance, load, } = stores.main;
59
+ const { instance, instanceError, instanceRetrieval, isLoadingInstance, isLoadingInstanceRetrieval, load, } = stores.main;
60
60
  const switchTab = (_, tabID) => {
61
61
  const contentElement = document.getElementById('content-container');
62
62
  setActiveTab(tabID);
@@ -66,7 +66,7 @@ export const Instance = observer((props) => {
66
66
  contentElement === null || contentElement === void 0 ? void 0 : contentElement.scrollTo(0, 0);
67
67
  };
68
68
  const isInstanceIntegrated = instanceRetrieval ||
69
- (!isLoadingInstance && instance && (instance === null || instance === void 0 ? void 0 : instance.url) && !instanceError);
69
+ (!isLoadingInstance && !isLoadingInstanceRetrieval && instance && (instance === null || instance === void 0 ? void 0 : instance.url) && !instanceError);
70
70
  const isConfigurationActive = (instanceRetrieval === null || instanceRetrieval === void 0 ? void 0 : instanceRetrieval.mode) !== 'physical';
71
71
  useEffect(() => {
72
72
  load(instanceId, isPlatform);
@@ -86,7 +86,7 @@ export const Instance = observer((props) => {
86
86
  }
87
87
  }, [instance, hasBeenRedirected]);
88
88
  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 &&
89
- (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, isConfigurationActive: isConfigurationActive, 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 && !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, {}) }) })))] }) }));
89
+ (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, isConfigurationActive: isConfigurationActive, 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, {}) }) })))] }) }));
90
90
  });
91
91
  function TabPanel(props) {
92
92
  const { children, value, index, ...other } = props;
@@ -72,6 +72,7 @@ export declare class MainStore {
72
72
  isBranchesLoading: boolean;
73
73
  isConfigLoading: boolean;
74
74
  isLoadingInstance: boolean;
75
+ isLoadingInstanceRetrieval: boolean;
75
76
  private readonly api;
76
77
  constructor(api: Api);
77
78
  get isDisabledInstance(): boolean;
@@ -27,9 +27,12 @@ export class MainStore {
27
27
  this.isBranchesLoading = false;
28
28
  this.isConfigLoading = false;
29
29
  this.isLoadingInstance = false;
30
+ this.isLoadingInstanceRetrieval = false;
30
31
  this.load = (instanceId, isPlatform = false) => {
31
32
  this.instance = null;
33
+ this.instanceRetrieval = null;
32
34
  this.isReloadingInstance = true;
35
+ this.isLoadingInstanceRetrieval = true;
33
36
  if (!isPlatform) {
34
37
  this.getBranches(instanceId);
35
38
  }
@@ -58,7 +61,9 @@ export class MainStore {
58
61
  };
59
62
  this.reload = (instanceId) => {
60
63
  this.instance = null;
64
+ this.instanceRetrieval = null;
61
65
  this.isReloadingInstance = true;
66
+ this.isLoadingInstanceRetrieval = true;
62
67
  this.loadInstance(instanceId, false).then(() => {
63
68
  var _a, _b, _c;
64
69
  if (this.api.refreshInstance)
@@ -88,11 +93,15 @@ export class MainStore {
88
93
  this.isReloadingInstanceRetrieval = false;
89
94
  };
90
95
  this.loadInstanceRetrieval = async (instanceId) => {
91
- if (!this.api.getInstanceRetrieval)
96
+ if (!this.api.getInstanceRetrieval) {
97
+ this.isLoadingInstanceRetrieval = false;
92
98
  return;
99
+ }
100
+ this.isLoadingInstanceRetrieval = true;
93
101
  const { response, error } = await this.api.getInstanceRetrieval({
94
102
  instanceId: instanceId,
95
103
  });
104
+ this.isLoadingInstanceRetrieval = false;
96
105
  if (response)
97
106
  this.instanceRetrieval = response;
98
107
  if (error)