diff --git a/package-lock.json b/package-lock.json
index 6944e5ad10..809fbe6a81 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -19,7 +19,6 @@
"@hookform/resolvers": "^3.9.1",
"@monaco-editor/react": "^4.6.0",
"@netlify/edge-functions": "^2.11.1",
- "@types/lodash": "^4.17.13",
"@types/pako": "^2.0.3",
"@uiw/react-json-view": "^2.0.0-alpha.30",
"axios": "^1.9.0",
@@ -33,7 +32,6 @@
"js-cookie": "^3.0.5",
"js-yaml": "^4.1.0",
"jszip": "^3.10.1",
- "lodash": "^4.17.21",
"mermaid": "^11.8.1",
"monaco-editor-textmate": "^4.0.0",
"monaco-textmate": "^3.0.1",
@@ -42,6 +40,7 @@
"pako": "^2.1.0",
"pdfjs-dist": "^5.4.394",
"psl": "^1.15.0",
+ "radash": "^12.1.1",
"randomatic": "^3.1.1",
"react": "^18.3.1",
"react-apexcharts": "^1.7.0",
@@ -103,6 +102,7 @@
"@typescript-eslint/eslint-plugin": "^8.21.0",
"@typescript-eslint/parser": "^8.18.2",
"@vitejs/plugin-react": "^4.3.4",
+ "@vitejs/plugin-react-swc": "^4.2.2",
"@vitest/ui": "^3.2.4",
"autoprefixer": "^10.4.20",
"babel-eslint": "^10.1.0",
@@ -134,6 +134,7 @@
"openai": "^4.77.0",
"postcss": "^8.4.49",
"rollup": "^4.34.6",
+ "rollup-plugin-visualizer": "^6.0.5",
"semantic-release-slack-bot": "^4.0.2",
"storybook": "^8.6.3",
"tailwind-config-viewer": "^2.0.4",
@@ -5430,6 +5431,232 @@
"@svgr/core": "*"
}
},
+ "node_modules/@swc/core": {
+ "version": "1.15.3",
+ "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.3.tgz",
+ "integrity": "sha512-Qd8eBPkUFL4eAONgGjycZXj1jFCBW8Fd+xF0PzdTlBCWQIV1xnUT7B93wUANtW3KGjl3TRcOyxwSx/u/jyKw/Q==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/counter": "^0.1.3",
+ "@swc/types": "^0.1.25"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/swc"
+ },
+ "optionalDependencies": {
+ "@swc/core-darwin-arm64": "1.15.3",
+ "@swc/core-darwin-x64": "1.15.3",
+ "@swc/core-linux-arm-gnueabihf": "1.15.3",
+ "@swc/core-linux-arm64-gnu": "1.15.3",
+ "@swc/core-linux-arm64-musl": "1.15.3",
+ "@swc/core-linux-x64-gnu": "1.15.3",
+ "@swc/core-linux-x64-musl": "1.15.3",
+ "@swc/core-win32-arm64-msvc": "1.15.3",
+ "@swc/core-win32-ia32-msvc": "1.15.3",
+ "@swc/core-win32-x64-msvc": "1.15.3"
+ },
+ "peerDependencies": {
+ "@swc/helpers": ">=0.5.17"
+ },
+ "peerDependenciesMeta": {
+ "@swc/helpers": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@swc/core-darwin-arm64": {
+ "version": "1.15.3",
+ "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.3.tgz",
+ "integrity": "sha512-AXfeQn0CvcQ4cndlIshETx6jrAM45oeUrK8YeEY6oUZU/qzz0Id0CyvlEywxkWVC81Ajpd8TQQ1fW5yx6zQWkQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-darwin-x64": {
+ "version": "1.15.3",
+ "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.3.tgz",
+ "integrity": "sha512-p68OeCz1ui+MZYG4wmfJGvcsAcFYb6Sl25H9TxWl+GkBgmNimIiRdnypK9nBGlqMZAcxngNPtnG3kEMNnvoJ2A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-linux-arm-gnueabihf": {
+ "version": "1.15.3",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.3.tgz",
+ "integrity": "sha512-Nuj5iF4JteFgwrai97mUX+xUOl+rQRHqTvnvHMATL/l9xE6/TJfPBpd3hk/PVpClMXG3Uvk1MxUFOEzM1JrMYg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-linux-arm64-gnu": {
+ "version": "1.15.3",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.3.tgz",
+ "integrity": "sha512-2Nc/s8jE6mW2EjXWxO/lyQuLKShcmTrym2LRf5Ayp3ICEMX6HwFqB1EzDhwoMa2DcUgmnZIalesq2lG3krrUNw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-linux-arm64-musl": {
+ "version": "1.15.3",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.3.tgz",
+ "integrity": "sha512-j4SJniZ/qaZ5g8op+p1G9K1z22s/EYGg1UXIb3+Cg4nsxEpF5uSIGEE4mHUfA70L0BR9wKT2QF/zv3vkhfpX4g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-linux-x64-gnu": {
+ "version": "1.15.3",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.3.tgz",
+ "integrity": "sha512-aKttAZnz8YB1VJwPQZtyU8Uk0BfMP63iDMkvjhJzRZVgySmqt/apWSdnoIcZlUoGheBrcqbMC17GGUmur7OT5A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-linux-x64-musl": {
+ "version": "1.15.3",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.3.tgz",
+ "integrity": "sha512-oe8FctPu1gnUsdtGJRO2rvOUIkkIIaHqsO9xxN0bTR7dFTlPTGi2Fhk1tnvXeyAvCPxLIcwD8phzKg6wLv9yug==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-win32-arm64-msvc": {
+ "version": "1.15.3",
+ "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.3.tgz",
+ "integrity": "sha512-L9AjzP2ZQ/Xh58e0lTRMLvEDrcJpR7GwZqAtIeNLcTK7JVE+QineSyHp0kLkO1rttCHyCy0U74kDTj0dRz6raA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-win32-ia32-msvc": {
+ "version": "1.15.3",
+ "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.3.tgz",
+ "integrity": "sha512-B8UtogMzErUPDWUoKONSVBdsgKYd58rRyv2sHJWKOIMCHfZ22FVXICR4O/VwIYtlnZ7ahERcjayBHDlBZpR0aw==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-win32-x64-msvc": {
+ "version": "1.15.3",
+ "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.3.tgz",
+ "integrity": "sha512-SpZKMR9QBTecHeqpzJdYEfgw30Oo8b/Xl6rjSzBt1g0ZsXyy60KLXrp6IagQyfTYqNYE/caDvwtF2FPn7pomog==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/counter": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
+ "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/@swc/types": {
+ "version": "0.1.25",
+ "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.25.tgz",
+ "integrity": "sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/counter": "^0.1.3"
+ }
+ },
"node_modules/@testing-library/dom": {
"version": "10.4.0",
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz",
@@ -5994,12 +6221,6 @@
"license": "MIT",
"peer": true
},
- "node_modules/@types/lodash": {
- "version": "4.17.20",
- "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.20.tgz",
- "integrity": "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==",
- "license": "MIT"
- },
"node_modules/@types/mdast": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz",
@@ -6914,6 +7135,30 @@
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
}
},
+ "node_modules/@vitejs/plugin-react-swc": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-4.2.2.tgz",
+ "integrity": "sha512-x+rE6tsxq/gxrEJN3Nv3dIV60lFflPj94c90b+NNo6n1QV1QQUTLoL0MpaOVasUZ0zqVBn7ead1B5ecx1JAGfA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rolldown/pluginutils": "1.0.0-beta.47",
+ "@swc/core": "^1.13.5"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "peerDependencies": {
+ "vite": "^4 || ^5 || ^6 || ^7"
+ }
+ },
+ "node_modules/@vitejs/plugin-react-swc/node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-beta.47",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.47.tgz",
+ "integrity": "sha512-8QagwMH3kNCuzD8EWL8R2YPW5e4OrHNSAHRFDdmFqEwEaD/KcNKjVoumo+gP2vW5eKB2UPbM6vTYiGZX0ixLnw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@vitest/expect": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.0.5.tgz",
@@ -14836,6 +15081,7 @@
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "dev": true,
"license": "MIT"
},
"node_modules/lodash-es": {
@@ -20652,6 +20898,15 @@
],
"license": "MIT"
},
+ "node_modules/radash": {
+ "version": "12.1.1",
+ "resolved": "https://registry.npmjs.org/radash/-/radash-12.1.1.tgz",
+ "integrity": "sha512-h36JMxKRqrAxVD8201FrCpyeNuUY9Y5zZwujr20fFO77tpUtGa6EZzfKw/3WaiBX95fq7+MpsuMLNdSnORAwSA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.18.0"
+ }
+ },
"node_modules/raf": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
@@ -22015,6 +22270,47 @@
"fsevents": "~2.3.2"
}
},
+ "node_modules/rollup-plugin-visualizer": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-6.0.5.tgz",
+ "integrity": "sha512-9+HlNgKCVbJDs8tVtjQ43US12eqaiHyyiLMdBwQ7vSZPiHMysGNo2E88TAp1si5wx8NAoYriI2A5kuKfIakmJg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "open": "^8.0.0",
+ "picomatch": "^4.0.2",
+ "source-map": "^0.7.4",
+ "yargs": "^17.5.1"
+ },
+ "bin": {
+ "rollup-plugin-visualizer": "dist/bin/cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "rolldown": "1.x || ^1.0.0-beta",
+ "rollup": "2.x || 3.x || 4.x"
+ },
+ "peerDependenciesMeta": {
+ "rolldown": {
+ "optional": true
+ },
+ "rollup": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/rollup-plugin-visualizer/node_modules/source-map": {
+ "version": "0.7.6",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz",
+ "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
"node_modules/rollup/node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.2.tgz",
diff --git a/package.json b/package.json
index c94cf1a6fe..69966a6870 100644
--- a/package.json
+++ b/package.json
@@ -5,6 +5,7 @@
"version": "2.239.1",
"scripts": {
"build": "node scripts/validateRuleConsistency.mjs && vite build",
+ "build:analyze": "ANALYZE=true npm run build",
"build-storybook": "storybook build",
"build:prod": "node scripts/fetchTemplates && node scripts/verifyTourStepIdsUniqueness.mjs && node scripts/validateRuleConsistency.mjs && NODE_ENV=production npm run build",
"fetch-templates": "node scripts/fetchTemplates",
@@ -79,7 +80,6 @@
"@hookform/resolvers": "^3.9.1",
"@monaco-editor/react": "^4.6.0",
"@netlify/edge-functions": "^2.11.1",
- "@types/lodash": "^4.17.13",
"@types/pako": "^2.0.3",
"@uiw/react-json-view": "^2.0.0-alpha.30",
"axios": "^1.9.0",
@@ -93,7 +93,6 @@
"js-cookie": "^3.0.5",
"js-yaml": "^4.1.0",
"jszip": "^3.10.1",
- "lodash": "^4.17.21",
"mermaid": "^11.8.1",
"monaco-editor-textmate": "^4.0.0",
"monaco-textmate": "^3.0.1",
@@ -102,6 +101,7 @@
"pako": "^2.1.0",
"pdfjs-dist": "^5.4.394",
"psl": "^1.15.0",
+ "radash": "^12.1.1",
"randomatic": "^3.1.1",
"react": "^18.3.1",
"react-apexcharts": "^1.7.0",
@@ -163,6 +163,7 @@
"@typescript-eslint/eslint-plugin": "^8.21.0",
"@typescript-eslint/parser": "^8.18.2",
"@vitejs/plugin-react": "^4.3.4",
+ "@vitejs/plugin-react-swc": "^4.2.2",
"@vitest/ui": "^3.2.4",
"autoprefixer": "^10.4.20",
"babel-eslint": "^10.1.0",
@@ -194,6 +195,7 @@
"openai": "^4.77.0",
"postcss": "^8.4.49",
"rollup": "^4.34.6",
+ "rollup-plugin-visualizer": "^6.0.5",
"semantic-release-slack-bot": "^4.0.2",
"storybook": "^8.6.3",
"tailwind-config-viewer": "^2.0.4",
@@ -231,4 +233,4 @@
"rimraf": "^4.0.0",
"micromatch": "^4.0.8"
}
-}
+}
\ No newline at end of file
diff --git a/src/components/atoms/index.ts b/src/components/atoms/index.ts
index 84f8858b9f..ba723decf1 100644
--- a/src/components/atoms/index.ts
+++ b/src/components/atoms/index.ts
@@ -31,4 +31,5 @@ export { RadioButton } from "@components/atoms/radioButton";
export { DesignedForDesktopBanner } from "@components/atoms/designedForDesktopTopBanner";
export { CodeFixMessage } from "@components/atoms/codeFixMessage";
export { DeleteFileConfirmation } from "@components/atoms/deleteFileConfirmation";
+export { PageLoader } from "@components/atoms/pageLoader";
export { StatusIndicator } from "@components/atoms/statusIndicator";
diff --git a/src/components/atoms/pageLoader.tsx b/src/components/atoms/pageLoader.tsx
new file mode 100644
index 0000000000..c867eb0b57
--- /dev/null
+++ b/src/components/atoms/pageLoader.tsx
@@ -0,0 +1,7 @@
+import React from "react";
+
+export const PageLoader = () => (
+
+);
diff --git a/src/components/molecules/copyButton.tsx b/src/components/molecules/copyButton.tsx
index 89ae343d63..8545bd0a98 100644
--- a/src/components/molecules/copyButton.tsx
+++ b/src/components/molecules/copyButton.tsx
@@ -1,6 +1,6 @@
import React, { forwardRef, useImperativeHandle, useRef } from "react";
-import { debounce } from "lodash";
+import { debounce } from "radash";
import { useTranslation } from "react-i18next";
import { useToastStore } from "@src/store";
@@ -53,14 +53,14 @@ export const CopyButton = forwardRef<
const addToast = useToastStore((state) => state.addToast);
const copyTextToClipboardRef = useRef(
- debounce(async (text: string) => {
+ debounce({ delay: 300 }, async (text: string) => {
const { isError, message } = await copyToClipboard(text);
addToast({
message: successMessage && !isError ? successMessage : message,
type: isError ? "error" : "success",
});
- }, 300)
+ })
);
const copyTextToClipboard = copyTextToClipboardRef.current;
diff --git a/src/components/molecules/popover/popoverListContent.tsx b/src/components/molecules/popover/popoverListContent.tsx
index c478b27a4f..1cc81fa171 100644
--- a/src/components/molecules/popover/popoverListContent.tsx
+++ b/src/components/molecules/popover/popoverListContent.tsx
@@ -1,6 +1,6 @@
import React, { useEffect, useMemo, useRef, useState } from "react";
-import { debounce } from "lodash";
+import { debounce } from "radash";
import { PopoverContentBase } from "./popoverContentBase";
import { usePopoverListContext } from "@contexts/usePopover";
@@ -76,7 +76,7 @@ export const PopoverListContent = React.forwardRef<
const debouncedFilter = useMemo(
() =>
- debounce((filterTerm: string) => {
+ debounce({ delay: searchByTermDebounceTime }, (filterTerm: string) => {
const filteredItems = items.filter((item) => {
if (typeof item.label === "string") {
return item.label.toLowerCase().includes(filterTerm);
@@ -84,16 +84,10 @@ export const PopoverListContent = React.forwardRef<
return item.id.toLowerCase().includes(filterTerm);
});
setPopoverItems(filteredItems);
- }, searchByTermDebounceTime),
+ }),
[items]
);
- useEffect(() => {
- return () => {
- debouncedFilter.cancel();
- };
- }, [debouncedFilter]);
-
const filterItemsBySearchTerm = (event: React.ChangeEvent) => {
const filterTerm = event.target.value.toLowerCase();
setSearchTerm(filterTerm);
diff --git a/src/components/organisms/deployments/sessions/table/table.tsx b/src/components/organisms/deployments/sessions/table/table.tsx
index 2c28f58df3..3b8b81cac9 100644
--- a/src/components/organisms/deployments/sessions/table/table.tsx
+++ b/src/components/organisms/deployments/sessions/table/table.tsx
@@ -1,6 +1,6 @@
import React, { useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
-import { debounce, isEqual } from "lodash";
+import { debounce, isEqual } from "radash";
import { useTranslation } from "react-i18next";
import { Outlet, useParams, useSearchParams } from "react-router-dom";
import { ListOnItemsRenderedProps } from "react-window";
@@ -76,7 +76,6 @@ export const SessionsTable = () => {
const firstTimeLoadingRef = useRef(true);
const refreshDataRef = useRef<(forceRefresh?: boolean) => Promise>();
const fetchSessionsRef = useRef<(nextPageToken?: string, forceRefresh?: boolean) => Promise>();
- const debouncedFetchSessionsRef = useRef>>();
const isCompactMode = leftSideWidth < 25;
const hideSourceColumn = leftSideWidth < 35;
const hideActionsColumn = leftSideWidth < 27;
@@ -179,7 +178,7 @@ export const SessionsTable = () => {
return fetchedDeployments;
}
- if (isEqual(sessionsCountByState, sessionStats)) return;
+ if (isEqual(sessionsCountByState, sessionStats.sessionStats)) return;
setSessionStats({
sessionStats: sessionsCountByState,
totalDeployments,
@@ -255,8 +254,7 @@ export const SessionsTable = () => {
[projectId, deploymentId, urlSessionStateFilter, addToast, tErrors, lastSeenSession, navigateWithSettings]
);
- const debouncedFetchSessions = useMemo(() => debounce(fetchSessions, 100), [fetchSessions]);
- debouncedFetchSessionsRef.current = debouncedFetchSessions;
+ const debouncedFetchSessions = useMemo(() => debounce({ delay: 100 }, fetchSessions), [fetchSessions]);
const refreshData = useCallback(
async (forceRefresh = false) => {
@@ -298,10 +296,6 @@ export const SessionsTable = () => {
};
loadData();
-
- return () => {
- debouncedFetchSessionsRef.current?.cancel();
- };
}, [deployments]);
const closeSessionLog = useCallback(() => {
diff --git a/src/components/organisms/editorTabs.tsx b/src/components/organisms/editorTabs.tsx
index 3185a9b774..c897d6e57a 100644
--- a/src/components/organisms/editorTabs.tsx
+++ b/src/components/organisms/editorTabs.tsx
@@ -2,8 +2,8 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"
import Editor, { Monaco } from "@monaco-editor/react";
import dayjs from "dayjs";
-import { debounce, last } from "lodash";
import * as monaco from "monaco-editor";
+import { debounce, last, throttle } from "radash";
import { useTranslation } from "react-i18next";
import Markdown from "react-markdown";
import { Document, Page, pdfjs } from "react-pdf";
@@ -457,21 +457,17 @@ export const EditorTabs = () => {
// eslint-disable-next-line react-hooks/exhaustive-deps
const debouncedManualSave = useCallback(
- debounce(
- () => {
- const currentContent = editorRef.current?.getValue();
- if (currentContent !== undefined) {
- updateContent(currentContent);
- }
- },
- 1500,
- { leading: true, trailing: false }
- ),
+ throttle({ interval: 1500 }, () => {
+ const currentContent = editorRef.current?.getValue();
+ if (currentContent !== undefined) {
+ updateContent(currentContent);
+ }
+ }),
[projectId, activeEditorFileName]
);
// eslint-disable-next-line react-hooks/exhaustive-deps
- const debouncedAutosave = useCallback(debounce(updateContent, 1500), [projectId, activeEditorFileName]);
+ const debouncedAutosave = useCallback(debounce({ delay: 1500 }, updateContent), [projectId, activeEditorFileName]);
const saveFileWithContent = async (fileName: string, content: string): Promise => {
if (!projectId) {
diff --git a/src/components/organisms/files/fileTree.tsx b/src/components/organisms/files/fileTree.tsx
index feab53d742..17fb66e74d 100644
--- a/src/components/organisms/files/fileTree.tsx
+++ b/src/components/organisms/files/fileTree.tsx
@@ -1,6 +1,6 @@
import React, { useEffect, useRef, useState } from "react";
-import { debounce } from "lodash";
+import { debounce } from "radash";
import { Tree, TreeApi } from "react-arborist";
import { useTranslation } from "react-i18next";
@@ -40,18 +40,11 @@ export const FileTree = ({
const [treeHeight, setTreeHeight] = useState(600);
const debouncedSetSearchTerm = useRef(
- debounce((value: string) => {
+ debounce({ delay: fileTreeTiming.SEARCH_DEBOUNCE_MS }, (value: string) => {
setSearchTerm(value);
- }, fileTreeTiming.SEARCH_DEBOUNCE_MS)
+ })
).current;
- useEffect(() => {
- return () => {
- debouncedSetSearchTerm.cancel();
- };
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, []);
-
useEffect(() => {
const updateHeight = () => {
if (containerRef.current) {
diff --git a/src/components/organisms/settings/organization/settings.tsx b/src/components/organisms/settings/organization/settings.tsx
index f91c04c5c3..e8701cb0c7 100644
--- a/src/components/organisms/settings/organization/settings.tsx
+++ b/src/components/organisms/settings/organization/settings.tsx
@@ -1,7 +1,6 @@
import React, { useMemo, useState } from "react";
-import debounce from "lodash/debounce";
-import omit from "lodash/omit";
+import { debounce, omit } from "radash";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
@@ -55,7 +54,7 @@ export const OrganizationSettings = () => {
}
setNameError("");
setOrganizationDisplayName(displayName);
- const { error } = await updateOrganization({ ...omit(organization, "currentMember"), displayName }, [
+ const { error } = await updateOrganization({ ...omit(organization!, ["currentMember"]), displayName }, [
"display_name",
]);
if (error) {
@@ -70,7 +69,7 @@ export const OrganizationSettings = () => {
setDisplaySuccess(false);
}, 3000);
};
- const debouncedRename = debounce(renameOrganization, 2000);
+ const debouncedRename = debounce({ delay: 2000 }, renameOrganization);
if (!organization) {
return null;
@@ -79,7 +78,7 @@ export const OrganizationSettings = () => {
const onDelete = async () => {
const deletingCurrentOrganization = organization.id === currentOrganization?.id;
- const { error } = await deleteOrganization(omit(organization, "currentMember"));
+ const { error } = await deleteOrganization(omit(organization, ["currentMember"]));
closeModal(ModalName.deleteOrganization);
if (error) {
diff --git a/src/components/organisms/settings/user/organizations/table.tsx b/src/components/organisms/settings/user/organizations/table.tsx
index d8b675ec85..5515823d43 100644
--- a/src/components/organisms/settings/user/organizations/table.tsx
+++ b/src/components/organisms/settings/user/organizations/table.tsx
@@ -1,6 +1,6 @@
import React from "react";
-import omit from "lodash/omit";
+import { omit } from "radash";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
@@ -33,7 +33,7 @@ export const UserOrganizationsTable = () => {
const onDelete = async (organization: EnrichedOrganization) => {
const deletingCurrentOrganization = organization.id === currentOrganization?.id;
- const { error } = await deleteOrganization(omit(organization, "currentMember"));
+ const { error } = await deleteOrganization(omit(organization, ["currentMember"]));
closeModal(ModalName.deleteOrganization);
if (error) {
addToast({
diff --git a/src/components/organisms/settings/user/profile.tsx b/src/components/organisms/settings/user/profile.tsx
index a4341ce98c..0d12dca3f8 100644
--- a/src/components/organisms/settings/user/profile.tsx
+++ b/src/components/organisms/settings/user/profile.tsx
@@ -1,6 +1,6 @@
import React, { useState } from "react";
-import debounce from "lodash/debounce";
+import { debounce } from "radash";
import { useTranslation } from "react-i18next";
import { version } from "@constants";
@@ -61,7 +61,7 @@ export const Profile = () => {
setDisplaySuccess(false);
}, 3000);
};
- const debouncedRename = debounce(renameUser, 2000);
+ const debouncedRename = debounce({ delay: 2000 }, renameUser);
return (
diff --git a/src/components/organisms/topbar/project/buttons.tsx b/src/components/organisms/topbar/project/buttons.tsx
index 107e06a9bb..659c6f9785 100644
--- a/src/components/organisms/topbar/project/buttons.tsx
+++ b/src/components/organisms/topbar/project/buttons.tsx
@@ -1,6 +1,6 @@
-import React, { useCallback, useEffect, useMemo, useState } from "react";
+import React, { useCallback, useMemo, useState } from "react";
-import { debounce } from "lodash";
+import { throttle } from "radash";
import { useTranslation } from "react-i18next";
import { useNavigate, useParams } from "react-router-dom";
@@ -272,15 +272,8 @@ export const ProjectTopbarButtons = () => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [projectId]);
- const debouncedBuild = useMemo(() => debounce(build, 1000, { leading: true, trailing: false }), [build]);
- const debouncedDeploy = useMemo(() => debounce(deploy, 1000, { leading: true, trailing: false }), [deploy]);
-
- useEffect(() => {
- return () => {
- debouncedBuild.cancel();
- debouncedDeploy.cancel();
- };
- }, [debouncedBuild, debouncedDeploy]);
+ const throttledBuild = useMemo(() => throttle({ interval: 1000 }, build), [build]);
+ const throttledDeploy = useMemo(() => throttle({ interval: 1000 }, deploy), [deploy]);
const isDeployAndBuildDisabled = Object.values(actionInProcess).some((value) => value);
@@ -292,7 +285,7 @@ export const ProjectTopbarButtons = () => {
ariaLabel={t("topbar.buttons.ariaBuildProject")}
className="group h-8 whitespace-nowrap px-3.5 text-white maxScreenWidth-1600:px-2"
disabled={isDeployAndBuildDisabled}
- onClick={debouncedBuild}
+ onClick={throttledBuild}
title={isValid ? t("topbar.buttons.build") : projectErrors}
variant="outline"
>
@@ -321,7 +314,7 @@ export const ProjectTopbarButtons = () => {
className="group h-8 items-center whitespace-nowrap px-3.5 text-white maxScreenWidth-1600:px-2"
disabled={isDeployAndBuildDisabled}
id={tourStepsHTMLIds.deployButton}
- onClick={debouncedDeploy}
+ onClick={throttledDeploy}
title={isValid ? t("topbar.buttons.deploy") : projectErrors}
variant="outline"
>
diff --git a/src/components/organisms/topbar/project/manualRun/manualRunButtons.tsx b/src/components/organisms/topbar/project/manualRun/manualRunButtons.tsx
index 92a87901fe..d775a43afc 100644
--- a/src/components/organisms/topbar/project/manualRun/manualRunButtons.tsx
+++ b/src/components/organisms/topbar/project/manualRun/manualRunButtons.tsx
@@ -1,6 +1,6 @@
import React, { useCallback, useEffect } from "react";
-import { isEqual } from "lodash";
+import { isEqual } from "radash";
import { useTranslation } from "react-i18next";
import { useParams } from "react-router-dom";
diff --git a/src/hooks/useSort.tsx b/src/hooks/useSort.tsx
index 9669d59d41..dbca7938b1 100644
--- a/src/hooks/useSort.tsx
+++ b/src/hooks/useSort.tsx
@@ -1,6 +1,6 @@
import { useCallback, useMemo, useState } from "react";
-import { orderBy } from "lodash";
+import { sort } from "radash";
import { initialSortConfig } from "@constants";
import { SortDirectionVariant } from "@enums/components";
@@ -16,21 +16,18 @@ export const useSort =
(items: T[], initialSortKey?: keyof T) => {
return [...items];
}
- return orderBy(
- items,
- [
- (item) => {
- const value = sortConfig.key ? item[sortConfig.key] : undefined;
-
- if (value === undefined || value === null) {
- return sortConfig.direction === SortDirectionVariant.ASC ? Number.MAX_VALUE : Number.MIN_VALUE;
- }
-
- return typeof value === "string" ? value.toLowerCase() : value;
- },
- ],
- [sortConfig.direction]
- );
+ const getValue = (item: T) => {
+ const value = sortConfig.key ? item[sortConfig.key] : undefined;
+
+ if (value === undefined || value === null) {
+ return sortConfig.direction === SortDirectionVariant.ASC ? Number.MAX_VALUE : Number.MIN_VALUE;
+ }
+
+ return typeof value === "string" ? value.toLowerCase() : value;
+ };
+
+ const sorted = sort(items, getValue as (item: T) => number);
+ return sortConfig.direction === SortDirectionVariant.DESC ? sorted.reverse() : sorted;
}, [items, sortConfig]);
const requestSort = useCallback((key: keyof T) => {
diff --git a/src/routes.tsx b/src/routes.tsx
index 42e1fca929..cf2ddf2725 100644
--- a/src/routes.tsx
+++ b/src/routes.tsx
@@ -1,62 +1,154 @@
-import React from "react";
+import React, { Suspense, lazy } from "react";
import { Navigate } from "react-router-dom";
import { featureFlags } from "./constants";
import { MemberRole } from "@enums";
-import { EventsList } from "@shared-components";
import { legacyRoutes } from "@src/routes.legacy";
-import {
- DeploymentsTable,
- EventViewer,
- ProtectedRoute,
- SessionsTable,
- GlobalConnectionsTable,
-} from "@components/organisms";
-import { AddConnection, EditConnection } from "@components/organisms/configuration/connections";
-import { TemplatesCatalog } from "@components/organisms/dashboard/templates";
-import { SessionViewer } from "@components/organisms/deployments";
-import { ActivityList, SessionOutputs } from "@components/organisms/deployments/sessions/tabs";
-import {
- AddOrganization,
- OrganizationMembersTable,
- OrganizationSettings,
- SwitchOrganization,
-} from "@components/organisms/settings/organization";
-import { OrganizationBilling } from "@components/organisms/settings/organization/billing";
-import { ClientConfiguration, Profile, UserOrganizationsTable } from "@components/organisms/settings/user";
-import { WelcomePage } from "@components/organisms/welcome";
-import {
- AiLandingPage,
- ChatPage,
- CustomError,
- Dashboard,
- Internal404,
- Intro,
- Project,
- TemplateLanding,
-} from "@components/pages";
+import { PageLoader } from "@components/atoms";
+import { ProtectedRoute } from "@components/organisms";
import { AppLayout, EventsLayout, GlobalConnectionsLayout } from "@components/templates";
import { ProjectWrapper } from "@components/templates/projectWrapper";
import { SettingsLayout } from "@components/templates/settingsLayout";
-const sessionViewRoutes = [
- { index: true, element: },
- { path: "executionflow", element: },
- { path: "settings/*", element: },
- { path: "executionflow/settings/*", element: },
-];
+const LazyDashboard = lazy(() => import("@components/pages/dashboard").then((m) => ({ default: m.Dashboard })));
+const LazyProject = lazy(() => import("@components/pages/project").then((m) => ({ default: m.Project })));
+const LazyAiLandingPage = lazy(() =>
+ import("@components/pages/aiLandingPage").then((m) => ({ default: m.AiLandingPage }))
+);
+const LazyChatPage = lazy(() => import("@components/pages/chat").then((m) => ({ default: m.ChatPage })));
+const LazyIntro = lazy(() => import("@components/pages/intro").then((m) => ({ default: m.Intro })));
+const LazyTemplateLanding = lazy(() =>
+ import("@components/pages/templateLanding").then((m) => ({ default: m.TemplateLanding }))
+);
+const LazyCustomError = lazy(() => import("@components/pages/customError").then((m) => ({ default: m.CustomError })));
+const LazyInternal404 = lazy(() => import("@components/pages/internal404").then((m) => ({ default: m.Internal404 })));
+
+const LazyDeploymentsTable = lazy(() =>
+ import("@components/organisms/deployments/table").then((m) => ({ default: m.DeploymentsTable }))
+);
+const LazySessionsTable = lazy(() =>
+ import("@components/organisms/deployments/sessions/table/table").then((m) => ({ default: m.SessionsTable }))
+);
+const LazySessionViewer = lazy(() =>
+ import("@components/organisms/deployments/sessions/viewer").then((m) => ({ default: m.SessionViewer }))
+);
+const LazyEventViewer = lazy(() =>
+ import("@components/organisms/events/viewer").then((m) => ({ default: m.EventViewer }))
+);
+const LazyEventsList = lazy(() =>
+ import("@components/organisms/shared/events").then((m) => ({ default: m.EventsList }))
+);
+const GlobalConnectionsTable = lazy(() =>
+ import("@components/organisms/globalConnections/table").then((m) => ({
+ default: m.GlobalConnectionsTable,
+ }))
+);
+
+const LazyProjectConfigurationDrawer = lazy(() =>
+ import("@components/organisms/configuration/configrationDrawer").then((m) => ({
+ default: m.ProjectConfigurationDrawer,
+ }))
+);
+const LazyProjectConfigurationView = lazy(() =>
+ import("@components/organisms/configuration/configurationView").then((m) => ({
+ default: m.ConfigurationView,
+ }))
+);
+const LazyAddConnection = lazy(() =>
+ import("@components/organisms/configuration/connections/add").then((m) => ({ default: m.AddConnection }))
+);
+const LazyEditConnection = lazy(() =>
+ import("@components/organisms/configuration/connections/edit").then((m) => ({
+ default: m.EditConnection,
+ }))
+);
+const LazyAddTrigger = lazy(() =>
+ import("@components/organisms/configuration/triggers/add").then((m) => ({ default: m.AddTrigger }))
+);
+const LazyEditTrigger = lazy(() =>
+ import("@components/organisms/configuration/triggers/edit").then((m) => ({ default: m.EditTrigger }))
+);
+const LazyAddVariable = lazy(() =>
+ import("@components/organisms/configuration/variables/add").then((m) => ({ default: m.AddVariable }))
+);
+const LazyEditVariable = lazy(() =>
+ import("@components/organisms/configuration/variables/edit").then((m) => ({ default: m.EditVariable }))
+);
+
+const LazyTemplatesCatalog = lazy(() =>
+ import("@components/organisms/dashboard/templates/catalog").then((m) => ({ default: m.TemplatesCatalog }))
+);
+const LazyWelcomePage = lazy(() => import("@components/organisms/welcome").then((m) => ({ default: m.WelcomePage })));
-const sessionRouteConfig = [
+const LazyActivityList = lazy(() =>
+ import("@components/organisms/deployments/sessions/tabs/activities").then((m) => ({ default: m.ActivityList }))
+);
+const LazySessionOutputs = lazy(() =>
+ import("@components/organisms/deployments/sessions/tabs/outputs").then((m) => ({
+ default: m.SessionOutputs,
+ }))
+);
+
+const LazyProfile = lazy(() =>
+ import("@components/organisms/settings/user/profile").then((m) => ({ default: m.Profile }))
+);
+const LazyClientConfiguration = lazy(() =>
+ import("@components/organisms/settings/user/clientConfiguration").then((m) => ({
+ default: m.ClientConfiguration,
+ }))
+);
+const LazyUserOrganizationsTable = lazy(() =>
+ import("@components/organisms/settings/user/organizations/table").then((m) => ({
+ default: m.UserOrganizationsTable,
+ }))
+);
+const LazyAddOrganization = lazy(() =>
+ import("@components/organisms/settings/organization/add").then((m) => ({
+ default: m.AddOrganization,
+ }))
+);
+const LazyOrganizationSettings = lazy(() =>
+ import("@components/organisms/settings/organization/settings").then((m) => ({ default: m.OrganizationSettings }))
+);
+const LazyOrganizationMembersTable = lazy(() =>
+ import("@components/organisms/settings/organization/members/table").then((m) => ({
+ default: m.OrganizationMembersTable,
+ }))
+);
+const LazySwitchOrganization = lazy(() =>
+ import("@components/organisms/settings/organization/switchOrganization").then((m) => ({
+ default: m.SwitchOrganization,
+ }))
+);
+const LazyOrganizationBilling = lazy(() =>
+ import("@components/organisms/settings/organization/billing/organizationBilling").then((m) => ({
+ default: m.OrganizationBilling,
+ }))
+);
+
+const withSuspense = (Component: React.ReactNode) => }>{Component};
+
+const settingsRouteConfig = [
+ { index: true, element: withSuspense() },
+ { path: "connections/new", element: withSuspense() },
+ { path: "connections", element: withSuspense() },
{
- path: ":sessionId",
- element: ,
- children: sessionViewRoutes,
+ path: "connections/:id/edit",
+ element: withSuspense(),
},
+ { path: "variables", element: withSuspense() },
+ { path: "variables/new", element: withSuspense() },
+ { path: "variables/:name/edit", element: withSuspense() },
+ { path: "triggers", element: withSuspense() },
+ { path: "triggers/new", element: withSuspense() },
+ { path: "triggers/:id/edit", element: withSuspense() },
];
-const noProjectHome = featureFlags.displayChatbot ? : ;
+const noProjectHome = featureFlags.displayChatbot
+ ? withSuspense()
+ : withSuspense();
const globalConnectionsRoutes = featureFlags.displayGlobalConnections
? [
@@ -65,14 +157,19 @@ const globalConnectionsRoutes = featureFlags.displayGlobalConnections
children: [
{
path: "connections",
- element: ,
+ element: withSuspense(),
children: [
- { path: "new", element: },
+ {
+ path: "new",
+ element: withSuspense(
+
+ ),
+ },
{ path: ":id", element: null },
{
path: ":id/edit",
- element: (
-
+ element: withSuspense(
+
),
},
],
@@ -88,13 +185,13 @@ export const mainRoutes = [
path: "/",
element: ,
children: [
- { index: true, element: },
+ { index: true, element: withSuspense() },
{ path: "ai", element: noProjectHome },
{ path: "welcome", element: noProjectHome },
- { path: "intro", element: },
- { path: "templates-library", element: },
- { path: "404", element: },
- { path: "chat", element: },
+ { path: "intro", element: withSuspense() },
+ { path: "templates-library", element: withSuspense() },
+ { path: "404", element: withSuspense() },
+ { path: "chat", element: withSuspense() },
{ path: "*", element: },
],
},
@@ -102,7 +199,7 @@ export const mainRoutes = [
path: "/template",
element: ,
children: [
- { index: true, element: },
+ { index: true, element: withSuspense() },
{ path: "*", element: },
],
},
@@ -115,8 +212,17 @@ export const mainRoutes = [
children: [
{ index: true, element: },
{ path: "code", element: },
- { path: "explorer", element: },
- { path: "explorer/settings/*", element: },
+ {
+ path: "explorer",
+ element: withSuspense(),
+ children: [
+ {
+ path: "settings",
+ element: withSuspense(),
+ children: settingsRouteConfig,
+ },
+ ],
+ },
],
},
],
@@ -128,12 +234,52 @@ export const mainRoutes = [
{
element: ,
children: [
- { index: true, element: },
- { path: "settings/*", element: },
+ { index: true, element: withSuspense() },
+ {
+ path: "settings",
+ element: withSuspense(
+ <>
+
+
+ >
+ ),
+ children: settingsRouteConfig,
+ },
+
+ {
+ path: ":deploymentId/sessions/settings",
+ element: withSuspense(
+ <>
+
+
+ >
+ ),
+ children: settingsRouteConfig,
+ },
+
{
path: ":deploymentId/sessions",
- element: ,
- children: sessionRouteConfig,
+ element: withSuspense(),
+ children: [
+ {
+ path: ":sessionId",
+ element: withSuspense(),
+ children: [
+ { index: true, element: withSuspense() },
+ { path: "executionflow", element: withSuspense() },
+ {
+ path: "settings",
+ element: withSuspense(
+ <>
+
+
+ >
+ ),
+ children: settingsRouteConfig,
+ },
+ ],
+ },
+ ],
},
{ path: "*", element: },
],
@@ -147,11 +293,22 @@ export const mainRoutes = [
{
element: ,
children: [
- { index: true, element: },
- { path: "settings/*", element: },
+ { index: true, element: withSuspense() },
+ { path: "settings/*", element: withSuspense() },
{
- element: ,
- children: sessionRouteConfig,
+ element: withSuspense(),
+ children: [
+ {
+ path: ":sessionId",
+ element: withSuspense(),
+ children: [
+ { index: true, element: withSuspense() },
+ { path: "executionflow", element: withSuspense() },
+ { path: "settings/*", element: withSuspense() },
+ { path: "executionflow/settings/*", element: withSuspense() },
+ ],
+ },
+ ],
},
{ path: "*", element: },
],
@@ -166,10 +323,10 @@ export const mainRoutes = [
),
children: [
- { index: true, element: },
- { path: "client-configuration", element: },
- { path: "organizations", element: },
- { path: "add-organization", element: },
+ { index: true, element: withSuspense() },
+ { path: "client-configuration", element: withSuspense() },
+ { path: "organizations", element: withSuspense() },
+ { path: "add-organization", element: withSuspense() },
{ path: "*", element: },
],
},
@@ -185,7 +342,7 @@ export const mainRoutes = [
index: true,
element: (
-
+ {withSuspense()}
),
},
@@ -193,11 +350,11 @@ export const mainRoutes = [
path: "billing",
element: (
-
+ {withSuspense()}
),
},
- { path: "members", element: },
+ { path: "members", element: withSuspense() },
{ path: "*", element: },
],
},
@@ -206,8 +363,8 @@ export const mainRoutes = [
children: [
{
path: "events",
- element: ,
- children: [{ path: ":eventId", element: }],
+ element: withSuspense(),
+ children: [{ path: ":eventId", element: withSuspense() }],
},
{ path: "*", element: },
],
@@ -216,12 +373,12 @@ export const mainRoutes = [
{
path: "switch-organization/:organizationId",
element: ,
- children: [{ index: true, element: }],
+ children: [{ index: true, element: withSuspense() }],
},
{
path: "error",
element: ,
- children: [{ index: true, element: }],
+ children: [{ index: true, element: withSuspense() }],
},
...legacyRoutes,
{ path: "*", element: },
diff --git a/src/services/sessions.service.ts b/src/services/sessions.service.ts
index 882cfef74d..5da9cc6e41 100644
--- a/src/services/sessions.service.ts
+++ b/src/services/sessions.service.ts
@@ -1,5 +1,5 @@
import { t } from "i18next";
-import { omit } from "lodash";
+import { omit } from "radash";
import {
Session as ProtoSession,
@@ -177,7 +177,7 @@ export class SessionsService {
projectId: string
): Promise> {
try {
- const sessionToStart = { ...omit(startSessionArgs, "jsonInputs"), projectId };
+ const sessionToStart = { ...omit(startSessionArgs, ["jsonInputs"]), projectId };
const sessionAsStartRequest = {
session: sessionToStart,
jsonObjectInput: startSessionArgs.jsonInputs,
diff --git a/src/store/cache/useCacheStore.ts b/src/store/cache/useCacheStore.ts
index b1cf97db7f..3a264b1aa1 100644
--- a/src/store/cache/useCacheStore.ts
+++ b/src/store/cache/useCacheStore.ts
@@ -1,5 +1,5 @@
import { t } from "i18next";
-import isEqual from "lodash/isEqual";
+import { isEqual } from "radash";
import { createSelector } from "reselect";
import { StateCreator, create } from "zustand";
diff --git a/src/store/useProjectStore.ts b/src/store/useProjectStore.ts
index eb63024956..c4f622d96e 100644
--- a/src/store/useProjectStore.ts
+++ b/src/store/useProjectStore.ts
@@ -1,6 +1,6 @@
import { t } from "i18next";
import { load } from "js-yaml";
-import isEqual from "lodash/isEqual";
+import { isEqual } from "radash";
import { StateCreator, create } from "zustand";
import { persist } from "zustand/middleware";
import { immer } from "zustand/middleware/immer";
diff --git a/src/utilities/calculateDeploymentSessionsStats.utils.ts b/src/utilities/calculateDeploymentSessionsStats.utils.ts
index 2eeac705a9..e8fc662087 100644
--- a/src/utilities/calculateDeploymentSessionsStats.utils.ts
+++ b/src/utilities/calculateDeploymentSessionsStats.utils.ts
@@ -1,5 +1,3 @@
-import { cloneDeep } from "lodash";
-
import { SessionStateType } from "@src/enums";
import { SessionStatsFilterType } from "@src/types/components";
import { Deployment } from "@src/types/models";
@@ -8,7 +6,7 @@ export const calculateDeploymentSessionsStats = (deployments: Deployment[]): Ses
const allSessionStats = deployments.flatMap((deployment) => deployment.sessionStats || []);
let totalSessionsCount = 0;
- const resetSessionStats = cloneDeep(initialSessionCounts);
+ const resetSessionStats = structuredClone(initialSessionCounts);
const sessionStats = allSessionStats.reduce>((acc, { count, state }) => {
if (!state) return acc;
diff --git a/src/utilities/convertBuildRuntimesToViewTriggers.utils.ts b/src/utilities/convertBuildRuntimesToViewTriggers.utils.ts
index 9e1b5f61fb..c45070bd50 100644
--- a/src/utilities/convertBuildRuntimesToViewTriggers.utils.ts
+++ b/src/utilities/convertBuildRuntimesToViewTriggers.utils.ts
@@ -1,4 +1,4 @@
-import { uniqBy } from "lodash";
+import { unique } from "radash";
import { namespaces } from "@constants";
import { LoggerService } from "@services";
@@ -16,7 +16,7 @@ const processRuntime = (runtime: BuildInfoRuntimes): Record =>
.filter(({ location: { path }, symbol: name }) => path === fileName && !name.startsWith("_"))
.map(({ symbol: name }) => name);
- const uniqueEntrypoints = uniqBy(entrypointsForFile, (func) => func);
+ const uniqueEntrypoints = unique(entrypointsForFile, (func) => func);
result[fileName] = uniqueEntrypoints;
});
diff --git a/src/utilities/fetchAndExtractZip.utils.ts b/src/utilities/fetchAndExtractZip.utils.ts
index 18494af9d7..63dbd0e3d7 100644
--- a/src/utilities/fetchAndExtractZip.utils.ts
+++ b/src/utilities/fetchAndExtractZip.utils.ts
@@ -2,7 +2,7 @@ import axios from "axios";
import frontMatter from "front-matter";
import { t } from "i18next";
import JSZip from "jszip";
-import { memoize } from "lodash";
+import { memo } from "radash";
import {
DirectoryNode,
@@ -16,9 +16,9 @@ import { LoggerService } from "@services/logger.service";
import { namespaces } from "@src/constants";
import { ProcessedRemoteCategory, RemoteTemplateCardWithFiles } from "@src/interfaces/store";
-const isFileNode = memoize((node: FileNode | DirectoryNode): node is FileNode => node?.type === "file");
+const isFileNode = (node: FileNode | DirectoryNode): node is FileNode => node?.type === "file";
-const isDirectoryNode = memoize((node: FileNode | DirectoryNode): node is DirectoryNode => node?.type === "directory");
+const isDirectoryNode = (node: FileNode | DirectoryNode): node is DirectoryNode => node?.type === "directory";
const directoryCache = new Map();
@@ -145,7 +145,7 @@ export const fetchAndUnpackZip = async (remoteTemplatesArchiveUrl: string): Prom
}
};
-const getFileName = memoize((path: string): string => path.split("/").pop() || path);
+const getFileName = memo((path: string): string => path.split("/").pop() || path);
const getDirectoryStructure = (fileStructure: FileStructure, targetPath: string): FileStructure | null => {
if (!targetPath) return fileStructure;
diff --git a/src/utilities/openedEditorFilesState.utils.ts b/src/utilities/openedEditorFilesState.utils.ts
index e8182535e6..993741bff1 100644
--- a/src/utilities/openedEditorFilesState.utils.ts
+++ b/src/utilities/openedEditorFilesState.utils.ts
@@ -1,8 +1,8 @@
-import { map, uniqBy } from "lodash";
+import { unique } from "radash";
export const updateOpenedFilesState = (files: { isActive: boolean; name: string }[], name: string) => {
- return uniqBy(
- [...map(files, (file) => ({ ...file, isActive: file.name === name })), { isActive: true, name }],
- "name"
+ return unique(
+ [...files.map((file) => ({ ...file, isActive: file.name === name })), { isActive: true, name }],
+ (f) => f.name
);
};
diff --git a/vite.config.ts b/vite.config.ts
index 49d795571e..fd70f8bf39 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,7 +1,8 @@
-import react from "@vitejs/plugin-react";
+import react from "@vitejs/plugin-react-swc";
import dotenv from "dotenv";
import fs from "fs";
import path from "path";
+import { visualizer } from "rollup-plugin-visualizer";
import { defineConfig } from "vite";
import { ViteEjsPlugin } from "vite-plugin-ejs";
import mkcert from "vite-plugin-mkcert";
@@ -30,11 +31,13 @@ export default defineConfig({
: 8000,
},
build: {
- sourcemap: true,
+ sourcemap: process.env.NODE_ENV === "production" ? "hidden" : true,
minify: "terser",
+ chunkSizeWarningLimit: 1000,
terserOptions: {
compress: {
dead_code: true,
+ drop_console: process.env.NODE_ENV === "production",
if_return: true,
unused: true,
reduce_vars: true,
@@ -42,6 +45,30 @@ export default defineConfig({
passes: 2,
},
},
+ rollupOptions: {
+ output: {
+ manualChunks: {
+ "vendor-react": ["react", "react-dom", "react-router-dom"],
+ "vendor-ui": [
+ "motion",
+ "swiper",
+ "react-select",
+ "@floating-ui/react",
+ "react-arborist",
+ "react-complex-tree",
+ ],
+ "vendor-editor": ["@monaco-editor/react", "monaco-editor-textmate", "monaco-textmate", "onigasm"],
+ "vendor-charts": ["apexcharts", "react-apexcharts"],
+ "vendor-pdf": ["pdfjs-dist", "react-pdf"],
+ "vendor-mermaid": ["mermaid"],
+ "vendor-monitoring": ["@sentry/react", "@datadog/browser-rum", "@datadog/browser-rum-react"],
+ "vendor-utils": ["radash", "dayjs", "zod", "zustand", "immer", "clsx", "tailwind-merge"],
+ "vendor-forms": ["react-hook-form", "@hookform/resolvers"],
+ "vendor-i18n": ["i18next", "react-i18next"],
+ "vendor-markdown": ["react-markdown", "remark-gfm", "remark-github-blockquote-alert"],
+ },
+ },
+ },
},
define: {
"import.meta.env.VITE_APP_VERSION": JSON.stringify(version),
@@ -145,6 +172,16 @@ export default defineConfig({
],
}),
reactVirtualized(),
+ ...(process.env.ANALYZE === "true"
+ ? [
+ visualizer({
+ open: true,
+ filename: "dist/stats.html",
+ gzipSize: true,
+ brotliSize: true,
+ }),
+ ]
+ : []),
],
resolve: {
alias: {