diff --git a/.version b/.version index e9763f6bfe..ad2261920c 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.23.0 +2.24.0 diff --git a/cypress-tests/cypress.config.js b/cypress-tests/cypress.config.js index fbe68ac201..d2a331a8d5 100644 --- a/cypress-tests/cypress.config.js +++ b/cypress-tests/cypress.config.js @@ -19,9 +19,9 @@ module.exports = defineConfig({ trashAssetsBeforeRuns: true, e2e: { - setupNodeEvents(on, config) { + setupNodeEvents (on, config) { on("task", { - readPdf(pathToPdf) { + readPdf (pathToPdf) { return new Promise((resolve) => { const pdfPath = path.resolve(pathToPdf); let dataBuffer = fs.readFileSync(pdfPath); @@ -33,7 +33,7 @@ module.exports = defineConfig({ }); on("task", { - readXlsx(filePath) { + readXlsx (filePath) { return new Promise((resolve, reject) => { try { let dataBuffer = fs.readFileSync(filePath); @@ -48,7 +48,7 @@ module.exports = defineConfig({ }); on("task", { - deleteFolder(folderName) { + deleteFolder (folderName) { return new Promise((resolve, reject) => { if (fs.existsSync(folderName)) { rmdir(folderName, { maxRetries: 10, recursive: true }, (err) => { @@ -66,7 +66,7 @@ module.exports = defineConfig({ }); on("task", { - updateId({ dbconfig, sql }) { + updateId ({ dbconfig, sql }) { const client = new pg.Pool(dbconfig); return client.query(sql); }, @@ -80,7 +80,7 @@ module.exports = defineConfig({ specPattern: "cypress/e2e/**/*.cy.js", downloadsFolder: "cypress/downloads", numTestsKeptInMemory: 0, - redirectionLimit: 7, + redirectionLimit: 10, experimentalRunAllSpecs: true, trashAssetsBeforeRuns: true, experimentalMemoryManagement: true, diff --git a/cypress-tests/cypress/commands/apiCommands.js b/cypress-tests/cypress/commands/apiCommands.js index 8d88670dc8..193422ad0f 100644 --- a/cypress-tests/cypress/commands/apiCommands.js +++ b/cypress-tests/cypress/commands/apiCommands.js @@ -163,3 +163,21 @@ Cypress.Commands.add("apiCreateWorkspace", (workspaceName, workspaceSlug) => { }); }); }); + +Cypress.Commands.add("logoutApi", () => { + cy.getCookie("tj_auth_token").then((cookie) => { + cy.request( + { + method: "GET", + url: "http://localhost:3000/api/logout", + headers: { + "Tj-Workspace-Id": Cypress.env("workspaceId"), + Cookie: `tj_auth_token=${cookie.value}`, + }, + }, + { log: false } + ).then((response) => { + expect(response.status).to.equal(200); + }); + }); +}); \ No newline at end of file diff --git a/cypress-tests/cypress/commands/commands.js b/cypress-tests/cypress/commands/commands.js index 016af0bf06..ae505b066d 100644 --- a/cypress-tests/cypress/commands/commands.js +++ b/cypress-tests/cypress/commands/commands.js @@ -108,18 +108,23 @@ Cypress.Commands.add( .find("pre.CodeMirror-line") .invoke("text") .then((text) => { - cy.wrap(subject).type(createBackspaceText(text), { delay: 0 }), + cy + .wrap(subject) + .last() + .click() + .type(createBackspaceText(text), { delay: 0 }), { delay: 0, }; }); if (!Array.isArray(value)) { - cy.wrap(subject).type(value, { + cy.wrap(subject).last().type(value, { parseSpecialCharSequences: false, delay: 0, }); } else { cy.wrap(subject) + .last() .type(value[1], { parseSpecialCharSequences: false, delay: 0, diff --git a/cypress-tests/cypress/constants/texts/workspaceConstants.js b/cypress-tests/cypress/constants/texts/workspaceConstants.js index 5a9a7af802..58a4af8568 100644 --- a/cypress-tests/cypress/constants/texts/workspaceConstants.js +++ b/cypress-tests/cypress/constants/texts/workspaceConstants.js @@ -3,7 +3,7 @@ export const workspaceConstantsText = { "To resolve a Workspace constant use {{constants.access_token}}", emptyStateHeader: "No Workspace constants yet", emptyStateText: - "Use Workspace constants seamlessly in both the app builder and global data source connections across ToolJet.", + "Use workspace constants seamlessly in both the app builder and data source connections across ToolJet.", addNewConstantButton: "Create new constant", addConstatntText: "Add new constant in production ", constantCreatedToast: "Constant has been created", diff --git a/cypress-tests/cypress/e2e/ce/appSlug.cy.js b/cypress-tests/cypress/e2e/ce/appSlug.cy.js new file mode 100644 index 0000000000..25369c9c16 --- /dev/null +++ b/cypress-tests/cypress/e2e/ce/appSlug.cy.js @@ -0,0 +1,39 @@ +import { commonSelectors, commonWidgetSelector } from "Selectors/common"; +import { fake } from "Fixtures/fake"; +import { logout, navigateToAppEditor, verifyTooltip, releaseApp } from "Support/utils/common"; +import { commonText } from "Texts/common"; +import { addNewUserMW } from "Support/utils/userPermissions"; +import { userSignUp } from "Support/utils/onboarding"; + +describe("App share functionality", () => { + const data = {}; + data.appName = `${fake.companyName} App`; + data.firstName = fake.firstName; + data.lastName = fake.lastName.replaceAll("[^A-Za-z]", ""); + data.email = fake.email.toLowerCase(); + const slug = data.appName.toLowerCase().replace(/\s+/g, "-"); + const firstUserEmail = data.email + const envVar = Cypress.env("environment"); + // beforeEach(() => { + // cy.appUILogin(); + // }); + before(() => { + cy.apiLogin(); + cy.apiCreateApp(data.appName); + // cy.visit('/') + // logout(); + }) + + it("", () => { + cy.openApp(data.appName); + + cy.get('[data-cy="left-sidebar-settings-button"]').click(); + cy.get('[data-cy="app-slug-label"]').verifyVisibleElement("have.text", "Unique app slug"); + cy.get('[data-cy="app-slug-input-field"]').verifyVisibleElement("have.value", Cypress.env("appId")); + cy.get('[data-cy="app-slug-info-label"]').verifyVisibleElement("have.text", "URL-friendly 'slug' consists of lowercase letters, numbers, and hyphens"); + cy.get('[data-cy="app-link-label"]').verifyVisibleElement("have.text", "App link"); + cy.get('[data-cy="app-link-field"]').verifyVisibleElement("have.text", `http://localhost:8082/my-workspace/apps/${Cypress.env("appId")}`) + + }) + +}); \ No newline at end of file diff --git a/cypress-tests/cypress/e2e/editor/app-version/version.cy.js b/cypress-tests/cypress/e2e/editor/app-version/version.cy.js index b3ade9630e..229a62260f 100644 --- a/cypress-tests/cypress/e2e/editor/app-version/version.cy.js +++ b/cypress-tests/cypress/e2e/editor/app-version/version.cy.js @@ -37,12 +37,18 @@ describe("App Version Functionality", () => { let currentVersion = ""; let newVersion = []; let versionFrom = ""; - beforeEach(() => { - cy.appUILogin(); + before(() => { + cy.apiLogin(); + cy.apiCreateApp(data.appName); + cy.logoutApi(); }); + beforeEach(() => { + cy.apiLogin(); + cy.visit('/my-workspace') + }) it("Verify the elements of the version module", () => { - cy.createApp(data.appName); + navigateToAppEditor(data.appName); cy.get(appVersionSelectors.appVersionLabel).should("be.visible"); cy.get(commonSelectors.appNameInput).verifyVisibleElement( "have.value", @@ -103,6 +109,6 @@ describe("App Version Functionality", () => { createNewVersion((newVersion = ["v6"]), (versionFrom = "v3")); verifyVersionAfterPreview((currentVersion = "v6")); - cy.go("back"); + }); }); diff --git a/cypress-tests/cypress/e2e/editor/inspectorHappypath.cy.js b/cypress-tests/cypress/e2e/editor/inspectorHappypath.cy.js index a8dce694c5..0008418468 100644 --- a/cypress-tests/cypress/e2e/editor/inspectorHappypath.cy.js +++ b/cypress-tests/cypress/e2e/editor/inspectorHappypath.cy.js @@ -77,27 +77,37 @@ describe("Editor- Inspector", () => { cy.get(multipageSelector.sidebarPageButton).click(); addNewPage("test_page"); - cy.dragAndDropWidget("Button", 100, 200); + cy.dragAndDropWidget("Button", 500, 500); selectEvent("On click", "Switch page"); cy.get('[data-cy="switch-page-label-and-input"] > .select-search') .click() .type("home{enter}"); cy.get('[data-cy="button-add-query-param"]').click(); + cy.wait(1000); + cy.get('[data-cy="button-add-query-param"]').click(); + addSupportCSAData("query-param-key", "key"); addSupportCSAData("query-param-value", "value"); + cy.get('[data-cy="switch-page-label-and-input"] > .select-search') + .click() + .type("home{enter}"); cy.get('[data-cy="real-canvas"]').click("topRight", { force: true }); - cy.dragAndDropWidget("Button", 100, 300); + cy.dragAndDropWidget("Button", 500, 300); selectEvent("On click", "Set variable"); addSupportCSAData("key", "globalVar"); addSupportCSAData("variable", "globalVar"); + cy.forceClickOnCanvas(); + cy.waitForAutoSave(); cy.get(commonWidgetSelector.draggableWidget("button2")).click(); cy.get('[data-cy="real-canvas"]').click("topRight", { force: true }); - cy.dragAndDropWidget("Button", 100, 400); + cy.dragAndDropWidget("Button", 500, 400); selectEvent("On click", "Set page variable"); addSupportCSAData("key", "pageVar"); addSupportCSAData("variable", "pageVar"); + cy.forceClickOnCanvas(); + cy.waitForAutoSave(); cy.get(commonWidgetSelector.draggableWidget("button3")).click(); cy.get(commonWidgetSelector.sidebarinspector).click(); @@ -147,7 +157,7 @@ describe("Editor- Inspector", () => { }); }); - cy.dragAndDropWidget("Button", 100, 300); + cy.dragAndDropWidget("Button", 500, 300); cy.get(commonWidgetSelector.sidebarinspector).click(); openNode("components"); cy.get(`[data-cy="inspector-node-button1"] > .mx-1`).realHover(); diff --git a/cypress-tests/cypress/e2e/editor/widget/buttonHappyPath.cy.js b/cypress-tests/cypress/e2e/editor/widget/buttonHappyPath.cy.js index e5318b6dcc..9207f78956 100644 --- a/cypress-tests/cypress/e2e/editor/widget/buttonHappyPath.cy.js +++ b/cypress-tests/cypress/e2e/editor/widget/buttonHappyPath.cy.js @@ -34,7 +34,7 @@ import { describe("Editor- Test Button widget", () => { beforeEach(() => { cy.apiLogin(); - cy.apiCreateApp(); + cy.apiCreateApp(`${fake.companyName}-App`); cy.openApp(); cy.dragAndDropWidget(buttonText.defaultWidgetText, 500, 500); }); @@ -76,6 +76,8 @@ describe("Editor- Test Button widget", () => { openEditorSidebar(data.widgetName); openAccordion(commonWidgetText.accordionEvents); addDefaultEventHandler(data.alertMessage); + cy.forceClickOnCanvas(); + cy.waitForAutoSave(); cy.get(commonWidgetSelector.draggableWidget(data.widgetName)).click(); cy.verifyToastMessage(commonSelectors.toastMessage, data.alertMessage); @@ -341,6 +343,7 @@ describe("Editor- Test Button widget", () => { }); it("Should verify csa", () => { + cy.get('[data-tooltip-content="Hide query panel"]').click(); // cy.dragAndDropWidget(buttonText.defaultWidgetText); selectEvent("On click", "Show alert"); @@ -359,7 +362,8 @@ describe("Editor- Test Button widget", () => { cy.dragAndDropWidget(buttonText.defaultWidgetText, 500, 150); selectEvent("On click", "Control Component"); selectCSA("button1", "Disable"); - cy.get('[data-cy="Value-toggle-button"]').click(); + cy.get('[data-cy="Value-fx-button"]').realClick(); + cy.get('[data-cy="Value-input-field"]').clearAndTypeOnCodeMirror(`{{true`); cy.get('[data-cy="real-canvas"]').click("topRight", { force: true }); cy.dragAndDropWidget(buttonText.defaultWidgetText, 500, 200); @@ -370,7 +374,9 @@ describe("Editor- Test Button widget", () => { cy.dragAndDropWidget(buttonText.defaultWidgetText, 500, 250); selectEvent("On click", "Control Component"); selectCSA("button1", "Loading"); - cy.get('[data-cy="Value-toggle-button"]').click(); + cy.wait(500); + cy.get('[data-cy="Value-fx-button"]').realClick(); + cy.get('[data-cy="Value-input-field"]').clearAndTypeOnCodeMirror(`{{true`); cy.get(commonWidgetSelector.draggableWidget("textinput1")).type("testBtn"); cy.wait(500); diff --git a/cypress-tests/cypress/e2e/editor/widget/csa.cy.js b/cypress-tests/cypress/e2e/editor/widget/csa.cy.js index 1094cf955d..d67a9c1f8a 100644 --- a/cypress-tests/cypress/e2e/editor/widget/csa.cy.js +++ b/cypress-tests/cypress/e2e/editor/widget/csa.cy.js @@ -21,6 +21,7 @@ describe("Editor- CSA", () => { cy.apiLogin(); cy.apiCreateApp(appName1); cy.openApp(); + cy.get('[data-tooltip-content="Hide query panel"]').click(); }); afterEach(() => { @@ -38,6 +39,8 @@ describe("Editor- CSA", () => { selectEvent("On click", "Control Component"); selectCSA("tabs1", "Set current tab"); addSupportCSAData("Id", "2"); + cy.forceClickOnCanvas(); + cy.waitForAutoSave(); cy.get(commonWidgetSelector.draggableWidget("button1")).click(); cy.get(".nav-link").eq(0).verifyVisibleElement("not.have.class", "active"); @@ -69,7 +72,14 @@ describe("Editor- CSA", () => { cy.get('[data-cy="draggable-widget-numberinput1"]') .click() .type(`{selectAll}{backspace}30{enter}`); + cy.wait(200); + cy.forceClickOnCanvas(); + + cy.waitForAutoSave(); cy.get(commonWidgetSelector.draggableWidget("button2")).click(); + cy.wait(200); + cy.get(commonWidgetSelector.draggableWidget("button2")).click(); + cy.verifyToastMessage( commonSelectors.toastMessage, "Form submitted successfully" @@ -79,6 +89,8 @@ describe("Editor- CSA", () => { cy.get('[data-cy="draggable-widget-numberinput1"]') .click() .type(`{selectAll}{backspace}20{enter}`); + cy.forceClickOnCanvas(); + cy.waitForAutoSave(); cy.get(commonWidgetSelector.draggableWidget("button3")).click(); cy.get('[data-cy="draggable-widget-numberinput1"]').should( "have.value", @@ -98,7 +110,8 @@ describe("Editor- CSA", () => { selectEvent("On click", "Control Component"); selectCSA("dropdown1", "Select option"); addSupportCSAData("Select", "{{3"); - + cy.forceClickOnCanvas(); + cy.waitForAutoSave(); cy.get(commonWidgetSelector.draggableWidget("button1")).click(); cy.get( '[data-cy="draggable-widget-dropdown1"] .css-1qrxvr1-singleValue' @@ -130,6 +143,8 @@ describe("Editor- CSA", () => { cy.get(commonWidgetSelector.draggableWidget("textarea1")) .should("be.visible") .and("have.text", "New Text"); + cy.forceClickOnCanvas(); + cy.waitForAutoSave(); cy.get(commonWidgetSelector.draggableWidget("button2")).click(); cy.get(commonWidgetSelector.draggableWidget("textarea1")) @@ -182,10 +197,13 @@ describe("Editor- CSA", () => { cy.dragAndDropWidget("Button", 500, 300); selectEvent("On click", "Control Component"); selectCSA("icon1", "Set Visibility"); - cy.get('[data-cy="Value-toggle-button"]').click(); - cy.get('[data-cy="Value-toggle-button"]') - .should("be.visible") - .and("not.be.checked"); + cy.get('[data-cy="Value-fx-button"]').click(); + cy.get('[data-cy="Value-input-field"]').clearAndTypeOnCodeMirror("{{false"); + // cy.get('[data-cy="Value-toggle-button"]') + // .should("be.visible") + // .and("not.be.checked"); + cy.forceClickOnCanvas(); + cy.waitForAutoSave(); cy.get(commonWidgetSelector.draggableWidget("button1")).click(); cy.verifyToastMessage( @@ -200,7 +218,7 @@ describe("Editor- CSA", () => { cy.get('[data-cy="draggable-widget-icon1"]').should("not.be.visible"); }); - it("Should verify Kanban CSA", () => { + it.only("Should verify Kanban CSA", () => { cy.viewport(1400, 1900); cy.dragAndDropWidget("Kanban", 50, 400); diff --git a/cypress-tests/cypress/e2e/editor/widget/listViewHappyPath.cy.js b/cypress-tests/cypress/e2e/editor/widget/listViewHappyPath.cy.js index 725c841bc3..fe21f3809d 100644 --- a/cypress-tests/cypress/e2e/editor/widget/listViewHappyPath.cy.js +++ b/cypress-tests/cypress/e2e/editor/widget/listViewHappyPath.cy.js @@ -35,7 +35,7 @@ import { describe("List view widget", () => { beforeEach(() => { cy.apiLogin(); - cy.apiCreateApp(); + cy.apiCreateApp(`${fake.companyName}-App`); cy.openApp(); cy.viewport(1200, 1200); cy.dragAndDropWidget("List View", 50, 500); @@ -164,6 +164,8 @@ describe("List view widget", () => { ) ); cy.get(commonWidgetSelector.buttonCloseEditorSideBar).click(); + cy.forceClickOnCanvas(); + cy.waitForAutoSave(); cy.get(`[data-cy=${data.widgetName.toLowerCase()}-row-1]`).click(); cy.verifyToastMessage(commonSelectors.toastMessage, data.marks[1]); diff --git a/cypress-tests/cypress/e2e/editor/widget/tableRegression.cy.js b/cypress-tests/cypress/e2e/editor/widget/tableRegression.cy.js index 1fea385edf..f08a07b469 100644 --- a/cypress-tests/cypress/e2e/editor/widget/tableRegression.cy.js +++ b/cypress-tests/cypress/e2e/editor/widget/tableRegression.cy.js @@ -275,6 +275,10 @@ describe("Table", () => { "have.text", "Button Position" ); // dropdown_type + cy.forceClickOnCanvas(); + cy.waitForAutoSave(); + openEditorSidebar(data.widgetName); + cy.get('[data-cy="pages-name-fakename1"]').click(); cy.get('[data-cy="rightActions-cell-2"]') .eq(0) @@ -291,6 +295,9 @@ describe("Table", () => { ); cy.get('[data-cy="add-event-handler"]').eq(1).click(); + cy.waitForAutoSave(); + openEditorSidebar(data.widgetName); + cy.get('[data-cy="pages-name-fakename1"]').click(); cy.get('[data-cy="leftActions-cell-0"]').eq(0).find("button").click(); cy.verifyToastMessage(commonSelectors.toastMessage, "Hello world!"); openEditorSidebar(data.widgetName); @@ -1098,7 +1105,7 @@ describe("Table", () => { verifyNodeData(tableText.defaultWidgetName, "Object", "22 entries "); cy.wait(1000); openNode(tableText.defaultWidgetName, 0, 1); - openNode(tableText.defaultWidgetName, 0, 1); + // openNode(tableText.defaultWidgetName, 0, 1); verifyNodeData("newRows", "Array", "1 item "); openNode("newRows"); verifyNodeData("0", "Object", "3 entries "); diff --git a/cypress-tests/cypress/e2e/exportImport/import.cy.js b/cypress-tests/cypress/e2e/exportImport/import.cy.js index d5763aeeb8..dab1aebc91 100644 --- a/cypress-tests/cypress/e2e/exportImport/import.cy.js +++ b/cypress-tests/cypress/e2e/exportImport/import.cy.js @@ -50,15 +50,20 @@ describe("App Import Functionality", () => { ); } }); - cy.get(importSelectors.importOptionInput).selectFile(toolJetImage, { + cy.get(importSelectors.importOptionInput).eq(0).selectFile(toolJetImage, { force: true, }); cy.verifyToastMessage( commonSelectors.toastMessage, importText.couldNotImportAppToastMessage ); - - cy.get(importSelectors.importOptionInput).selectFile(appFile, { + cy.reload(); + cy.get(importSelectors.dropDownMenu).should("be.visible").click(); + cy.get(importSelectors.importOptionLabel).verifyVisibleElement( + "have.text", + importText.importOption + ); + cy.get(importSelectors.importOptionInput).eq(0).selectFile(appFile, { force: true, }); cy.get('[data-cy="import-app-title"]').should("be.visible"); @@ -72,6 +77,7 @@ describe("App Import Functionality", () => { "contain.value", appData.name.toLowerCase() ); + cy.skipEditorPopover(); cy.modifyCanvasSize(900, 600); cy.dragAndDropWidget(buttonText.defaultWidgetText); cy.get(appVersionSelectors.appVersionLabel).should("be.visible"); diff --git a/cypress-tests/cypress/e2e/workspace/shareApp.cy.js b/cypress-tests/cypress/e2e/workspace/shareApp.cy.js index be5c35f03a..975bbb5034 100644 --- a/cypress-tests/cypress/e2e/workspace/shareApp.cy.js +++ b/cypress-tests/cypress/e2e/workspace/shareApp.cy.js @@ -57,7 +57,9 @@ describe("App share functionality", () => { cy.get(commonSelectors.editorPageLogo).click(); logout(); + cy.wait(2500); cy.visit(`/applications/${slug}`); + cy.wait(2500); cy.get(commonSelectors.loginButton).should("be.visible"); @@ -76,21 +78,34 @@ describe("App share functionality", () => { cy.get(commonSelectors.editorPageLogo).click(); logout(); + cy.wait(2500); cy.visit(`/applications/${slug}`); - cy.wait(500); + cy.wait(2500); cy.get('[data-cy="draggable-widget-table1"]').should("be.visible"); }); it("Verify app private and public app visibility for the same workspace user", () => { - addNewUserMW(data.firstName, data.email); + navigateToAppEditor(data.appName); + cy.wait(2000); + cy.get(commonWidgetSelector.shareAppButton).click(); + cy.get("body").then(($el) => { + if (!$el.text().includes("Embedded app link", { timeout: 2000 })) { + cy.get(commonWidgetSelector.makePublicAppToggle).check(); + } + }); + cy.get(commonWidgetSelector.modalCloseButton).click(); + cy.get(commonSelectors.editorPageLogo).click(); + addNewUserMW(data.firstName, data.email); logout(); + cy.visit(`/applications/${slug}`); cy.get('[data-cy="draggable-widget-table1"]').should("be.visible"); cy.appUILogin(); navigateToAppEditor(data.appName); - cy.skipEditorPopover() + cy.wait(2000); + cy.skipEditorPopover(); cy.get(commonWidgetSelector.shareAppButton).click(); cy.get(commonWidgetSelector.makePublicAppToggle).uncheck(); cy.get(commonWidgetSelector.modalCloseButton).click(); @@ -119,6 +134,10 @@ describe("App share functionality", () => { cy.clearAndType(commonSelectors.passwordInputField, "password"); cy.get(commonSelectors.signInButton).click(); cy.wait(1000); + cy.get(`[data-cy="workspace-sign-in-sub-header"]`).verifyVisibleElement( + "have.text", + "Sign in to your workspace - My workspace" + ); cy.visit("/"); cy.wait(2000); @@ -126,6 +145,7 @@ describe("App share functionality", () => { cy.appUILogin(); navigateToAppEditor(data.appName); + cy.wait(2000); cy.skipEditorPopover(); cy.get(commonWidgetSelector.shareAppButton).click(); cy.get(commonWidgetSelector.makePublicAppToggle).check(); diff --git a/cypress-tests/cypress/e2e/workspace/userPermissions.cy.js b/cypress-tests/cypress/e2e/workspace/userPermissions.cy.js index 826d783b13..5f8e029492 100644 --- a/cypress-tests/cypress/e2e/workspace/userPermissions.cy.js +++ b/cypress-tests/cypress/e2e/workspace/userPermissions.cy.js @@ -21,21 +21,21 @@ describe("User permissions", () => { cy.intercept("GET", "/api/apps?page=1&folder=&searchKey=").as("homePage"); cy.apiLogin(); cy.apiCreateApp(data.appName); - cy.visit('/') + cy.visit('/my-workspace') permissions.reset(); cy.get(commonSelectors.homePageLogo).click(); cy.wait("@homePage"); permissions.addNewUserMW(data.firstName, data.email); - common.logout(); + cy.logoutApi(); }); beforeEach(() => { - cy.appUILogin(); - cy.visitTheWorkspace("My workspace"); + cy.apiLogin(); + cy.visit("/my-workspace"); }); it("Should verify the create new app permission", () => { - common.logout(); - cy.login(data.email, usersText.password); + cy.logoutApi(); + cy.apiLogin(data.email, usersText.password); cy.get("body").then(($title) => { if ($title.text().includes(dashboardText.emptyPageDescription)) { cy.get(commonSelectors.dashboardAppCreateButton).should('be.disabled'); @@ -43,7 +43,7 @@ describe("User permissions", () => { cy.contains(dashboardText.createAppButton).should("not.exist"); } }); - common.logout(); + cy.logoutApi(); }); it("Should verify the View and Edit permission", () => { @@ -59,7 +59,9 @@ describe("User permissions", () => { }); common.logout(); - cy.login(data.email, usersText.password); + cy.apiLogin(data.email, usersText.password); + cy.visit("/my-workspace"); + cy.wait(500) cy.contains(data.appName).should("exist"); cy.get(commonSelectors.appCard(data.appName)).should( "contain.text", @@ -74,8 +76,11 @@ describe("User permissions", () => { "tj-disabled-btn" ); }); + common.logout(); - permissions.adminLogin(); + cy.apiLogin(); + cy.visit("/my-workspace"); + common.navigateToManageGroups(); cy.contains("tr", data.appName) .parent() .within(() => { @@ -87,7 +92,9 @@ describe("User permissions", () => { ); common.logout(); - cy.login(data.email, usersText.password); + cy.apiLogin(data.email, usersText.password); + cy.visit("/my-workspace"); + cy.wait(500) cy.get(commonSelectors.appCard(data.appName)).should( "contain.text", data.appName @@ -114,9 +121,10 @@ describe("User permissions", () => { it("Should verify the Create and Delete app permission", () => { data.appName = `${fake.companyName}-App`; - cy.createApp(data.appName); - cy.get(commonSelectors.editorPageLogo).click(); - cy.wait(1000); + cy.apiCreateApp(data.appName); + cy.visit('/my-workspace') + cy.wait(500); + common.navigateToManageGroups(); cy.get(groupsSelector.appSearchBox).click(); cy.get(groupsSelector.searchBoxOptions).contains(data.appName).click(); @@ -133,18 +141,24 @@ describe("User permissions", () => { cy.get(groupsSelector.permissionsLink).click(); cy.get(groupsSelector.appsDeleteCheck).check(); - common.logout(); - cy.login(data.email, usersText.password); + cy.logoutApi(); + cy.apiLogin(data.email, usersText.password); + cy.visit('/my-workspace'); cy.get(commonSelectors.appCreateButton).should("exist"); common.viewAppCardOptions(data.appName); cy.contains("Delete app").should("exist"); - permissions.adminLogin(); + common.logout(); + cy.apiLogin(); + cy.visit("/my-workspace"); + common.navigateToManageGroups(); cy.get(groupsSelector.permissionsLink).click(); cy.get(groupsSelector.appsDeleteCheck).uncheck(); - common.logout(); - cy.login(data.email, usersText.password); + cy.logoutApi(); + cy.apiLogin(data.email, usersText.password); + cy.visit("/my-workspace"); + cy.wait(1000) common.viewAppCardOptions(data.appName); cy.contains("Delete app").should("not.exist"); @@ -157,12 +171,17 @@ describe("User permissions", () => { cy.get(commonSelectors.appCardOptions(commonText.deleteAppOption)).click(); cy.get(commonSelectors.buttonSelector("Yes")).click(); - permissions.adminLogin(); + common.logout + cy.apiLogin(); + cy.visit("/my-workspace"); + common.navigateToManageGroups(); cy.get(groupsSelector.permissionsLink).click(); cy.get(groupsSelector.appsCreateCheck).uncheck(); - common.logout(); - cy.login(data.email, usersText.password); + cy.logoutApi(); + cy.apiLogin(data.email, usersText.password); + cy.visit("/my-workspace"); + cy.wait(1000) cy.contains("Create new application").should("not.exist"); }); @@ -171,8 +190,10 @@ describe("User permissions", () => { cy.get(groupsSelector.permissionsLink).click(); cy.get(groupsSelector.foldersCreateCheck).check(); - common.logout(); - cy.login(data.email, usersText.password); + cy.logoutApi(); + cy.apiLogin(data.email, usersText.password); + cy.visit("/my-workspace"); + cy.wait(500) cy.get(commonSelectors.createNewFolderButton).click(); cy.clearAndType(commonSelectors.folderNameInput, data.folderName); @@ -188,15 +209,22 @@ describe("User permissions", () => { }); cy.get(commonSelectors.deleteFolderOption(data.folderName)).click(); cy.get(commonSelectors.buttonSelector("Yes")).click(); + common.logout(); - permissions.adminLogin(); + cy.apiLogin(); + cy.visit("/my-workspace"); + common.navigateToManageGroups(); cy.get(groupsSelector.permissionsLink).click(); cy.get(groupsSelector.foldersCreateCheck).uncheck(); common.logout(); - cy.login(data.email, usersText.password); + cy.apiLogin(data.email, usersText.password); + cy.visit("/my-workspace"); + cy.wait(500) - permissions.adminLogin(); + cy.apiLogin(); + cy.visit("/my-workspace"); + common.navigateToManageGroups(); cy.contains("td", data.appName) .parent() .within(() => { @@ -204,7 +232,9 @@ describe("User permissions", () => { }); common.logout(); - cy.login(data.email, usersText.password); + cy.apiLogin(data.email, usersText.password); + cy.visit("/my-workspace"); + cy.wait(500) cy.contains(data.appName).should("not.exist"); common.logout(); @@ -223,7 +253,9 @@ describe("User permissions", () => { ).verifyVisibleElement("have.text", "Go to workspace constants"); common.logout(); - cy.login(data.email, usersText.password); + cy.apiLogin(data.email, usersText.password); + cy.visit("/my-workspace"); + cy.wait(500) common.navigateToWorkspaceVariable(); cy.get('[data-cy="alert-info-text"]>>.text-muted').verifyVisibleElement( "have.text", diff --git a/cypress-tests/cypress/e2e/workspace/workspaceConstants.cy.js b/cypress-tests/cypress/e2e/workspace/workspaceConstants.cy.js index 0d8258136b..8212dc20d8 100644 --- a/cypress-tests/cypress/e2e/workspace/workspaceConstants.cy.js +++ b/cypress-tests/cypress/e2e/workspace/workspaceConstants.cy.js @@ -47,7 +47,9 @@ describe("Workspace constants", () => { .click(); cy.get(commonSelectors.breadcrumbTitle).should(($el) => { - expect($el.contents().first().text().trim()).to.eq("Workspace settings"); + expect($el.contents().first().text().trim()).to.eq( + "Workspace settings" + ); }); cy.get(commonSelectors.breadcrumbPageTitle).verifyVisibleElement( "have.text", @@ -67,7 +69,9 @@ describe("Workspace constants", () => { ); cy.get("body").then(($body) => { - if ($body.find(workspaceConstantsSelectors.emptyStateImage).length > 0) { + if ( + $body.find(workspaceConstantsSelectors.emptyStateImage).length > 0 + ) { cy.get(workspaceConstantsSelectors.emptyStateImage).should( "be.visible" ); @@ -77,7 +81,9 @@ describe("Workspace constants", () => { "have.text", workspaceConstantsText.emptyStateHeader ); - cy.get(workspaceConstantsSelectors.emptyStateText).verifyVisibleElement( + cy.get( + workspaceConstantsSelectors.emptyStateText + ).verifyVisibleElement( "have.text", workspaceConstantsText.emptyStateText ); @@ -94,7 +100,10 @@ describe("Workspace constants", () => { "have.text", workspaceConstantsText.addConstatntText ); - cy.get(commonSelectors.nameLabel).verifyVisibleElement("have.text", "Name"); + cy.get(commonSelectors.nameLabel).verifyVisibleElement( + "have.text", + "Name" + ); cy.get(commonSelectors.nameInputField) .invoke("attr", "placeholder") .should("eq", "Enter Constant Name"); @@ -111,11 +120,12 @@ describe("Workspace constants", () => { "have.text", "Cancel" ); - cy.get(workspaceConstantsSelectors.addConstantButton).verifyVisibleElement( - "have.text", - "Add constant" + cy.get( + workspaceConstantsSelectors.addConstantButton + ).verifyVisibleElement("have.text", "Add constant"); + cy.get(workspaceConstantsSelectors.addConstantButton).should( + "be.disabled" ); - cy.get(workspaceConstantsSelectors.addConstantButton).should("be.disabled"); contantsNameValidation(" ", commonText.constantsNameError); contantsNameValidation("9", commonText.constantsNameError); @@ -134,13 +144,17 @@ describe("Workspace constants", () => { "have.text", commonText.constantsValueError ); - cy.get(workspaceConstantsSelectors.addConstantButton).should("be.disabled"); + cy.get(workspaceConstantsSelectors.addConstantButton).should( + "be.disabled" + ); cy.get(commonSelectors.cancelButton).click(); cy.get(workspaceConstantsSelectors.addNewConstantButton).click(); cy.clearAndType(commonSelectors.nameInputField, data.constName); cy.clearAndType(commonSelectors.valueInputField, data.constName); - cy.get(workspaceConstantsSelectors.addConstantButton).should("be.enabled"); + cy.get(workspaceConstantsSelectors.addConstantButton).should( + "be.enabled" + ); cy.get(commonSelectors.cancelButton).click(); cy.get(workspaceConstantsSelectors.constantName(data.constName)).should( "not.exist" @@ -189,14 +203,22 @@ describe("Workspace constants", () => { ).verifyVisibleElement("have.text", "Delete"); cy.get(commonSelectors.pagination).should("be.visible"); - cy.get(workspaceConstantsSelectors.constEditButton(data.constName)).click(); + cy.get( + workspaceConstantsSelectors.constEditButton(data.constName) + ).click(); cy.get(workspaceConstantsSelectors.contantFormTitle).verifyVisibleElement( "have.text", "Update constant in production " ); - cy.get(commonSelectors.nameLabel).verifyVisibleElement("have.text", "Name"); - cy.get(commonSelectors.nameInputField).should("have.value", data.constName); + cy.get(commonSelectors.nameLabel).verifyVisibleElement( + "have.text", + "Name" + ); + cy.get(commonSelectors.nameInputField).should( + "have.value", + data.constName + ); cy.get(commonSelectors.nameInputField) .should("be.visible") .and("be.disabled"); @@ -211,20 +233,25 @@ describe("Workspace constants", () => { "have.text", "Cancel" ); - cy.get(workspaceConstantsSelectors.addConstantButton).verifyVisibleElement( - "have.text", - "Update" + cy.get( + workspaceConstantsSelectors.addConstantButton + ).verifyVisibleElement("have.text", "Update"); + cy.get(workspaceConstantsSelectors.addConstantButton).should( + "be.disabled" ); - cy.get(workspaceConstantsSelectors.addConstantButton).should("be.disabled"); cy.clearAndType(commonSelectors.valueInputField, data.newConstvalue); - cy.get(workspaceConstantsSelectors.addConstantButton).should("be.enabled"); + cy.get(workspaceConstantsSelectors.addConstantButton).should( + "be.enabled" + ); cy.get(commonSelectors.cancelButton).click(); cy.get( workspaceConstantsSelectors.constantValue(data.constName) ).verifyVisibleElement("have.text", data.constName); - cy.get(workspaceConstantsSelectors.constEditButton(data.constName)).click(); + cy.get( + workspaceConstantsSelectors.constEditButton(data.constName) + ).click(); cy.clearAndType(commonSelectors.valueInputField, data.newConstvalue); cy.get(workspaceConstantsSelectors.addConstantButton).click(); cy.verifyToastMessage( @@ -246,7 +273,10 @@ describe("Workspace constants", () => { "have.text", "Cancel" ); - cy.get(commonSelectors.yesButton).verifyVisibleElement("have.text", "Yes"); + cy.get(commonSelectors.yesButton).verifyVisibleElement( + "have.text", + "Yes" + ); cy.get(commonSelectors.cancelButton).click(); cy.get( workspaceConstantsSelectors.constantValue(data.constName) @@ -312,47 +342,31 @@ describe("Workspace constants", () => { `[data-cy="inspector-node-${data.constantsName}"] > .mx-2` ).verifyVisibleElement("have.text", `"dJ_8Q~BcaMPd"`); + cy.get('[data-cy="button-release"]').click(); + cy.get('[data-cy="yes-button"]').click(); + cy.verifyToastMessage(commonSelectors.toastMessage, "Version v1 released"); - if (envVar === "Community") { - cy.get('[data-cy="button-release"]').click(); - cy.get('[data-cy="yes-button"]').click(); - cy.verifyToastMessage( - commonSelectors.toastMessage, - "Version v1 released" - ); + cy.get(commonWidgetSelector.shareAppButton).click(); + cy.clearAndType(commonWidgetSelector.appNameSlugInput, `${data.slug}`); + cy.wait(1500); + cy.get(commonWidgetSelector.modalCloseButton).click(); + cy.forceClickOnCanvas(); + cy.waitForAutoSave(); + cy.wait(500) + cy.openInCurrentTab(commonWidgetSelector.previewButton); + cy.wait(4000); - cy.get(commonWidgetSelector.shareAppButton).click(); - cy.clearAndType(commonWidgetSelector.appNameSlugInput, `${data.slug}`); - cy.get(commonWidgetSelector.modalCloseButton).click(); - cy.forceClickOnCanvas(); - cy.waitForAutoSave(); - cy.openInCurrentTab(commonWidgetSelector.previewButton); - cy.wait(4000); + cy.get( + commonWidgetSelector.draggableWidget(data.constantsName) + ).verifyVisibleElement("have.text", "dJ_8Q~BcaMPd"); - cy.get( - commonWidgetSelector.draggableWidget(data.constantsName) - ).verifyVisibleElement("have.text", "dJ_8Q~BcaMPd"); + cy.get('[data-cy="viewer-page-logo"]').click(); + cy.wait("@homePage"); + cy.visit(`/applications/${data.slug}`); + cy.wait(4000); - cy.get('[data-cy="viewer-page-logo"]').click(); - cy.wait("@homePage"); - cy.visit(`/applications/${data.slug}`); - cy.wait(4000); - - cy.get( - commonWidgetSelector.draggableWidget(data.constantsName) - ).verifyVisibleElement("have.text", "dJ_8Q~BcaMPd"); - } else { - cy.forceClickOnCanvas(); - cy.waitForAutoSave(); - cy.openInCurrentTab(commonWidgetSelector.previewButton); - cy.wait(4000); - - cy.get( - commonWidgetSelector.draggableWidget(data.constantsName) - ).verifyVisibleElement("have.text", "dJ_8Q~BcaMPd"); - - cy.get('[data-cy="viewer-page-logo"]').click(); - cy.wait("@homePage"); - } + cy.get( + commonWidgetSelector.draggableWidget(data.constantsName) + ).verifyVisibleElement("have.text", "dJ_8Q~BcaMPd"); }); -}); \ No newline at end of file +}); diff --git a/cypress-tests/cypress/support/utils/button.js b/cypress-tests/cypress/support/utils/button.js index 1ed2ed70e2..2662b7a839 100644 --- a/cypress-tests/cypress/support/utils/button.js +++ b/cypress-tests/cypress/support/utils/button.js @@ -22,7 +22,12 @@ export const verifyControlComponentAction = (widgetName, value) => { cy.get(commonWidgetSelector.componentTextInput) .find('[data-cy*="-input-field"]') .clearAndTypeOnCodeMirror(value); + cy.forceClickOnCanvas(); + cy.waitForAutoSave(); cy.get(commonWidgetSelector.draggableWidget(widgetName)).click(); - cy.get(commonWidgetSelector.draggableWidget('textinput1')).should("have.value", value); + cy.get(commonWidgetSelector.draggableWidget("textinput1")).should( + "have.value", + value + ); }; diff --git a/cypress-tests/cypress/support/utils/version.js b/cypress-tests/cypress/support/utils/version.js index 1db2df7359..860bd8d44b 100644 --- a/cypress-tests/cypress/support/utils/version.js +++ b/cypress-tests/cypress/support/utils/version.js @@ -151,5 +151,7 @@ export const verifyVersionAfterPreview = (currentVersion) => { .click(); cy.url().should("include", "/home"); verifyComponent("button1"); + cy.go("back"); + cy.waitForAppLoad() cy.contains(currentVersion); }; diff --git a/docs/package-lock.json b/docs/package-lock.json index acf95dd8c1..3a88b082a6 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -12,7 +12,6 @@ "@docusaurus/plugin-google-gtag": "^2.4.3", "@docusaurus/plugin-sitemap": "^2.4.3", "@docusaurus/preset-classic": "^2.4.3", - "@mdx-js/react": "^1.6.22", "clsx": "^1.2.1", "plugin-image-zoom": "github:flexanalytics/plugin-image-zoom", "react": "^17.0.2", @@ -192,16 +191,81 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dependencies": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/compat-data": { "version": "7.18.13", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.13.tgz", @@ -240,12 +304,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.13.tgz", - "integrity": "sha512-CkPg8ySSPuHTYPJYo7IRALdqyjM9HCbt/3uOBEFbzyGVP6Mn8bwFPB0jX6982JVNBlYzM1nnPkfjuXSOPtQeEQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dependencies": { - "@babel/types": "^7.18.13", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "engines": { @@ -355,9 +420,9 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "engines": { "node": ">=6.9.0" } @@ -374,23 +439,23 @@ } }, "node_modules/@babel/helper-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", - "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dependencies": { - "@babel/template": "^7.18.6", - "@babel/types": "^7.18.9" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -510,28 +575,28 @@ } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", - "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", - "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "engines": { "node": ">=6.9.0" } @@ -572,12 +637,12 @@ } }, "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { @@ -621,6 +686,14 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/@babel/highlight/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -641,9 +714,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.13.tgz", - "integrity": "sha512-dgXcIfMuQ0kgzLB2b9tRZs7TTFFaGM2AbtA4fJgUUYukzGH4jwsS7hzQHEGs67jdehpm22vkgKwvbU+aEflgwg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "bin": { "parser": "bin/babel-parser.js" }, @@ -1868,31 +1941,31 @@ } }, "node_modules/@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.13.tgz", - "integrity": "sha512-N6kt9X1jRMLPxxxPYWi7tgvJRH/rtoU+dbKAPDM44RFHiMH8igdsaSBgFeskhSl/kLWLDUvIh1RXCrTmg0/zvA==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.13", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.18.13", - "@babel/types": "^7.18.13", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -1901,12 +1974,12 @@ } }, "node_modules/@babel/types": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.13.tgz", - "integrity": "sha512-ePqfTihzW0W6XAU+aMw2ykilisStJfDnsejDCXRchCcMJ4O0+8DhPXf2YUbZ6wjBlsEmZwLK/sPweWtu8hcJYQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dependencies": { - "@babel/helper-string-parser": "^7.18.10", - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -2047,9 +2120,9 @@ } }, "node_modules/@docusaurus/core/node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -2552,17 +2625,6 @@ "node": ">=16.14" } }, - "node_modules/@docusaurus/utils/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@hapi/hoek": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.0.tgz", @@ -2590,9 +2652,9 @@ } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", - "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", "engines": { "node": ">=6.0.0" } @@ -2606,26 +2668,26 @@ } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", + "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", - "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==" + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.15", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", - "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@leichtgewicht/ip-codec": { @@ -2804,9 +2866,9 @@ } }, "node_modules/@sideway/formula": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz", - "integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" }, "node_modules/@sideway/pinpoint": { "version": "2.0.0", @@ -2814,11 +2876,14 @@ "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" }, "node_modules/@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", + "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", "engines": { - "node": ">=6" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" } }, "node_modules/@slorber/static-site-generator-webpack-plugin": { @@ -3280,14 +3345,14 @@ } }, "node_modules/@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", "dependencies": { - "defer-to-connect": "^1.0.1" + "defer-to-connect": "^2.0.1" }, "engines": { - "node": ">=6" + "node": ">=14.16" } }, "node_modules/@trysound/sax": { @@ -3351,9 +3416,9 @@ } }, "node_modules/@types/estree": { - "version": "0.0.51", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.3.tgz", + "integrity": "sha512-CS2rOaoQ/eAgAfcTfq6amKG7bsN+EMcgGY4FAFQdvSj2y1ixvOZTUA9mOtCai7E1SYu283XNw7urKK30nP3wkQ==" }, "node_modules/@types/express": { "version": "4.17.13", @@ -3394,6 +3459,11 @@ "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.0.0.tgz", "integrity": "sha512-NZwaaynfs1oIoLAV1vg18e7QMVDvw+6SQrdJc8w3BwUaoroVSf6EBj/Sk4PBWGxsq0dzhA2drbsuMC1/6C6KgQ==" }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.3.tgz", + "integrity": "sha512-V46MYLFp08Wf2mmaBhvgjStM3tPa+2GAdy/iqoX+noX1//zje2x4XmrIU0cAwyClATsTmahbtoQ2EwP7I5WSiA==" + }, "node_modules/@types/http-proxy": { "version": "1.17.9", "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", @@ -3546,133 +3616,133 @@ } }, "node_modules/@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", + "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" } }, "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==" + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==" }, "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==" + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==" }, "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==" + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", + "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==" }, "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==" + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==" }, "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", + "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6" } }, "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", "dependencies": { "@xtuc/ieee754": "^1.2.0" } }, "node_modules/@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", "dependencies": { "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==" + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==" }, "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", + "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-opt": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6", + "@webassemblyjs/wast-printer": "1.11.6" } }, "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", + "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", + "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6" } }, "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", + "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", + "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", "dependencies": { - "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/ast": "1.11.6", "@xtuc/long": "4.2.2" } }, @@ -3699,9 +3769,9 @@ } }, "node_modules/acorn": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", "bin": { "acorn": "bin/acorn" }, @@ -3709,6 +3779,14 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-import-assertions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "peerDependencies": { + "acorn": "^8" + } + }, "node_modules/acorn-walk": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.0.2.tgz", @@ -3848,9 +3926,9 @@ } }, "node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "engines": { "node": ">=8" } @@ -4329,9 +4407,9 @@ } }, "node_modules/browserslist": { - "version": "4.21.10", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", - "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", + "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", "funding": [ { "type": "opencollective", @@ -4347,10 +4425,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001517", - "electron-to-chromium": "^1.4.477", + "caniuse-lite": "^1.0.30001541", + "electron-to-chromium": "^1.4.535", "node-releases": "^2.0.13", - "update-browserslist-db": "^1.0.11" + "update-browserslist-db": "^1.0.13" }, "bin": { "browserslist": "cli.js" @@ -4372,51 +4450,40 @@ "node": ">= 0.8" } }, + "node_modules/cacheable-lookup": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", + "engines": { + "node": ">=14.16" + } + }, "node_modules/cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "version": "10.2.14", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", + "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" + "@types/http-cache-semantics": "^4.0.2", + "get-stream": "^6.0.1", + "http-cache-semantics": "^4.1.1", + "keyv": "^4.5.3", + "mimic-response": "^4.0.0", + "normalize-url": "^8.0.0", + "responselike": "^3.0.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/cacheable-request/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cacheable-request/node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "engines": { - "node": ">=8" + "node": ">=14.16" } }, "node_modules/cacheable-request/node_modules/normalize-url": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", - "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz", + "integrity": "sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==", "engines": { - "node": ">=8" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/call-bind": { @@ -4479,9 +4546,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001559", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001559.tgz", - "integrity": "sha512-cPiMKZgqgkg5LY3/ntGeLFUpi6tzddBNS58A4tnTgQw1zON7u2sZMU7SzOeVH4tj20++9ggL+V6FDOFMTaFFYA==", + "version": "1.0.30001554", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001554.tgz", + "integrity": "sha512-A2E3U//MBwbJVzebddm1YfNp7Nud5Ip+IPn4BozBmn4KqVX7AvluoIDFWjsv5OkGnKUXQVmMSoMKLa3ScCblcQ==", "funding": [ { "type": "opencollective", @@ -4773,14 +4840,6 @@ "node": ">=6" } }, - "node_modules/clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", - "dependencies": { - "mimic-response": "^1.0.0" - } - }, "node_modules/clsx": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", @@ -5051,26 +5110,17 @@ } }, "node_modules/core-js-compat": { - "version": "3.24.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.24.1.tgz", - "integrity": "sha512-XhdNAGeRnTpp8xbD+sR/HFDK9CbeeeqXT6TuofXh3urqEevzkWmLRgrVoykodsw8okqo2pu1BOmuCKrHx63zdw==", + "version": "3.33.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.33.1.tgz", + "integrity": "sha512-6pYKNOgD/j/bkC5xS5IIg6bncid3rfrI42oBH1SQJbsmYPKF7rhzcFzYCcxYMmNQQ0rCEB8WqpW7QHndOggaeQ==", "dependencies": { - "browserslist": "^4.21.3", - "semver": "7.0.0" + "browserslist": "^4.22.1" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/core-js" } }, - "node_modules/core-js-compat/node_modules/semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/core-js-pure": { "version": "3.24.1", "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.24.1.tgz", @@ -5233,9 +5283,9 @@ } }, "node_modules/css-loader/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -5457,14 +5507,28 @@ } }, "node_modules/decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "dependencies": { - "mimic-response": "^1.0.0" + "mimic-response": "^3.1.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/deep-extend": { @@ -5495,9 +5559,12 @@ } }, "node_modules/defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "engines": { + "node": ">=10" + } }, "node_modules/define-lazy-prop": { "version": "2.0.0", @@ -5785,11 +5852,6 @@ "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" }, - "node_modules/duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" - }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -5801,9 +5863,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.477", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.477.tgz", - "integrity": "sha512-shUVy6Eawp33dFBFIoYbIwLHrX0IZ857AlH9ug2o4rvbWmpaCUdBpQ5Zw39HRrfzAFm4APJE9V+E2A/WB0YqJw==" + "version": "1.4.568", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.568.tgz", + "integrity": "sha512-3TCOv8+BY6Ltpt1/CmGBMups2IdKOyfEmz4J8yIS4xLSeMm0Rf+psSaxLuswG9qMKt+XbNbmADybtXGpTFlbDg==" }, "node_modules/emoji-regex": { "version": "8.0.0", @@ -5835,18 +5897,10 @@ "node": ">= 0.8" } }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dependencies": { - "once": "^1.4.0" - } - }, "node_modules/enhanced-resolve": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", - "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", + "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -5875,9 +5929,9 @@ } }, "node_modules/es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==" + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.1.tgz", + "integrity": "sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q==" }, "node_modules/escalade": { "version": "3.1.1", @@ -5901,11 +5955,14 @@ "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "engines": { - "node": ">=0.8.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/eslint-scope": { @@ -6466,9 +6523,9 @@ } }, "node_modules/fork-ts-checker-webpack-plugin/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -6487,6 +6544,14 @@ "node": ">=6" } }, + "node_modules/form-data-encoder": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", + "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", + "engines": { + "node": ">= 14.17" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -6718,35 +6783,27 @@ } }, "node_modules/got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", + "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", "dependencies": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" + "@sindresorhus/is": "^5.2.0", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.8", + "decompress-response": "^6.0.0", + "form-data-encoder": "^2.1.2", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^3.0.0" }, "engines": { - "node": ">=8.6" - } - }, - "node_modules/got/node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dependencies": { - "pump": "^3.0.0" + "node": ">=14.16" }, - "engines": { - "node": ">=6" + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" } }, "node_modules/graceful-fs": { @@ -7116,9 +7173,9 @@ } }, "node_modules/http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" }, "node_modules/http-deceiver": { "version": "1.2.7", @@ -7192,6 +7249,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/http2-wrapper": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.0.tgz", + "integrity": "sha512-kZB0wxMo0sh1PehyjJUWRFEd99KC5TLjZ2cULC4f9iqJBAmKQQXEICjxl5iPJRwP40dpeHFqqhm7tYCvODpqpQ==", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -7716,9 +7785,9 @@ } }, "node_modules/json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", @@ -7731,9 +7800,9 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "node_modules/json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "bin": { "json5": "lib/cli.js" }, @@ -7753,11 +7822,11 @@ } }, "node_modules/keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dependencies": { - "json-buffer": "3.0.0" + "json-buffer": "3.0.1" } }, "node_modules/kind-of": { @@ -7825,9 +7894,9 @@ } }, "node_modules/loader-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", - "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -7898,11 +7967,14 @@ } }, "node_modules/lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", "engines": { - "node": ">=0.10.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/lru-cache": { @@ -8102,11 +8174,14 @@ } }, "node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", "engines": { - "node": ">=4" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/mini-create-react-context": { @@ -8146,9 +8221,9 @@ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" }, "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -8157,9 +8232,12 @@ } }, "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/ms": { "version": "2.1.2", @@ -8423,11 +8501,11 @@ } }, "node_modules/p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", "engines": { - "node": ">=6" + "node": ">=12.20" } }, "node_modules/p-limit": { @@ -8752,9 +8830,9 @@ } }, "node_modules/postcss": { - "version": "8.4.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.27.tgz", - "integrity": "sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==", + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "funding": [ { "type": "opencollective", @@ -8902,9 +8980,9 @@ } }, "node_modules/postcss-loader/node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -9276,14 +9354,6 @@ "postcss": "^8.2.15" } }, - "node_modules/prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", - "engines": { - "node": ">=4" - } - }, "node_modules/pretty-error": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", @@ -9384,15 +9454,6 @@ "node": ">= 0.10" } }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "node_modules/punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -9458,6 +9519,17 @@ } ] }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -9594,25 +9666,6 @@ "node": ">=14" } }, - "node_modules/react-dev-utils/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/react-dev-utils/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/react-dev-utils/node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -9629,9 +9682,9 @@ } }, "node_modules/react-dev-utils/node_modules/loader-utils": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.0.tgz", - "integrity": "sha512-HVl9ZqccQihZ7JM85dco1MvO9G+ONvxoGa9rkhzFsneGLKSUg1gJf9bWzhRhcvm2qChhWpebQhP44qxjKIUCaQ==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", + "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", "engines": { "node": ">= 12.13.0" } @@ -9896,14 +9949,14 @@ } }, "node_modules/recursive-readdir": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", - "integrity": "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", "dependencies": { - "minimatch": "3.0.4" + "minimatch": "^3.0.5" }, "engines": { - "node": ">=0.10.0" + "node": ">=6.0.0" } }, "node_modules/regenerate": { @@ -10179,14 +10232,6 @@ "strip-ansi": "^6.0.1" } }, - "node_modules/renderkid/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, "node_modules/renderkid/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -10243,6 +10288,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -10257,11 +10307,17 @@ "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" }, "node_modules/responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", + "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", "dependencies": { - "lowercase-keys": "^1.0.0" + "lowercase-keys": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/retry": { @@ -10509,9 +10565,9 @@ } }, "node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -10580,23 +10636,23 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", "dependencies": { "randombytes": "^2.1.0" } }, "node_modules/serve-handler": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.3.tgz", - "integrity": "sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w==", + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.5.tgz", + "integrity": "sha512-ijPFle6Hwe8zfmBxJdE+5fta53fdIY0lHISJvuikXB3VYFafRjMRpOffSPvCYsbKyBA7pvy9oYr/BT1O3EArlg==", "dependencies": { "bytes": "3.0.0", "content-disposition": "0.5.2", "fast-url-parser": "1.1.3", "mime-types": "2.1.18", - "minimatch": "3.0.4", + "minimatch": "3.1.2", "path-is-inside": "1.0.2", "path-to-regexp": "2.2.1", "range-parser": "1.2.0" @@ -11126,12 +11182,12 @@ } }, "node_modules/terser": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.0.tgz", - "integrity": "sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==", + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.22.0.tgz", + "integrity": "sha512-hHZVLgRA2z4NWcN6aS5rQDc+7Dcy58HOf2zbYwmFcQ+ua3h6eEFf5lIDKTzbWwlazPyOZsFQO8V80/IjVNExEw==", "dependencies": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -11143,15 +11199,15 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.5", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.5.tgz", - "integrity": "sha512-AOEDLDxD2zylUGf/wxHxklEkOe2/r+seuyOWujejFrIxHf11brA1/dWQNIgXa1c6/Wkxgu7zvv0JhOWfc2ELEA==", + "version": "5.3.9", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", + "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.14", + "@jridgewell/trace-mapping": "^0.3.17", "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "terser": "^5.14.1" + "serialize-javascript": "^6.0.1", + "terser": "^5.16.8" }, "engines": { "node": ">= 10.13.0" @@ -11225,14 +11281,6 @@ "node": ">=4" } }, - "node_modules/to-readable-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", - "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", - "engines": { - "node": ">=6" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -11266,9 +11314,9 @@ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, "node_modules/trim": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", - "integrity": "sha512-YzQV+TZg4AxpKxaTHK3c3D+kRDCGVEE7LemdlQZoQXn0iennk10RsIoY6ikzAqJTc9Xjl9C1/waHom/J86ziAQ==", + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.3.tgz", + "integrity": "sha512-h82ywcYhHK7veeelXrCScdH7HkWfbIT1D/CgYO+nmDarz3SGNssVBMws6jU16Ga60AJCRAvPV6w6RLuNerQqjg==", "deprecated": "Use String.prototype.trim() instead" }, "node_modules/trim-trailing-lines": { @@ -11339,9 +11387,9 @@ } }, "node_modules/ua-parser-js": { - "version": "1.0.37", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz", - "integrity": "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==", + "version": "1.0.36", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.36.tgz", + "integrity": "sha512-znuyCIXzl8ciS3+y3fHJI/2OhQIXbXw9MWC/o3qwyR+RGppjZHrM27CGFSKCJXi2Kctiz537iOu2KnXs1lMQhw==", "funding": [ { "type": "opencollective", @@ -11553,9 +11601,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", - "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "funding": [ { "type": "opencollective", @@ -11630,9 +11678,9 @@ } }, "node_modules/update-notifier/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -11694,17 +11742,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", - "dependencies": { - "prepend-http": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/use-composed-ref": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.3.0.tgz", @@ -11887,21 +11924,21 @@ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "node_modules/webpack": { - "version": "5.74.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz", - "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", + "version": "5.89.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz", + "integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==", "dependencies": { "@types/eslint-scope": "^3.7.3", - "@types/estree": "^0.0.51", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", + "@types/estree": "^1.0.0", + "@webassemblyjs/ast": "^1.11.5", + "@webassemblyjs/wasm-edit": "^1.11.5", + "@webassemblyjs/wasm-parser": "^1.11.5", "acorn": "^8.7.1", - "acorn-import-assertions": "^1.7.6", + "acorn-import-assertions": "^1.9.0", "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.10.0", - "es-module-lexer": "^0.9.0", + "enhanced-resolve": "^5.15.0", + "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", @@ -11910,9 +11947,9 @@ "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", + "schema-utils": "^3.2.0", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", + "terser-webpack-plugin": "^5.3.7", "watchpack": "^2.4.0", "webpack-sources": "^3.2.3" }, @@ -12078,18 +12115,10 @@ "node": ">=10.13.0" } }, - "node_modules/webpack/node_modules/acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "peerDependencies": { - "acorn": "^8" - } - }, "node_modules/webpack/node_modules/schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -12218,9 +12247,9 @@ } }, "node_modules/ws": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.5.tgz", - "integrity": "sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g==", + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", "engines": { "node": ">=8.3.0" }, @@ -12459,11 +12488,63 @@ } }, "@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "requires": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } } }, "@babel/compat-data": { @@ -12494,12 +12575,13 @@ } }, "@babel/generator": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.13.tgz", - "integrity": "sha512-CkPg8ySSPuHTYPJYo7IRALdqyjM9HCbt/3uOBEFbzyGVP6Mn8bwFPB0jX6982JVNBlYzM1nnPkfjuXSOPtQeEQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "requires": { - "@babel/types": "^7.18.13", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "dependencies": { @@ -12575,9 +12657,9 @@ } }, "@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==" + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==" }, "@babel/helper-explode-assignable-expression": { "version": "7.18.6", @@ -12588,20 +12670,20 @@ } }, "@babel/helper-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", - "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "requires": { - "@babel/template": "^7.18.6", - "@babel/types": "^7.18.9" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" } }, "@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-member-expression-to-functions": { @@ -12688,22 +12770,22 @@ } }, "@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-string-parser": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", - "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==" + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==" }, "@babel/helper-validator-identifier": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", - "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==" + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==" }, "@babel/helper-validator-option": { "version": "7.18.6", @@ -12732,12 +12814,12 @@ } }, "@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "requires": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "dependencies": { @@ -12772,6 +12854,11 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -12788,9 +12875,9 @@ } }, "@babel/parser": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.13.tgz", - "integrity": "sha512-dgXcIfMuQ0kgzLB2b9tRZs7TTFFaGM2AbtA4fJgUUYukzGH4jwsS7hzQHEGs67jdehpm22vkgKwvbU+aEflgwg==" + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==" }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.18.6", @@ -13577,39 +13664,39 @@ } }, "@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" } }, "@babel/traverse": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.13.tgz", - "integrity": "sha512-N6kt9X1jRMLPxxxPYWi7tgvJRH/rtoU+dbKAPDM44RFHiMH8igdsaSBgFeskhSl/kLWLDUvIh1RXCrTmg0/zvA==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.13", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.18.13", - "@babel/types": "^7.18.13", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.13.tgz", - "integrity": "sha512-ePqfTihzW0W6XAU+aMw2ykilisStJfDnsejDCXRchCcMJ4O0+8DhPXf2YUbZ6wjBlsEmZwLK/sPweWtu8hcJYQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "requires": { - "@babel/helper-string-parser": "^7.18.10", - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" } }, @@ -13714,9 +13801,9 @@ }, "dependencies": { "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "requires": { "lru-cache": "^6.0.0" } @@ -14057,13 +14144,6 @@ "tslib": "^2.4.0", "url-loader": "^4.1.1", "webpack": "^5.73.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" - } } }, "@docusaurus/utils-common": { @@ -14110,9 +14190,9 @@ } }, "@jridgewell/resolve-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", - "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==" }, "@jridgewell/set-array": { "version": "1.1.2", @@ -14120,26 +14200,26 @@ "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==" }, "@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", + "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", "requires": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" } }, "@jridgewell/sourcemap-codec": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", - "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==" + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "@jridgewell/trace-mapping": { - "version": "0.3.15", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", - "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "@leichtgewicht/ip-codec": { @@ -14277,9 +14357,9 @@ } }, "@sideway/formula": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz", - "integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" }, "@sideway/pinpoint": { "version": "2.0.0", @@ -14287,9 +14367,9 @@ "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" }, "@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", + "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==" }, "@slorber/static-site-generator-webpack-plugin": { "version": "4.0.7", @@ -14517,11 +14597,11 @@ } }, "@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", "requires": { - "defer-to-connect": "^1.0.1" + "defer-to-connect": "^2.0.1" } }, "@trysound/sax": { @@ -14582,9 +14662,9 @@ } }, "@types/estree": { - "version": "0.0.51", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.3.tgz", + "integrity": "sha512-CS2rOaoQ/eAgAfcTfq6amKG7bsN+EMcgGY4FAFQdvSj2y1ixvOZTUA9mOtCai7E1SYu283XNw7urKK30nP3wkQ==" }, "@types/express": { "version": "4.17.13", @@ -14625,6 +14705,11 @@ "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.0.0.tgz", "integrity": "sha512-NZwaaynfs1oIoLAV1vg18e7QMVDvw+6SQrdJc8w3BwUaoroVSf6EBj/Sk4PBWGxsq0dzhA2drbsuMC1/6C6KgQ==" }, + "@types/http-cache-semantics": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.3.tgz", + "integrity": "sha512-V46MYLFp08Wf2mmaBhvgjStM3tPa+2GAdy/iqoX+noX1//zje2x4XmrIU0cAwyClATsTmahbtoQ2EwP7I5WSiA==" + }, "@types/http-proxy": { "version": "1.17.9", "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", @@ -14777,133 +14862,133 @@ } }, "@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", + "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", "requires": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" } }, "@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==" + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==" }, "@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==" + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==" }, "@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==" + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", + "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==" }, "@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", "requires": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", "@xtuc/long": "4.2.2" } }, "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==" + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==" }, "@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", + "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6" } }, "@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", "requires": { "@xtuc/ieee754": "^1.2.0" } }, "@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", "requires": { "@xtuc/long": "4.2.2" } }, "@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==" + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==" }, "@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", + "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-opt": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6", + "@webassemblyjs/wast-printer": "1.11.6" } }, "@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", + "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, "@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", + "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6" } }, "@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", + "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, "@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", + "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", "requires": { - "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/ast": "1.11.6", "@xtuc/long": "4.2.2" } }, @@ -14927,9 +15012,15 @@ } }, "acorn": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==" + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==" + }, + "acorn-import-assertions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "requires": {} }, "acorn-walk": { "version": "8.0.2", @@ -15036,9 +15127,9 @@ "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==" }, "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" }, "ansi-styles": { "version": "4.3.0", @@ -15378,14 +15469,14 @@ } }, "browserslist": { - "version": "4.21.10", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", - "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", + "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", "requires": { - "caniuse-lite": "^1.0.30001517", - "electron-to-chromium": "^1.4.477", + "caniuse-lite": "^1.0.30001541", + "electron-to-chromium": "^1.4.535", "node-releases": "^2.0.13", - "update-browserslist-db": "^1.0.11" + "update-browserslist-db": "^1.0.13" } }, "buffer-from": { @@ -15398,37 +15489,29 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" }, + "cacheable-lookup": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==" + }, "cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "version": "10.2.14", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", + "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", "requires": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" + "@types/http-cache-semantics": "^4.0.2", + "get-stream": "^6.0.1", + "http-cache-semantics": "^4.1.1", + "keyv": "^4.5.3", + "mimic-response": "^4.0.0", + "normalize-url": "^8.0.0", + "responselike": "^3.0.0" }, "dependencies": { - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "requires": { - "pump": "^3.0.0" - } - }, - "lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" - }, "normalize-url": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", - "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==" + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz", + "integrity": "sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==" } } }, @@ -15477,9 +15560,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001559", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001559.tgz", - "integrity": "sha512-cPiMKZgqgkg5LY3/ntGeLFUpi6tzddBNS58A4tnTgQw1zON7u2sZMU7SzOeVH4tj20++9ggL+V6FDOFMTaFFYA==" + "version": "1.0.30001554", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001554.tgz", + "integrity": "sha512-A2E3U//MBwbJVzebddm1YfNp7Nud5Ip+IPn4BozBmn4KqVX7AvluoIDFWjsv5OkGnKUXQVmMSoMKLa3ScCblcQ==" }, "ccount": { "version": "1.1.0", @@ -15662,14 +15745,6 @@ "shallow-clone": "^3.0.0" } }, - "clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", - "requires": { - "mimic-response": "^1.0.0" - } - }, "clsx": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", @@ -15867,19 +15942,11 @@ "integrity": "sha512-0QTBSYSUZ6Gq21utGzkfITDylE8jWC9Ne1D2MrhvlsZBI1x39OdDIVbzSqtgMndIy6BlHxBXpMGqzZmnztg2rg==" }, "core-js-compat": { - "version": "3.24.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.24.1.tgz", - "integrity": "sha512-XhdNAGeRnTpp8xbD+sR/HFDK9CbeeeqXT6TuofXh3urqEevzkWmLRgrVoykodsw8okqo2pu1BOmuCKrHx63zdw==", + "version": "3.33.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.33.1.tgz", + "integrity": "sha512-6pYKNOgD/j/bkC5xS5IIg6bncid3rfrI42oBH1SQJbsmYPKF7rhzcFzYCcxYMmNQQ0rCEB8WqpW7QHndOggaeQ==", "requires": { - "browserslist": "^4.21.3", - "semver": "7.0.0" - }, - "dependencies": { - "semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==" - } + "browserslist": "^4.22.1" } }, "core-js-pure": { @@ -15987,9 +16054,9 @@ } }, "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "requires": { "lru-cache": "^6.0.0" } @@ -16127,11 +16194,18 @@ } }, "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "requires": { - "mimic-response": "^1.0.0" + "mimic-response": "^3.1.0" + }, + "dependencies": { + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" + } } }, "deep-extend": { @@ -16153,9 +16227,9 @@ } }, "defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==" }, "define-lazy-prop": { "version": "2.0.0", @@ -16374,11 +16448,6 @@ "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" - }, "eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -16390,9 +16459,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "electron-to-chromium": { - "version": "1.4.477", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.477.tgz", - "integrity": "sha512-shUVy6Eawp33dFBFIoYbIwLHrX0IZ857AlH9ug2o4rvbWmpaCUdBpQ5Zw39HRrfzAFm4APJE9V+E2A/WB0YqJw==" + "version": "1.4.568", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.568.tgz", + "integrity": "sha512-3TCOv8+BY6Ltpt1/CmGBMups2IdKOyfEmz4J8yIS4xLSeMm0Rf+psSaxLuswG9qMKt+XbNbmADybtXGpTFlbDg==" }, "emoji-regex": { "version": "8.0.0", @@ -16414,18 +16483,10 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "requires": { - "once": "^1.4.0" - } - }, "enhanced-resolve": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", - "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", + "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", "requires": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -16445,9 +16506,9 @@ } }, "es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==" + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.1.tgz", + "integrity": "sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q==" }, "escalade": { "version": "3.1.1", @@ -16465,9 +16526,9 @@ "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" }, "eslint-scope": { "version": "5.1.1", @@ -16880,9 +16941,9 @@ } }, "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "requires": { "lru-cache": "^6.0.0" } @@ -16894,6 +16955,11 @@ } } }, + "form-data-encoder": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", + "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==" + }, "forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -17058,31 +17124,21 @@ } }, "got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", + "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", "requires": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" - }, - "dependencies": { - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "requires": { - "pump": "^3.0.0" - } - } + "@sindresorhus/is": "^5.2.0", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.8", + "decompress-response": "^6.0.0", + "form-data-encoder": "^2.1.2", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^3.0.0" } }, "graceful-fs": { @@ -17369,9 +17425,9 @@ } }, "http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" }, "http-deceiver": { "version": "1.2.7", @@ -17424,6 +17480,15 @@ } } }, + "http2-wrapper": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.0.tgz", + "integrity": "sha512-kZB0wxMo0sh1PehyjJUWRFEd99KC5TLjZ2cULC4f9iqJBAmKQQXEICjxl5iPJRwP40dpeHFqqhm7tYCvODpqpQ==", + "requires": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + } + }, "human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -17775,9 +17840,9 @@ "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==" }, "json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" }, "json-parse-even-better-errors": { "version": "2.3.1", @@ -17790,9 +17855,9 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==" + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" }, "jsonfile": { "version": "6.1.0", @@ -17804,11 +17869,11 @@ } }, "keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "requires": { - "json-buffer": "3.0.0" + "json-buffer": "3.0.1" } }, "kind-of": { @@ -17855,9 +17920,9 @@ "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==" }, "loader-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", - "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "requires": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -17919,9 +17984,9 @@ } }, "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==" }, "lru-cache": { "version": "6.0.0", @@ -18061,9 +18126,9 @@ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" }, "mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==" }, "mini-create-react-context": { "version": "0.4.1", @@ -18088,17 +18153,17 @@ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" }, "ms": { "version": "2.1.2", @@ -18276,9 +18341,9 @@ "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==" }, "p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==" }, "p-limit": { "version": "2.3.0", @@ -18323,7 +18388,7 @@ "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", "requires": { - "got": "^9.6.0", + "got": "^12.1.0", "registry-auth-token": "^4.0.0", "registry-url": "^5.0.0", "semver": "^6.2.0" @@ -18517,9 +18582,9 @@ } }, "postcss": { - "version": "8.4.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.27.tgz", - "integrity": "sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==", + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "requires": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", @@ -18598,9 +18663,9 @@ }, "dependencies": { "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "requires": { "lru-cache": "^6.0.0" } @@ -18823,11 +18888,6 @@ "integrity": "sha512-fgFMf0OtVSBR1va1JNHYgMxYk73yhn/qb4uQDq1DLGYolz8gHCyr/sesEuGUaYs58E3ZJRcpoGuPVoB7Meiq9A==", "requires": {} }, - "prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" - }, "pretty-error": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", @@ -18909,15 +18969,6 @@ } } }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -18957,6 +19008,11 @@ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -19064,16 +19120,6 @@ "text-table": "^0.2.0" }, "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" - }, "find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -19084,9 +19130,9 @@ } }, "loader-utils": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.0.tgz", - "integrity": "sha512-HVl9ZqccQihZ7JM85dco1MvO9G+ONvxoGa9rkhzFsneGLKSUg1gJf9bWzhRhcvm2qChhWpebQhP44qxjKIUCaQ==" + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", + "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==" }, "locate-path": { "version": "6.0.0", @@ -19283,11 +19329,11 @@ } }, "recursive-readdir": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", - "integrity": "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", "requires": { - "minimatch": "3.0.4" + "minimatch": "^3.0.5" } }, "regenerate": { @@ -19479,7 +19525,7 @@ "parse-entities": "^2.0.0", "repeat-string": "^1.5.4", "state-toggle": "^1.0.0", - "trim": "0.0.1", + "trim": "^0.0.3", "trim-trailing-lines": "^1.0.0", "unherit": "^1.0.4", "unist-util-remove-position": "^2.0.0", @@ -19507,11 +19553,6 @@ "strip-ansi": "^6.0.1" }, "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, "strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -19552,6 +19593,11 @@ "supports-preserve-symlinks-flag": "^1.0.0" } }, + "resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" + }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -19563,11 +19609,11 @@ "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" }, "responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", + "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", "requires": { - "lowercase-keys": "^1.0.0" + "lowercase-keys": "^3.0.0" } }, "retry": { @@ -19745,9 +19791,9 @@ } }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" }, "semver-diff": { "version": "3.1.1", @@ -19805,23 +19851,23 @@ } }, "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", "requires": { "randombytes": "^2.1.0" } }, "serve-handler": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.3.tgz", - "integrity": "sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w==", + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.5.tgz", + "integrity": "sha512-ijPFle6Hwe8zfmBxJdE+5fta53fdIY0lHISJvuikXB3VYFafRjMRpOffSPvCYsbKyBA7pvy9oYr/BT1O3EArlg==", "requires": { "bytes": "3.0.0", "content-disposition": "0.5.2", "fast-url-parser": "1.1.3", "mime-types": "2.1.18", - "minimatch": "3.0.4", + "minimatch": "3.1.2", "path-is-inside": "1.0.2", "path-to-regexp": "2.2.1", "range-parser": "1.2.0" @@ -20219,12 +20265,12 @@ "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==" }, "terser": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.0.tgz", - "integrity": "sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==", + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.22.0.tgz", + "integrity": "sha512-hHZVLgRA2z4NWcN6aS5rQDc+7Dcy58HOf2zbYwmFcQ+ua3h6eEFf5lIDKTzbWwlazPyOZsFQO8V80/IjVNExEw==", "requires": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -20237,15 +20283,15 @@ } }, "terser-webpack-plugin": { - "version": "5.3.5", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.5.tgz", - "integrity": "sha512-AOEDLDxD2zylUGf/wxHxklEkOe2/r+seuyOWujejFrIxHf11brA1/dWQNIgXa1c6/Wkxgu7zvv0JhOWfc2ELEA==", + "version": "5.3.9", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", + "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", "requires": { - "@jridgewell/trace-mapping": "^0.3.14", + "@jridgewell/trace-mapping": "^0.3.17", "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "terser": "^5.14.1" + "serialize-javascript": "^6.0.1", + "terser": "^5.16.8" }, "dependencies": { "schema-utils": { @@ -20285,11 +20331,6 @@ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" }, - "to-readable-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", - "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" - }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -20314,9 +20355,9 @@ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, "trim": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", - "integrity": "sha512-YzQV+TZg4AxpKxaTHK3c3D+kRDCGVEE7LemdlQZoQXn0iennk10RsIoY6ikzAqJTc9Xjl9C1/waHom/J86ziAQ==" + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.3.tgz", + "integrity": "sha512-h82ywcYhHK7veeelXrCScdH7HkWfbIT1D/CgYO+nmDarz3SGNssVBMws6jU16Ga60AJCRAvPV6w6RLuNerQqjg==" }, "trim-trailing-lines": { "version": "1.1.4", @@ -20362,9 +20403,9 @@ "peer": true }, "ua-parser-js": { - "version": "1.0.37", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz", - "integrity": "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==" + "version": "1.0.36", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.36.tgz", + "integrity": "sha512-znuyCIXzl8ciS3+y3fHJI/2OhQIXbXw9MWC/o3qwyR+RGppjZHrM27CGFSKCJXi2Kctiz537iOu2KnXs1lMQhw==" }, "unherit": { "version": "1.1.3", @@ -20494,9 +20535,9 @@ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" }, "update-browserslist-db": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", - "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "requires": { "escalade": "^3.1.1", "picocolors": "^1.0.0" @@ -20539,9 +20580,9 @@ } }, "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "requires": { "lru-cache": "^6.0.0" } @@ -20578,14 +20619,6 @@ } } }, - "url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", - "requires": { - "prepend-http": "^2.0.0" - } - }, "use-composed-ref": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.3.0.tgz", @@ -20712,21 +20745,21 @@ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "webpack": { - "version": "5.74.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz", - "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", + "version": "5.89.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz", + "integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==", "requires": { "@types/eslint-scope": "^3.7.3", - "@types/estree": "^0.0.51", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", + "@types/estree": "^1.0.0", + "@webassemblyjs/ast": "^1.11.5", + "@webassemblyjs/wasm-edit": "^1.11.5", + "@webassemblyjs/wasm-parser": "^1.11.5", "acorn": "^8.7.1", - "acorn-import-assertions": "^1.7.6", + "acorn-import-assertions": "^1.9.0", "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.10.0", - "es-module-lexer": "^0.9.0", + "enhanced-resolve": "^5.15.0", + "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", @@ -20735,23 +20768,17 @@ "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", + "schema-utils": "^3.2.0", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", + "terser-webpack-plugin": "^5.3.7", "watchpack": "^2.4.0", "webpack-sources": "^3.2.3" }, "dependencies": { - "acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "requires": {} - }, "schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "requires": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -20943,9 +20970,9 @@ } }, "ws": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.5.tgz", - "integrity": "sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g==", + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", "requires": {} }, "xdg-basedir": { diff --git a/docs/package.json b/docs/package.json index b0e7eefe18..49a34d9336 100644 --- a/docs/package.json +++ b/docs/package.json @@ -18,12 +18,15 @@ "@docusaurus/plugin-google-gtag": "^2.4.3", "@docusaurus/plugin-sitemap": "^2.4.3", "@docusaurus/preset-classic": "^2.4.3", - "@mdx-js/react": "^1.6.22", "clsx": "^1.2.1", "plugin-image-zoom": "github:flexanalytics/plugin-image-zoom", "react": "^17.0.2", "react-dom": "^17.0.2" }, + "overrides": { + "got": "^12.1.0", + "trim": "^0.0.3" + }, "browserslist": { "production": [ ">0.5%", diff --git a/frontend/.version b/frontend/.version index e9763f6bfe..ad2261920c 100644 --- a/frontend/.version +++ b/frontend/.version @@ -1 +1 @@ -2.23.0 +2.24.0 diff --git a/frontend/src/AppLoader/AppLoader.jsx b/frontend/src/AppLoader/AppLoader.jsx index 1c5a8d6314..c54c0e3c68 100644 --- a/frontend/src/AppLoader/AppLoader.jsx +++ b/frontend/src/AppLoader/AppLoader.jsx @@ -1,11 +1,41 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import { withTranslation } from 'react-i18next'; import { Editor } from '../Editor/Editor'; import { RealtimeEditor } from '@/Editor/RealtimeEditor'; import config from 'config'; +import { appService } from '@/_services'; +import { useAppDataActions } from '@/_stores/appDataStore'; -const AppLoaderComponent = (props) => { - return config.ENABLE_MULTIPLAYER_EDITING ? : ; -}; +const AppLoaderComponent = React.memo((props) => { + const [shouldLoadApp, setShouldLoadApp] = React.useState(false); + const { updateState } = useAppDataActions(); + + useEffect(() => { + props?.id && props?.slug && loadAppDetails(props?.id); + + return () => { + setShouldLoadApp(false); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const loadAppDetails = (appId) => { + appService.fetchApp(appId, 'edit').then((data) => { + setShouldLoadApp(true); + updateState({ + app: data, + appId: data.id, + }); + }); + }; + + if (!shouldLoadApp) return <>; + + return config.ENABLE_MULTIPLAYER_EDITING ? ( + + ) : ( + + ); +}); export const AppLoader = withTranslation()(AppLoaderComponent); diff --git a/frontend/src/Editor/AppVersionsManager/CreateVersionModal.jsx b/frontend/src/Editor/AppVersionsManager/CreateVersionModal.jsx index cb445c9d8f..2801f278b0 100644 --- a/frontend/src/Editor/AppVersionsManager/CreateVersionModal.jsx +++ b/frontend/src/Editor/AppVersionsManager/CreateVersionModal.jsx @@ -17,6 +17,7 @@ export const CreateVersion = ({ }) => { const [isCreatingVersion, setIsCreatingVersion] = useState(false); const [versionName, setVersionName] = useState(''); + const { t } = useTranslation(); const { editingVersion } = useAppVersionStore( (state) => ({ @@ -25,6 +26,14 @@ export const CreateVersion = ({ shallow ); + const options = appVersions.map((version) => { + return { label: version.name, value: version }; + }); + + const [selectedVersion, setSelectedVersion] = useState( + () => options.find((option) => option?.value?.id === editingVersion?.id)?.value + ); + const createVersion = () => { if (versionName.trim().length > 25) { toast.error('Version name should not be longer than 25 characters'); @@ -36,18 +45,26 @@ export const CreateVersion = ({ } setIsCreatingVersion(true); + appVersionService - .create(appId, versionName, editingVersion.id) - .then(() => { + .create(appId, versionName, selectedVersion.id) + .then((data) => { toast.success('Version Created'); appVersionService.getAll(appId).then((data) => { setVersionName(''); setIsCreatingVersion(false); setAppVersions(data.versions); - const latestVersion = data.versions.at(0); - setAppDefinitionFromVersion(latestVersion); setShowCreateAppVersion(false); }); + + appVersionService + .getAppVersionData(appId, data.id) + .then((data) => { + setAppDefinitionFromVersion(data); + }) + .catch((error) => { + toast.error(error); + }); }) .catch((error) => { toast.error(error?.error); @@ -55,10 +72,6 @@ export const CreateVersion = ({ }); }; - const options = appVersions.map((version) => { - return { label: version.name, value: version }; - }); - return ( { - const height = e.target.value; - if (!Number.isNaN(height) && height <= 2400) globalSettingsChanged('canvasMaxHeight', height); - }} - value={canvasMaxHeight} - /> - - - */} +
{t('leftSidebar.Settings.backgroundColorOfCanvas', 'Canvas bavkground')} @@ -313,8 +303,12 @@ export const GlobalSettings = ({ onFocus={() => setShowPicker(true)} color={canvasBackgroundColor} onChangeComplete={(color) => { - globalSettingsChanged('canvasBackgroundColor', [color.hex, color.rgb]); - globalSettingsChanged('backgroundFxQuery', color.hex); + const options = { + canvasBackgroundColor: [color.hex, color.rgb], + backgroundFxQuery: color.hex, + }; + + globalSettingsChanged(options); }} />
@@ -357,8 +351,11 @@ export const GlobalSettings = ({ className="canvas-hinter-wrap" lineNumbers={false} onChange={(color) => { - globalSettingsChanged('canvasBackgroundColor', resolveReferences(color, realState)); - globalSettingsChanged('backgroundFxQuery', color); + const options = { + canvasBackgroundColor: resolveReferences(color, realState), + backgroundFxQuery: color, + }; + globalSettingsChanged(options); }} /> )} diff --git a/frontend/src/Editor/Header/index.js b/frontend/src/Editor/Header/index.js index 5a3c99c332..dd2d7d26c1 100644 --- a/frontend/src/Editor/Header/index.js +++ b/frontend/src/Editor/Header/index.js @@ -14,30 +14,29 @@ import { useUpdatePresence } from '@y-presence/react'; import { useAppVersionStore } from '@/_stores/appVersionStore'; import { useCurrentState } from '@/_stores/currentStateStore'; import { shallow } from 'zustand/shallow'; +import { useAppInfo, useCurrentUser } from '@/_stores/appDataStore'; import SolidIcon from '@/_ui/Icon/SolidIcons'; import { redirectToDashboard } from '@/_helpers/routes'; export default function EditorHeader({ M, - app, - appVersionPreviewLink, - slug, - appId, canUndo, canRedo, handleUndo, handleRedo, - isSaving, saveError, onNameChanged, setAppDefinitionFromVersion, - handleSlugChange, onVersionRelease, saveEditingVersion, onVersionDelete, - currentUser, + slug, darkMode, }) { + const currentUser = useCurrentUser(); + + const { isSaving, appId, appName, app, isPublic, appVersionPreviewLink } = useAppInfo(); + const { isVersionReleased, editingVersion } = useAppVersionStore( (state) => ({ isVersionReleased: state.isVersionReleased, @@ -48,6 +47,7 @@ export default function EditorHeader({ const currentState = useCurrentState(); const updatePresence = useUpdatePresence(); + useEffect(() => { const initialPresence = { firstName: currentUser?.first_name ?? '', @@ -62,7 +62,9 @@ export default function EditorHeader({ updatePresence(initialPresence); // eslint-disable-next-line react-hooks/exhaustive-deps }, [currentUser]); - const handleLogoClick = () => { + + const handleLogoClick = (e) => { + e.preventDefault(); // Force a reload for clearing interval triggers redirectToDashboard(); }; @@ -96,7 +98,7 @@ export default function EditorHeader({ }} >
- +
@@ -133,9 +135,9 @@ export default function EditorHeader({ {editingVersion && ( )}
@@ -147,15 +149,16 @@ export default function EditorHeader({ >
- {app.id && ( + {appId && ( )}
@@ -172,14 +175,12 @@ export default function EditorHeader({
- {app.id && ( - - )} +
diff --git a/frontend/src/Editor/Inspector/ActionConfigurationPanels/GotoApp.jsx b/frontend/src/Editor/Inspector/ActionConfigurationPanels/GotoApp.jsx index 618a074e94..d42ebee133 100644 --- a/frontend/src/Editor/Inspector/ActionConfigurationPanels/GotoApp.jsx +++ b/frontend/src/Editor/Inspector/ActionConfigurationPanels/GotoApp.jsx @@ -69,7 +69,7 @@ export function GotoApp({ getAllApps, event, handlerChanged, eventIndex, darkMod
queryParamChangeHandler(index, 0, value)} mode="javascript" height={30} @@ -77,7 +77,7 @@ export function GotoApp({ getAllApps, event, handlerChanged, eventIndex, darkMod
queryParamChangeHandler(index, 1, value)} mode="javascript" height={30} diff --git a/frontend/src/Editor/Inspector/ActionConfigurationPanels/SwitchPage.jsx b/frontend/src/Editor/Inspector/ActionConfigurationPanels/SwitchPage.jsx index cd3dffe52e..dc697c4797 100644 --- a/frontend/src/Editor/Inspector/ActionConfigurationPanels/SwitchPage.jsx +++ b/frontend/src/Editor/Inspector/ActionConfigurationPanels/SwitchPage.jsx @@ -69,7 +69,7 @@ export function SwitchPage({ getPages, event, handlerChanged, eventIndex, darkMo
queryParamChangeHandler(index, 0, value)} mode="javascript" className="form-control codehinter-query-editor-input" @@ -79,7 +79,7 @@ export function SwitchPage({ getPages, event, handlerChanged, eventIndex, darkMo
queryParamChangeHandler(index, 1, value)} mode="javascript" className="form-control codehinter-query-editor-input" diff --git a/frontend/src/Editor/Inspector/Components/Chart.jsx b/frontend/src/Editor/Inspector/Components/Chart.jsx index 4c2c20c890..88e55d1e01 100644 --- a/frontend/src/Editor/Inspector/Components/Chart.jsx +++ b/frontend/src/Editor/Inspector/Components/Chart.jsx @@ -56,17 +56,16 @@ class Chart extends React.Component { } render() { - const { dataQueries, component, paramUpdated, componentMeta, components, currentState } = this.state; - const data = this.state.component.component.definition.properties.data; + const { dataQueries, component, paramUpdated, componentMeta, components, currentState } = this.props; + const data = this.props.component.component.definition.properties.data; // since component is not unmounting on every render in current scenario - const jsonDescription = this.state.component.component.definition.properties.jsonDescription; + const jsonDescription = this.props.component.component.definition.properties.jsonDescription; const plotFromJson = resolveReferences( - this.state.component.component.definition.properties.plotFromJson?.value, + this.props.component.component.definition.properties.plotFromJson?.value, currentState ); - - const chartType = this.state.component.component.definition.properties.type.value; + const chartType = this.props.component.component.definition.properties.type.value; let items = []; diff --git a/frontend/src/Editor/Inspector/Components/DefaultComponent.jsx b/frontend/src/Editor/Inspector/Components/DefaultComponent.jsx index b0c9cf0199..d61d01213d 100644 --- a/frontend/src/Editor/Inspector/Components/DefaultComponent.jsx +++ b/frontend/src/Editor/Inspector/Components/DefaultComponent.jsx @@ -98,8 +98,9 @@ export const baseComponentProperties = ( isOpen: true, children: ( { - if (component.parent === id && component?.component?.component === 'Button') { + if (component.component.parent === id && component?.component?.component === 'Button') { newOptions.push({ name: component.component.name, value: componentId }); } }); @@ -94,8 +94,9 @@ export const baseComponentProperties = ( isOpen: true, children: ( { const actions = this.props.component.component.definition.properties.actions; actions.value[index][property] = value; - this.props.paramUpdated({ name: 'actions' }, 'value', actions.value, 'properties'); + this.props.paramUpdated({ name: 'actions' }, 'value', actions.value, 'properties', true); }; actionButtonEventsChanged = (events, index) => { let actions = this.props.component.component.definition.properties.actions.value; actions[index]['events'] = events; - this.props.paramUpdated({ name: 'actions' }, 'value', actions, 'properties'); + this.props.paramUpdated({ name: 'actions' }, 'value', actions, 'properties', true); }; actionButtonEventUpdated = (event, value, extraData) => { @@ -91,7 +92,7 @@ class TableComponent extends React.Component { actionId: value, }; - this.props.paramUpdated({ name: 'actions' }, 'value', newValues, 'properties'); + this.props.paramUpdated({ name: 'actions' }, 'value', newValues, 'properties', true); }; actionButtonEventOptionUpdated = (event, option, value, extraData) => { @@ -106,7 +107,7 @@ class TableComponent extends React.Component { [option]: value, }; - this.props.paramUpdated({ name: 'actions' }, 'value', newValues, 'properties'); + this.props.paramUpdated({ name: 'actions' }, 'value', newValues, 'properties', true); }; columnEventChanged = (columnForWhichEventsAreChanged, events) => { @@ -454,22 +455,18 @@ class TableComponent extends React.Component { />
this.columnEventChanged(column, events)} apps={this.props.apps} popOverCallback={(showing) => { - this.setColumnPopoverRootCloseBlocker('event-manager', showing); + this.setState({ actionPopOverRootClose: !showing }); + this.setState({ showPopOver: showing }); }} pages={this.props.pages} /> @@ -751,6 +748,18 @@ class TableComponent extends React.Component { ); }; + deleteEvents = (ref, eventTarget) => { + const events = useAppDataStore.getState().events.filter((event) => event.target === eventTarget); + + const toDelete = events?.filter((e) => e.event?.ref === ref.ref); + + return new Promise.all( + toDelete?.forEach((e) => { + return useAppDataStore.getState().actions.deleteAppVersionEventHandler(e.id); + }) + ); + }; + actionPopOver = (action, index) => { const dummyComponentForActionButton = { component: { @@ -760,6 +769,8 @@ class TableComponent extends React.Component { }, }; + const actionRef = { ref: `${action?.name}` }; + return ( @@ -827,8 +838,12 @@ class TableComponent extends React.Component { paramType="properties" /> - @@ -891,20 +909,21 @@ class TableComponent extends React.Component { const columns = this.props.component.component.definition.properties.columns; const newValue = columns.value; newValue.push({ name: this.generateNewColumnName(columns.value), id: uuidv4() }); - this.props.paramUpdated({ name: 'columns' }, 'value', newValue, 'properties'); + this.props.paramUpdated({ name: 'columns' }, 'value', newValue, 'properties', true); }; addNewAction = () => { const actions = this.props.component.component.definition.properties.actions; const newValue = actions ? actions.value : []; newValue.push({ name: computeActionName(actions), buttonText: 'Button', events: [] }); - this.props.paramUpdated({ name: 'actions' }, 'value', newValue, 'properties'); + this.props.paramUpdated({ name: 'actions' }, 'value', newValue, 'properties', true); }; - removeAction = (index) => { + removeAction = (index, ref) => { const newValue = this.props.component.component.definition.properties.actions.value; newValue.splice(index, 1); - this.props.paramUpdated({ name: 'actions' }, 'value', newValue, 'properties'); + this.props.paramUpdated({ name: 'actions' }, 'value', newValue, 'properties', true); + this.deleteEvents(ref, 'table_action'); }; onColumnItemChange = (index, item, value) => { @@ -914,7 +933,8 @@ class TableComponent extends React.Component { column[item] = value; const newColumns = columns.value; newColumns[index] = column; - this.props.paramUpdated({ name: 'columns' }, 'value', newColumns, 'properties'); + + this.props.paramUpdated({ name: 'columns' }, 'value', newColumns, 'properties', true); }; getItemStyle = (isDragging, draggableStyle) => ({ @@ -922,11 +942,11 @@ class TableComponent extends React.Component { ...draggableStyle, }); - removeColumn = (index) => { + removeColumn = (index, ref) => { const columns = this.props.component.component.definition.properties.columns; const newValue = columns.value; const removedColumns = newValue.splice(index, 1); - this.props.paramUpdated({ name: 'columns' }, 'value', newValue, 'properties'); + this.props.paramUpdated({ name: 'columns' }, 'value', newValue, 'properties', true); const existingcolumnDeletionHistory = this.props.component.component.definition.properties.columnDeletionHistory?.value ?? []; @@ -934,14 +954,16 @@ class TableComponent extends React.Component { ...existingcolumnDeletionHistory, ...removedColumns.map((column) => column.key || column.name), ]; - this.props.paramUpdated({ name: 'columnDeletionHistory' }, 'value', newcolumnDeletionHistory, 'properties'); + this.props.paramUpdated({ name: 'columnDeletionHistory' }, 'value', newcolumnDeletionHistory, 'properties', true); + + this.deleteEvents(ref, 'table_column'); }; reorderColumns = (startIndex, endIndex) => { const result = this.props.component.component.definition.properties.columns.value; const [removed] = result.splice(startIndex, 1); result.splice(endIndex, 0, removed); - this.props.paramUpdated({ name: 'columns' }, 'value', result, 'properties'); + this.props.paramUpdated({ name: 'columns' }, 'value', result, 'properties', true); }; onDragEnd({ source, destination }) { @@ -959,7 +981,6 @@ class TableComponent extends React.Component { const columns = component.component.definition.properties.columns; const actions = component.component.definition.properties.actions || { value: [] }; - if (!component.component.definition.properties.displaySearchBox) paramUpdated({ name: 'displaySearchBox' }, 'value', true, 'properties'); const displaySearchBox = component.component.definition.properties.displaySearchBox.value; @@ -1056,7 +1077,8 @@ class TableComponent extends React.Component { enableActionsMenu isEditable={item.isEditable === '{{true}}'} onMenuOptionClick={(listItem, menuOptionLabel) => { - if (menuOptionLabel === 'Delete') this.removeColumn(index); + if (menuOptionLabel === 'Delete') + this.removeColumn(index, `${item.name}-${index}`); }} darkMode={darkMode} menuActions={[ @@ -1142,8 +1164,11 @@ class TableComponent extends React.Component { isOpen: true, children: ( { const dataQueries = useDataQueriesStore(({ dataQueries = [] }) => { if (callerQueryId) { @@ -41,13 +41,35 @@ export const EventManager = ({ } return dataQueries; }, shallow); - const [events, setEvents] = useState(() => component.component.definition.events || []); + const { apps, appId, events: allAppEvents } = useAppInfo(); + + const { updateAppVersionEventHandlers, createAppVersionEventHandlers, deleteAppVersionEventHandler } = + useAppDataActions(); + + const currentEvents = allAppEvents.filter((event) => { + if (customEventRefs) { + if (event.event.ref !== customEventRefs.ref) { + return false; + } + } + + return event.sourceId === sourceId && event.target === eventSourceType; + }); + + const [events, setEvents] = useState([]); const [focusedEventIndex, setFocusedEventIndex] = useState(null); const { t } = useTranslation(); useEffect(() => { - setEvents(component.component.definition.events || []); - }, [component?.component?.definition?.events]); + if (_.isEqual(currentEvents, events)) return; + + const sortedEvents = currentEvents.sort((a, b) => { + return a.index - b.index; + }); + + setEvents(sortedEvents || []); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [JSON.stringify(currentEvents)]); let actionOptions = ActionTypes.map((action) => { return { name: action.name, value: action.id }; @@ -100,11 +122,11 @@ export const EventManager = ({ excludeEvents = excludeEvents || []; /* Filter events based on excludesEvents ( a list of event ids to exclude ) */ - let possibleEvents = Object.keys(componentMeta.events) + let possibleEvents = Object.keys(eventMetaDefinition.events) .filter((eventId) => !excludeEvents.includes(eventId)) .map((eventId) => { return { - name: componentMeta.events[eventId].displayName, + name: eventMetaDefinition?.events[eventId]?.displayName, value: eventId, }; }); @@ -151,7 +173,7 @@ export const EventManager = ({ const actions = targetComponentMeta.actions; const options = actions.map((action) => ({ - name: action.displayName, + name: action?.displayName, value: action.handle, })); @@ -172,7 +194,7 @@ export const EventManager = ({ function getComponentActionDefaultParams(componentId, actionHandle) { const action = getAction(componentId, actionHandle); - const defaultParams = (action.params ?? []).map((param) => ({ + const defaultParams = (action?.params ?? []).map((param) => ({ handle: param.handle, value: param.defaultValue, })); @@ -182,7 +204,7 @@ export const EventManager = ({ function getAllApps() { let appsOptionsList = []; apps - .filter((item) => item.slug !== undefined) + .filter((item) => item.slug !== undefined && item.id !== appId) .forEach((item) => { appsOptionsList.push({ name: item.name, @@ -208,51 +230,105 @@ export const EventManager = ({ })); } - function handlerChanged(index, param, value) { - let newEvents = [...events]; - + function handleQueryChange(index, updates) { + let newEvents = _.cloneDeep(events); let updatedEvent = newEvents[index]; - updatedEvent[param] = value; + + updatedEvent.event = { + ...updatedEvent.event, + ...updates, + }; newEvents[index] = updatedEvent; - setEvents(newEvents); - eventsChanged(newEvents); + updateAppVersionEventHandlers( + [ + { + event_id: updatedEvent.id, + diff: updatedEvent, + }, + ], + 'update' + ); + } + + function handlerChanged(index, param, value) { + let newEvents = _.cloneDeep(events); + + let updatedEvent = newEvents[index]; + updatedEvent.event[param] = value; + + if (param === 'componentSpecificActionHandle') { + const getDefault = getComponentActionDefaultParams(updatedEvent.event?.componentId, value); + updatedEvent.event['componentSpecificActionParams'] = getDefault; + } + + newEvents[index] = updatedEvent; + + updateAppVersionEventHandlers( + [ + { + event_id: updatedEvent.id, + diff: updatedEvent, + }, + ], + 'update' + ); } function removeHandler(index) { - let newEvents = component.component.definition.events; - newEvents.splice(index, 1); - setEvents(newEvents); - eventsChanged(newEvents); + const eventsHandler = _.cloneDeep(events); + + const eventId = eventsHandler[index].id; + + deleteAppVersionEventHandler(eventId); } function addHandler() { - let newEvents = component.component.definition.events; - newEvents.push({ - eventId: Object.keys(componentMeta.events)[0], - actionId: 'show-alert', - message: 'Hello world!', - alertType: 'info', + let newEvents = events; + const eventIndex = newEvents.length; + + createAppVersionEventHandlers({ + event: { + eventId: Object.keys(eventMetaDefinition?.events)[0], + actionId: 'show-alert', + message: 'Hello world!', + alertType: 'info', + ...customEventRefs, + }, + eventType: eventSourceType, + attachedTo: sourceId, + index: eventIndex, }); - setEvents(newEvents); - eventsChanged(newEvents); } //following two are functions responsible for on change and value for the control specific actions const onChangeHandlerForComponentSpecificActionHandle = (value, index, param, event) => { const newParam = { ...param, value: value }; const params = event?.componentSpecificActionParams ?? []; - const newParams = params.map((paramOfParamList) => - paramOfParamList.handle === param.handle ? newParam : paramOfParamList - ); + + const newParams = + params.length > 0 + ? params.map((paramOfParamList) => { + return paramOfParamList.handle === param.handle ? newParam : paramOfParamList; + }) + : [newParam]; + return handlerChanged(index, 'componentSpecificActionParams', newParams); }; const valueForComponentSpecificActionHandle = (event, param) => { - return ( - event?.componentSpecificActionParams?.find((paramItem) => paramItem.handle === param.handle)?.value ?? - param.defaultValue - ); + const componentSpecificActionParamsExits = Array.isArray(event?.componentSpecificActionParams); + const defaultValue = param.defaultValue ?? ''; + + if (componentSpecificActionParamsExits) { + const paramValue = + event?.componentSpecificActionParams?.find((paramItem) => paramItem.handle === param.handle)?.value ?? + defaultValue; + + return paramValue; + } + + return defaultValue; }; function eventPopover(event, index) { @@ -260,10 +336,14 @@ export const EventManager = ({ - + { + e.stopPropagation(); + }} + >
{t('editor.inspector.eventManager.event', 'Event')} @@ -443,10 +523,11 @@ export const EventManager = ({ options={dataQueries .filter((qry) => isQueryRunnable(qry)) .map((qry) => ({ name: qry.name, value: qry.id }))} - value={event.queryId} + value={event?.queryId} search={true} onChange={(value) => { const query = dataQueries.find((dataquery) => dataquery.id === value); + const parameters = (query?.options?.parameters ?? []).reduce( (paramObj, param) => ({ ...paramObj, @@ -454,9 +535,12 @@ export const EventManager = ({ }), {} ); - handlerChanged(index, 'queryId', query.id); - handlerChanged(index, 'queryName', query.name); - handlerChanged(index, 'parameters', parameters); + + handleQueryChange(index, { + queryId: query.id, + queryName: query.name, + parameters: parameters, + }); }} placeholder={t('globals.select', 'Select') + '...'} styles={styles} @@ -688,7 +772,6 @@ export const EventManager = ({ value={event?.componentId} search={true} onChange={(value) => { - handlerChanged(index, 'componentSpecificActionHandle', ''); handlerChanged(index, 'componentId', value); }} placeholder={t('globals.select', 'Select') + '...'} @@ -710,11 +793,6 @@ export const EventManager = ({ search={true} onChange={(value) => { handlerChanged(index, 'componentSpecificActionHandle', value); - handlerChanged( - index, - 'componentSpecificActionParams', - getComponentActionDefaultParams(event?.componentId, value) - ); }} placeholder={t('globals.select', 'Select') + '...'} styles={styles} @@ -725,10 +803,10 @@ export const EventManager = ({
{event?.componentId && event?.componentSpecificActionHandle && - (getAction(event?.componentId, event?.componentSpecificActionHandle).params ?? []).map((param) => ( + (getAction(event?.componentId, event?.componentSpecificActionHandle)?.params ?? []).map((param) => (
-
- {param.displayName} +
+ {param?.displayName}
{param.type === 'select' ? (
@@ -763,7 +841,7 @@ export const EventManager = ({ enablePreview={true} type={param?.type} fieldMeta={{ options: param?.options }} - cyLabel={param.displayName} + cyLabel={param?.displayName} />
)} @@ -789,11 +867,24 @@ export const EventManager = ({ } const reorderEvents = (startIndex, endIndex) => { - const result = [...component.component.definition.events]; + const result = _.cloneDeep(events); const [removed] = result.splice(startIndex, 1); result.splice(endIndex, 0, removed); - setEvents(result); - eventsChanged(result, true); + + const reorderedEvents = result.map((event, index) => { + return { + ...event, + index: index, + }; + }); + + updateAppVersionEventHandlers( + reorderedEvents.map((event) => ({ + event_id: event.id, + diff: event, + })), + 'reorder' + ); }; const onDragEnd = ({ source, destination }) => { @@ -817,8 +908,8 @@ export const EventManager = ({ {({ innerRef, droppableProps, placeholder }) => (
{events.map((event, index) => { - const actionMeta = ActionTypes.find((action) => action.id === event.actionId); - const rowClassName = `card-body p-0 ${focusedEventIndex === index ? ' bg-azure-lt' : ''}`; + const actionMeta = ActionTypes.find((action) => action.id === event.event.actionId); + // const rowClassName = `card-body p-0 ${focusedEventIndex === index ? ' bg-azure-lt' : ''}`; return ( {renderDraggable((provided, snapshot) => { @@ -831,14 +922,13 @@ export const EventManager = ({ trigger="click" placement={popoverPlacement || 'left'} rootClose={true} - overlay={eventPopover(event, index)} + overlay={eventPopover(event.event, index)} onHide={() => setFocusedEventIndex(null)} onToggle={(showing) => { if (showing) { setFocusedEventIndex(index); } else { setFocusedEventIndex(null); - eventsChanged(events); } if (typeof popOverCallback === 'function') popOverCallback(showing); }} @@ -850,7 +940,7 @@ export const EventManager = ({ {...provided.dragHandleProps} > + {renderAddHandlerBtn()} + {!hideEmptyEventsAlert ? ( +
+ + {t( + 'editor.inspector.eventManager.emptyMessage', + "This {{componentName}} doesn't have any event handlers", + { + componentName: componentName.toLowerCase(), + } + )} + +
+ ) : null} + + ); + } + return ( <> -
- {renderHandlers(events)} - {renderAddHandlerBtn()} -
+ {renderHandlers(events)} + {renderAddHandlerBtn()} ); }; diff --git a/frontend/src/Editor/Inspector/Inspector.jsx b/frontend/src/Editor/Inspector/Inspector.jsx index 840efbdb77..b98ae05050 100644 --- a/frontend/src/Editor/Inspector/Inspector.jsx +++ b/frontend/src/Editor/Inspector/Inspector.jsx @@ -56,7 +56,6 @@ export const Inspector = ({ selectedComponentId, componentDefinitionChanged, allComponents, - apps, darkMode, switchSidebarTab, removeComponent, @@ -66,18 +65,17 @@ export const Inspector = ({ const dataQueries = useDataQueries(); const component = { id: selectedComponentId, - component: allComponents[selectedComponentId].component, + component: JSON.parse(JSON.stringify(allComponents[selectedComponentId].component)), layouts: allComponents[selectedComponentId].layouts, parent: allComponents[selectedComponentId].parent, }; const currentState = useCurrentState(); const [showWidgetDeleteConfirmation, setWidgetDeleteConfirmation] = useState(false); - // eslint-disable-next-line no-unused-vars - const [tabHeight, setTabHeight] = React.useState(0); + const componentNameRef = useRef(null); const [newComponentName, setNewComponentName] = useState(component.component.name); const [inputRef, setInputFocus] = useFocus(); - const [selectedTab, setSelectedTab] = useState('properties'); + // const [selectedTab, setSelectedTab] = useState('properties'); const [showHeaderActionsMenu, setShowHeaderActionsMenu] = useState(false); const { isVersionReleased } = useAppVersionStore( (state) => ({ @@ -101,13 +99,6 @@ export const Inspector = ({ componentNameRef.current = newComponentName; }, [newComponentName]); - useEffect(() => { - return () => { - handleComponentNameChange(componentNameRef.current); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - const validateComponentName = (name) => { const isValid = !Object.values(allComponents) .map((component) => component.component.name) @@ -131,9 +122,9 @@ export const Inspector = ({ return setInputFocus(); } if (validateQueryName(newName)) { - let newComponent = { ...component }; + let newComponent = JSON.parse(JSON.stringify(component)); newComponent.component.name = newName; - componentDefinitionChanged(newComponent); + componentDefinitionChanged(newComponent, { componentNameUpdated: true }); } else { toast.error( t( @@ -152,9 +143,9 @@ export const Inspector = ({ return null; }; - function paramUpdated(param, attr, value, paramType) { - console.log({ param, attr, value, paramType }); - let newDefinition = _.cloneDeep(component.component.definition); + function paramUpdated(param, attr, value, paramType, isParamFromTableColumn = false) { + let newComponent = JSON.parse(JSON.stringify(component)); + let newDefinition = _.cloneDeep(newComponent.component.definition); let allParams = newDefinition[paramType] || {}; const paramObject = allParams[param.name]; if (!paramObject) { @@ -163,13 +154,19 @@ export const Inspector = ({ if (attr) { allParams[param.name][attr] = value; const defaultValue = getDefaultValue(value); - // This is needed to have enable pagination as backward compatible + // This is needed to have enable pagination in Table as backward compatible // Whenever enable pagination is false, we turn client and server side pagination as false - if (param.name === 'enablePagination' && !resolveReferences(value, currentState)) { + if ( + component.component.component === 'Table' && + param.name === 'enablePagination' && + !resolveReferences(value, currentState) + ) { if (allParams?.['clientSidePagination']?.[attr]) { allParams['clientSidePagination'][attr] = value; } - allParams['serverSidePagination'][attr] = value; + if (allParams['serverSidePagination']?.[attr]) { + allParams['serverSidePagination'][attr] = value; + } } // This case is required to handle for older apps when serverSidePagination is connected to Fx if (param.name === 'serverSidePagination' && !allParams?.['enablePagination']?.[attr]) { @@ -194,12 +191,11 @@ export const Inspector = ({ allParams[param.name] = value; } newDefinition[paramType] = allParams; - let newComponent = _.merge(component, { - component: { - definition: newDefinition, - }, + newComponent.component.definition = newDefinition; + componentDefinitionChanged(newComponent, { + componentPropertyUpdated: true, + isParamFromTableColumn: isParamFromTableColumn, }); - componentDefinitionChanged(newComponent); } function layoutPropertyChanged(param, attr, value, paramType) { @@ -207,9 +203,7 @@ export const Inspector = ({ // User wants to show the widget on mobile devices if (param.name === 'showOnMobile' && value === true) { - let newComponent = { - ...component, - }; + let newComponent = JSON.parse(JSON.stringify(component)); const { width, height } = newComponent.layouts['desktop']; @@ -223,7 +217,7 @@ export const Inspector = ({ }, }; - componentDefinitionChanged(newComponent); + componentDefinitionChanged(newComponent, { layoutPropertyChanged: true }); // Child components should also have a mobile layout const childComponents = Object.keys(allComponents).filter((key) => allComponents[key].parent === component.id); @@ -246,54 +240,11 @@ export const Inspector = ({ }, }; - componentDefinitionChanged(newChild); + componentDefinitionChanged(newChild, { withChildLayout: true }); }); } } - function eventUpdated(event, actionId) { - let newDefinition = { ...component.component.definition }; - newDefinition.events[event.name] = { actionId }; - - let newComponent = { - ...component, - }; - - componentDefinitionChanged(newComponent); - } - - function eventsChanged(newEvents, isReordered = false) { - let newDefinition; - if (isReordered) { - newDefinition = { ...component.component }; - newDefinition.definition.events = newEvents; - } else { - newDefinition = { ...component.component.definition }; - newDefinition.events = newEvents; - } - - let newComponent = { - ...component, - }; - - componentDefinitionChanged(newComponent); - } - - function eventOptionUpdated(event, option, value) { - console.log('eventOptionUpdated', event, option, value); - - let newDefinition = { ...component.component.definition }; - let eventDefinition = newDefinition.events[event.name] || { options: {} }; - - newDefinition.events[event.name] = { ...eventDefinition, options: { ...eventDefinition.options, [option]: value } }; - - let newComponent = { - ...component, - }; - - componentDefinitionChanged(newComponent); - } - const handleInspectorHeaderActions = (value) => { if (value === 'rename') { setTimeout(() => setInputFocus(), 0); @@ -339,13 +290,13 @@ export const Inspector = ({ paramUpdated={paramUpdated} dataQueries={dataQueries} componentMeta={componentMeta} - eventUpdated={eventUpdated} - eventOptionUpdated={eventOptionUpdated} + // eventUpdated={eventUpdated} + // eventOptionUpdated={eventOptionUpdated} components={allComponents} currentState={currentState} darkMode={darkMode} - eventsChanged={eventsChanged} - apps={apps} + // eventsChanged={eventsChanged} + // apps={apps} !check pages={pages} allComponents={allComponents} /> @@ -382,31 +333,15 @@ export const Inspector = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [JSON.stringify({ showHeaderActionsMenu })]); - const handleDeleteConfirm = React.useCallback(() => { - switchSidebarTab(2); - removeComponent(component); - setWidgetDeleteConfirmation(false); - }, [switchSidebarTab, removeComponent, component, setWidgetDeleteConfirmation]); - - React.useEffect(() => { - const handleKeyPress = (event) => { - if (showWidgetDeleteConfirmation && event.key === 'Enter') { - handleDeleteConfirm(); - } - }; - - document.addEventListener('keydown', handleKeyPress); - return () => { - document.removeEventListener('keydown', handleKeyPress); - }; - }, [showWidgetDeleteConfirmation, handleDeleteConfirm]); - return (
{ + switchSidebarTab(2); + removeComponent(component.id); + }} onCancel={() => setWidgetDeleteConfirmation(false)} darkMode={darkMode} /> diff --git a/frontend/src/Editor/LeftSidebar/SidebarInspector.jsx b/frontend/src/Editor/LeftSidebar/SidebarInspector.jsx index 06335ff095..1510463c74 100644 --- a/frontend/src/Editor/LeftSidebar/SidebarInspector.jsx +++ b/frontend/src/Editor/LeftSidebar/SidebarInspector.jsx @@ -62,6 +62,7 @@ export const LeftSidebarInspector = ({ delete jsontreeData.server; delete jsontreeData.actions; delete jsontreeData.succededQuery; + delete jsontreeData.layout; //*Sorted components and queries alphabetically const sortedComponents = Object.keys(jsontreeData['components']) @@ -117,7 +118,7 @@ export const LeftSidebarInspector = ({ const iconsList = useMemo(() => [...queryIcons, ...componentIcons], [queryIcons, componentIcons]); const handleRemoveComponent = (component) => { - removeComponent(component); + removeComponent(component.id); }; const handleSelectComponentOnEditor = (component) => { diff --git a/frontend/src/Editor/LeftSidebar/SidebarPageSelector/GlobalSettings.jsx b/frontend/src/Editor/LeftSidebar/SidebarPageSelector/GlobalSettings.jsx index d2097a4c72..cda8280925 100644 --- a/frontend/src/Editor/LeftSidebar/SidebarPageSelector/GlobalSettings.jsx +++ b/frontend/src/Editor/LeftSidebar/SidebarPageSelector/GlobalSettings.jsx @@ -4,7 +4,7 @@ import { useAppVersionStore } from '@/_stores/appVersionStore'; import { shallow } from 'zustand/shallow'; import SolidIcon from '@/_ui/Icon/SolidIcons'; -export const GlobalSettings = ({ darkMode, showHideViewerNavigationControls, showPageViwerPageNavitation }) => { +export const GlobalSettings = ({ darkMode, showHideViewerNavigationControls, isViewerNavigationDisabled }) => { const { isVersionReleased, enableReleasedVersionPopupState } = useAppVersionStore( (state) => ({ isVersionReleased: state.isVersionReleased, @@ -35,7 +35,7 @@ export const GlobalSettings = ({ darkMode, showHideViewerNavigationControls, sho
- +
diff --git a/frontend/src/Editor/LeftSidebar/SidebarPageSelector/PageHandler.jsx b/frontend/src/Editor/LeftSidebar/SidebarPageSelector/PageHandler.jsx index 57289f787d..b434458452 100644 --- a/frontend/src/Editor/LeftSidebar/SidebarPageSelector/PageHandler.jsx +++ b/frontend/src/Editor/LeftSidebar/SidebarPageSelector/PageHandler.jsx @@ -25,7 +25,7 @@ export const PageHandler = ({ currentPageId, updateHomePage, updatePageHandle, - updateOnPageLoadEvents, + apps, pages, components, @@ -201,7 +201,6 @@ export const PageHandler = ({ !haveUserPinned && pinPagesPopover(false); }} darkMode={darkMode} - updateOnPageLoadEvents={updateOnPageLoadEvents} apps={apps} pages={pages} components={components} diff --git a/frontend/src/Editor/LeftSidebar/SidebarPageSelector/SettingsModal.jsx b/frontend/src/Editor/LeftSidebar/SidebarPageSelector/SettingsModal.jsx index 97c81027e8..b9ccbf4cf2 100644 --- a/frontend/src/Editor/LeftSidebar/SidebarPageSelector/SettingsModal.jsx +++ b/frontend/src/Editor/LeftSidebar/SidebarPageSelector/SettingsModal.jsx @@ -8,7 +8,7 @@ export const SettingsModal = ({ show, handleClose, darkMode, - updateOnPageLoadEvents, + apps, pages, components, @@ -55,6 +55,7 @@ export const SettingsModal = ({ pinPagesPopover(true)}> Events updateOnPageLoadEvents(page.id, events)} popOverCallback={(showing) => showing} /> diff --git a/frontend/src/Editor/LeftSidebar/SidebarPageSelector/index.jsx b/frontend/src/Editor/LeftSidebar/SidebarPageSelector/index.jsx index 239dbf7901..f39a5e2ad9 100644 --- a/frontend/src/Editor/LeftSidebar/SidebarPageSelector/index.jsx +++ b/frontend/src/Editor/LeftSidebar/SidebarPageSelector/index.jsx @@ -30,7 +30,7 @@ const LeftSidebarPageSelector = ({ homePageId, showHideViewerNavigationControls, updateOnSortingPages, - updateOnPageLoadEvents, + apps, pinned, setPinned, @@ -89,7 +89,7 @@ const LeftSidebarPageSelector = ({ } > @@ -166,7 +166,6 @@ const LeftSidebarPageSelector = ({ updatePageHandle={updatePageHandle} classNames="page-handler" onSort={updateOnSortingPages} - updateOnPageLoadEvents={updateOnPageLoadEvents} currentState={currentState} apps={apps} allpages={pages} diff --git a/frontend/src/Editor/LeftSidebar/index.jsx b/frontend/src/Editor/LeftSidebar/index.jsx index 55a8e11107..3fae5b4f9c 100644 --- a/frontend/src/Editor/LeftSidebar/index.jsx +++ b/frontend/src/Editor/LeftSidebar/index.jsx @@ -19,8 +19,8 @@ import { useDataSources } from '@/_stores/dataSourcesStore'; import { shallow } from 'zustand/shallow'; import useDebugger from './SidebarDebugger/useDebugger'; import { GlobalSettings } from '../Header/GlobalSettings'; -import { useCurrentState } from '@/_stores/currentStateStore'; import { resolveReferences } from '@/_helpers/utils'; +import { useCurrentState } from '@/_stores/currentStateStore'; export const LeftSidebar = forwardRef((props, ref) => { const router = useRouter(); @@ -46,7 +46,6 @@ export const LeftSidebar = forwardRef((props, ref) => { updatePageHandle, showHideViewerNavigationControls, updateOnSortingPages, - updateOnPageLoadEvents, apps, clonePage, setEditorMarginLeft, @@ -54,10 +53,8 @@ export const LeftSidebar = forwardRef((props, ref) => { toggleAppMaintenance, app, disableEnablePage, - slug, - handleSlugChange, + isMaintenanceOn, } = props; - const { is_maintenance_on } = app; const dataSources = useDataSources(); const prevSelectedSidebarItem = localStorage.getItem('selectedSidebarItem'); @@ -80,8 +77,9 @@ export const LeftSidebar = forwardRef((props, ref) => { }), shallow ); - const [pinned, setPinned] = useState(!!localStorage.getItem('selectedSidebarItem')); const currentState = useCurrentState(); + const [pinned, setPinned] = useState(!!localStorage.getItem('selectedSidebarItem')); + const [realState, setRealState] = useState(currentState); const { errorLogs, clearErrorLogs, unReadErrorCount, allLog } = useDebugger({ @@ -142,9 +140,10 @@ export const LeftSidebar = forwardRef((props, ref) => { sideBarBtnRefs.current[page] = ref; }; useEffect(() => { - setRealState(currentState); + setRealState(currentState); //!ceck this // eslint-disable-next-line react-hooks/exhaustive-deps }, [currentState.components]); + const backgroundFxQuery = appDefinition?.globalSettings?.backgroundFxQuery; const SELECTED_ITEMS = { @@ -164,11 +163,14 @@ export const LeftSidebar = forwardRef((props, ref) => { updateHomePage={updateHomePage} updatePageHandle={updatePageHandle} clonePage={clonePage} - pages={Object.entries(appDefinition.pages).map(([id, page]) => ({ id, ...page })) || []} + pages={ + Object.entries(_.cloneDeep(appDefinition).pages) + .map(([id, page]) => ({ id, ...page })) + .sort((a, b) => a.index - b.index) || [] + } homePageId={appDefinition.homePageId} showHideViewerNavigationControls={showHideViewerNavigationControls} updateOnSortingPages={updateOnSortingPages} - updateOnPageLoadEvents={updateOnPageLoadEvents} apps={apps} setPinned={handlePin} pinned={pinned} @@ -221,21 +223,19 @@ export const LeftSidebar = forwardRef((props, ref) => { globalSettings={appDefinition.globalSettings} darkMode={darkMode} toggleAppMaintenance={toggleAppMaintenance} - is_maintenance_on={is_maintenance_on} + isMaintenanceOn={isMaintenanceOn} app={app} + realState={currentState} backgroundFxQuery={backgroundFxQuery} - realState={realState} - slug={slug} - handleSlugChange={handleSlugChange} /> ), }; useEffect(() => { backgroundFxQuery && - globalSettingsChanged('canvasBackgroundColor', resolveReferences(backgroundFxQuery, realState)); + globalSettingsChanged({ canvasBackgroundColor: resolveReferences(backgroundFxQuery, currentState) }); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [JSON.stringify(resolveReferences(backgroundFxQuery, realState))]); + }, [JSON.stringify(resolveReferences(backgroundFxQuery, currentState))]); return (
diff --git a/frontend/src/Editor/ManageAppUsers.jsx b/frontend/src/Editor/ManageAppUsers.jsx index ebd8c7e637..42f8ccfc34 100644 --- a/frontend/src/Editor/ManageAppUsers.jsx +++ b/frontend/src/Editor/ManageAppUsers.jsx @@ -13,6 +13,7 @@ import SolidIcon from '@/_ui/Icon/SolidIcons'; import cx from 'classnames'; import { ToolTip } from '@/_components/ToolTip'; import { TOOLTIP_MESSAGES } from '@/_helpers/constants'; +import { useAppDataStore } from '@/_stores/appDataStore'; class ManageAppUsersComponent extends React.Component { constructor(props) { @@ -21,7 +22,7 @@ class ManageAppUsersComponent extends React.Component { this.state = { showModal: false, - app: { ...props.app }, + appId: null, isLoading: true, isSlugVerificationInProgress: false, addingUser: false, @@ -49,14 +50,14 @@ class ManageAppUsersComponent extends React.Component { }; componentDidMount() { - const appId = this.props.app.id; - this.fetchAppUsers(); + const appId = this.props.appId; + this.fetchAppUsers(appId); this.setState({ appId }); } - fetchAppUsers = () => { + fetchAppUsers = (appId) => { appsService - .getAppUsers(this.props.app.id) + .getAppUsers(appId) .then((data) => this.setState({ users: data.users, @@ -65,7 +66,8 @@ class ManageAppUsersComponent extends React.Component { ) .catch((error) => { this.setState({ isLoading: false }); - toast.error(error); + const errorMessage = error?.message || 'Something went wrong'; + toast.error(errorMessage); }); }; @@ -89,11 +91,11 @@ class ManageAppUsersComponent extends React.Component { const { organizationUserId, role } = this.state.newUser; appService - .createAppUser(this.state.app.id, organizationUserId, role) + .createAppUser(this.state.appId, organizationUserId, role) .then(() => { this.setState({ addingUser: false, newUser: {} }); toast.success('Added user successfully'); - this.fetchAppUsers(); + this.fetchAppUsers(this.state.appId); }) .catch(({ error }) => { this.setState({ addingUser: false }); @@ -102,21 +104,19 @@ class ManageAppUsersComponent extends React.Component { }; toggleAppVisibility = () => { - const newState = !this.state.app.is_public; + const newState = !this.props.isPublic; this.setState({ ischangingVisibility: true, }); + useAppDataStore.getState().actions.updateState({ isPublic: newState }); + // eslint-disable-next-line no-unused-vars appsService - .setVisibility(this.state.app.id, newState) + .setVisibility(this.state.appId, newState) .then(() => { this.setState({ ischangingVisibility: false, - app: { - ...this.state.app, - is_public: newState, - }, }); if (newState) { @@ -153,7 +153,7 @@ class ManageAppUsersComponent extends React.Component { isSlugVerificationInProgress: true, }); appsService - .setSlug(this.state.app.id, value) + .setSlug(this.state.appId, value) .then(() => { this.setState({ newSlug: { @@ -163,8 +163,9 @@ class ManageAppUsersComponent extends React.Component { isSlugVerificationInProgress: false, isSlugUpdated: true, }); - this.props.handleSlugChange(value); + replaceEditorURL(value, this.props.pageHandle); + useAppDataStore.getState().actions.updateState({ slug: value }); }) .catch(({ error }) => { this.setState({ @@ -189,8 +190,8 @@ class ManageAppUsersComponent extends React.Component { }; render() { - const { isLoading, app, isSlugVerificationInProgress, newSlug, isSlugUpdated } = this.state; - const appId = app.id; + const { isLoading, appId, isSlugVerificationInProgress, newSlug, isSlugUpdated } = this.state; + const appLink = `${getHostURL()}/applications/`; const shareableLink = appLink + (this.props.slug || appId); const slugButtonClass = !_.isEmpty(newSlug.error) ? 'is-invalid' : 'is-valid'; @@ -249,7 +250,7 @@ class ManageAppUsersComponent extends React.Component { className="form-check-input" type="checkbox" onClick={this.toggleAppVisibility} - checked={this.state.app.is_public} + checked={this?.props?.isPublic} disabled={this.state.ischangingVisibility} data-cy="make-public-app-toggle" /> @@ -356,7 +357,7 @@ class ManageAppUsersComponent extends React.Component { )}
- {(this.state.app.is_public || window?.public_config?.ENABLE_PRIVATE_APP_EMBED === 'true') && ( + {(this?.props?.isPublic || window?.public_config?.ENABLE_PRIVATE_APP_EMBED === 'true') && (
diff --git a/frontend/src/Editor/QueryManager/Components/QueryManagerBody.jsx b/frontend/src/Editor/QueryManager/Components/QueryManagerBody.jsx index 1d9185ac02..6bfbd12205 100644 --- a/frontend/src/Editor/QueryManager/Components/QueryManagerBody.jsx +++ b/frontend/src/Editor/QueryManager/Components/QueryManagerBody.jsx @@ -14,7 +14,7 @@ import { EventManager } from '@/Editor/Inspector/EventManager'; import { staticDataSources, customToggles, mockDataQueryAsComponent } from '../constants'; import { DataSourceTypes } from '../../DataSourceManager/SourceComponents'; import { useDataSources, useGlobalDataSources } from '@/_stores/dataSourcesStore'; -import { useDataQueriesActions, useDataQueriesStore } from '@/_stores/dataQueriesStore'; +import { useDataQueriesActions } from '@/_stores/dataQueriesStore'; import { useSelectedQuery, useSelectedDataSource } from '@/_stores/queryPanelStore'; import { useAppVersionStore } from '@/_stores/appVersionStore'; import { shallow } from 'zustand/shallow'; @@ -97,14 +97,6 @@ export const QueryManagerBody = ({ validateNewOptions(newOptions); }; - const eventsChanged = (events) => { - optionchanged('events', events); - //added this here since the subscriber added in QueryManager component does not detect this change - useDataQueriesStore - .getState() - .actions.saveData({ ...selectedQuery, options: { ...selectedQuery.options, events: events } }); - }; - const toggleOption = (option) => { const currentValue = selectedQuery?.options?.[option] ?? false; optionchanged(option, !currentValue); @@ -185,9 +177,9 @@ export const QueryManagerBody = ({
{t('editor.queryManager.eventsHandler', 'Events')}
{ +const QueryManager = ({ mode, appId, darkMode, apps, allComponents, appDefinition, editorRef }) => { const loadingDataSources = useLoadingDataSources(); const dataSources = useDataSources(); const globalDataSources = useGlobalDataSources(); const queryToBeRun = useQueryToBeRun(); - const isCreationInProcess = useQueryCreationLoading(); - const isUpdationInProcess = useQueryUpdationLoading(); const selectedQuery = useSelectedQuery(); const { setSelectedDataSource, setQueryToBeRun } = useQueryPanelActions(); const [options, setOptions] = useState({}); - const mounted = useRef(false); - - /** TODO: Below effect primarily used only for websocket invocation post update. Can be removed onece websocket logic is revamped */ - useEffect(() => { - if (mounted.current && !isCreationInProcess && !isUpdationInProcess) { - return dataQueriesChanged(); - } - mounted.current = true; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isCreationInProcess, isUpdationInProcess, mounted.current]); useEffect(() => { setOptions(selectedQuery?.options || {}); diff --git a/frontend/src/Editor/QueryPanel/QueryPanel.jsx b/frontend/src/Editor/QueryPanel/QueryPanel.jsx index 9ca51023e7..9f102c6f1d 100644 --- a/frontend/src/Editor/QueryPanel/QueryPanel.jsx +++ b/frontend/src/Editor/QueryPanel/QueryPanel.jsx @@ -15,7 +15,6 @@ const QueryPanel = ({ dataQueriesChanged, fetchDataQueries, darkMode, - apps, allComponents, appId, appDefinition, @@ -203,7 +202,6 @@ const QueryPanel = ({ dataQueriesChanged={updateDataQueries} appId={appId} darkMode={darkMode} - apps={apps} allComponents={allComponents} appDefinition={appDefinition} editorRef={editorRef} diff --git a/frontend/src/Editor/RealtimeAvatars.jsx b/frontend/src/Editor/RealtimeAvatars.jsx index 5bc3196767..cdd5c541a8 100644 --- a/frontend/src/Editor/RealtimeAvatars.jsx +++ b/frontend/src/Editor/RealtimeAvatars.jsx @@ -1,8 +1,9 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import Popover from '@/_ui/Popover'; import Avatar from '@/_ui/Avatar'; // eslint-disable-next-line import/no-unresolved import { useOthers, useSelf } from '@y-presence/react'; +import { useAppDataActions, useAppInfo } from '@/_stores/appDataStore'; const MAX_DISPLAY_USERS = 2; const RealtimeAvatars = ({ darkMode }) => { @@ -17,6 +18,17 @@ const RealtimeAvatars = ({ darkMode }) => { const getAvatarText = (presence) => presence.firstName?.charAt(0) + presence.lastName?.charAt(0); const getAvatarTitle = (presence) => `${presence.firstName} ${presence.lastName}`; + const { updateState } = useAppDataActions(); + const { areOthersOnSameVersionAndPage, currentVersionId } = useAppInfo(); + + useEffect(() => { + const areActiveUsersOnSameVersionAndPage = othersOnSameVersionAndPage.length > 0; + const shouldUpdateState = areActiveUsersOnSameVersionAndPage !== areOthersOnSameVersionAndPage; + + if (shouldUpdateState) updateState({ areOthersOnSameVersionAndPage: areActiveUsersOnSameVersionAndPage }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [JSON.stringify({ others, self, currentVersionId })]); + const popoverContent = () => { return othersOnSameVersionAndPage .slice(MAX_DISPLAY_USERS, othersOnSameVersionAndPage.length) diff --git a/frontend/src/Editor/ReleaseVersionButton.jsx b/frontend/src/Editor/ReleaseVersionButton.jsx index 0425b2fca4..04973bbd03 100644 --- a/frontend/src/Editor/ReleaseVersionButton.jsx +++ b/frontend/src/Editor/ReleaseVersionButton.jsx @@ -8,13 +8,7 @@ import { ConfirmDialog } from '@/_components/ConfirmDialog'; import { shallow } from 'zustand/shallow'; import { ButtonSolid } from '@/_ui/AppButton/AppButton'; -export const ReleaseVersionButton = function DeployVersionButton({ - appId, - appName, - fetchApp, - onVersionRelease, - saveEditingVersion, -}) { +export const ReleaseVersionButton = function DeployVersionButton({ appId, appName, fetchApp, onVersionRelease }) { const [isReleasing, setIsReleasing] = useState(false); const { isVersionReleased, editingVersion } = useAppVersionStore( (state) => ({ @@ -29,7 +23,7 @@ export const ReleaseVersionButton = function DeployVersionButton({ const releaseVersion = (editingVersion) => { setShowPageDeletionConfirmation(false); setIsReleasing(true); - saveEditingVersion(); + appsService .saveApp(appId, { name: appName, diff --git a/frontend/src/Editor/SubContainer.jsx b/frontend/src/Editor/SubContainer.jsx index 4f5edad122..06f8d0758d 100644 --- a/frontend/src/Editor/SubContainer.jsx +++ b/frontend/src/Editor/SubContainer.jsx @@ -15,6 +15,8 @@ import { useCurrentState } from '@/_stores/currentStateStore'; import { useAppVersionStore } from '@/_stores/appVersionStore'; import { shallow } from 'zustand/shallow'; import { useMounted } from '@/_hooks/use-mount'; +// eslint-disable-next-line import/no-unresolved +import { diff } from 'deep-object-diff'; const NO_OF_GRIDS = 43; @@ -92,17 +94,19 @@ export const SubContainer = ({ false; const getChildWidgets = (components) => { - let childWidgets = []; + let childWidgets = {}; Object.keys(components).forEach((key) => { - if (components[key].parent === parent) { + const componentParent = components[key].component.parent; + if (componentParent === parent) { childWidgets[key] = { ...components[key], component: { ...components[key]['component'], parent } }; } }); + return childWidgets; }; const [boxes, setBoxes] = useState(allComponents); - const [childWidgets, setChildWidgets] = useState(() => getChildWidgets(allComponents)); + const [childWidgets, setChildWidgets] = useState([]); const [isDragging, setIsDragging] = useState(false); const [isResizing, setIsResizing] = useState(false); // const [subContainerHeight, setSubContainerHeight] = useState('100%'); //used to determine the height of the sub container for modal @@ -111,6 +115,7 @@ export const SubContainer = ({ useEffect(() => { setBoxes(allComponents); setChildWidgets(() => getChildWidgets(allComponents)); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [allComponents, parent]); @@ -183,8 +188,11 @@ export const SubContainer = ({ ); _.set(childrenBoxes, newComponent.id, { - component: newComponent.component, - parent: parentComponent.component === 'Tabs' ? parentId + '-' + tab : parentId, + component: { + ...newComponent.component, + parent: parentComponent.component === 'Tabs' ? parentId + '-' + tab : parentId, + }, + layouts: { [currentLayout]: { ...layout, @@ -233,7 +241,23 @@ export const SubContainer = ({ }, }, }; - appDefinitionChanged(newDefinition); + + const oldComponents = appDefinition.pages[currentPageId]?.components ?? {}; + const newComponents = boxes; + + const componendAdded = Object.keys(newComponents).length > Object.keys(oldComponents).length; + + const opts = { containerChanges: true }; + + if (componendAdded) { + opts.componentAdded = true; + } + + const shouldUpdate = !_.isEmpty(diff(appDefinition, newDefinition)); + + if (shouldUpdate) { + appDefinitionChanged(newDefinition, opts); + } } // eslint-disable-next-line react-hooks/exhaustive-deps }, [boxes]); @@ -271,6 +295,7 @@ export const SubContainer = ({ } }); + //!Todo: need to check: this never gets called as draggingState is always false useEffect(() => { setIsDragging(draggingState); }, [draggingState]); @@ -306,8 +331,10 @@ export const SubContainer = ({ setBoxes({ ...boxes, [newComponent.id]: { - component: newComponent.component, - parent: parentRef.current.id, + component: { + ...newComponent.component, + parent: parentRef.current.id, + }, layouts: { ...newComponent.layout, }, @@ -357,6 +384,7 @@ export const SubContainer = ({ enableReleasedVersionPopupState(); return; } + const canvasWidth = getContainerCanvasWidth(); const nodeBounds = direction.node.getBoundingClientRect(); @@ -430,7 +458,12 @@ export const SubContainer = ({ } //round the width to nearest multiple of gridwidth before converting to % - const currentWidth = (_containerCanvasWidth * width) / NO_OF_GRIDS; + let currentWidth = (_containerCanvasWidth * width) / NO_OF_GRIDS; + + if (currentWidth > _containerCanvasWidth) { + currentWidth = _containerCanvasWidth; + } + let newWidth = currentWidth + deltaWidth; newWidth = Math.round(newWidth / gridWidth) * gridWidth; width = (newWidth * NO_OF_GRIDS) / _containerCanvasWidth; @@ -536,9 +569,11 @@ export const SubContainer = ({ Object.keys(childWidgets).map((key) => { const addDefaultChildren = childWidgets[key]['withDefaultChildren'] || false; const box = childWidgets[key]; + const canShowInCurrentLayout = box.component.definition.others[currentLayout === 'mobile' ? 'showOnMobile' : 'showOnDesktop'].value; - if (box.parent && resolveReferences(canShowInCurrentLayout, currentState)) { + + if (box.component.parent && resolveReferences(canShowInCurrentLayout, currentState)) { return ( { - const copyDefinition = _.cloneDeep(data.definition); - const pagesObj = copyDefinition.pages || {}; - - const newDefinition = { - ...copyDefinition, - pages: pagesObj, + getViewerRef() { + return { + appDefinition: this.state.appDefinition, + queryConfirmationList: this.props.queryConfirmationList, + updateQueryConfirmationList: this.updateQueryConfirmationList, + navigate: this.props.navigate, + switchPage: this.switchPage, + currentPageId: this.state.currentPageId, }; + } + + setStateForApp = (data, byAppSlug = false) => { + const appDefData = buildAppDefinition(data); + + if (byAppSlug) { + appDefData.globalSettings = data.globalSettings; + appDefData.homePageId = data.homePageId; + appDefData.showViewerNavigation = data.showViewerNavigation; + } this.setState({ app: data, isLoading: false, isAppLoaded: true, - appDefinition: newDefinition || { components: {} }, + appDefinition: { ...appDefData }, + pages: appDefData.pages, }); }; - setStateForContainer = async (data) => { + setStateForContainer = async (data, appVersionId) => { + const appDefData = buildAppDefinition(data); + const currentUser = this.state.currentUser; let userVars = {}; @@ -94,38 +108,57 @@ class ViewerComponent extends React.Component { let mobileLayoutHasWidgets = false; if (this.props.currentLayout === 'mobile') { - const currentComponents = data.definition.pages[data.definition.homePageId].components; + const currentComponents = appDefData.pages[appDefData.homePageId].components; mobileLayoutHasWidgets = Object.keys(currentComponents).filter((componentId) => currentComponents[componentId]['layouts']['mobile']) .length > 0; } let queryState = {}; - data.data_queries.forEach((query) => { - if (query.pluginId || query?.plugin?.id) { - queryState[query.name] = { - ...query.plugin.manifestFile.data.source.exposedVariables, - ...this.props.currentState.queries[query.name], - }; - } else { - const dataSourceTypeDetail = DataSourceTypes.find((source) => source.kind === query.kind); - queryState[query.name] = { - ...dataSourceTypeDetail.exposedVariables, - ...this.props.currentState.queries[query.name], - }; - } - }); + let dataQueries = []; + if (appVersionId) { + const { data_queries } = await dataqueryService.getAll(appVersionId); + dataQueries = data_queries; + } else { + dataQueries = data.data_queries; + } + const queryConfirmationList = []; + + if (dataQueries.length > 0) { + dataQueries.forEach((query) => { + if (query?.options && query?.options?.requestConfirmation && query?.options?.runOnPageLoad) { + queryConfirmationList.push({ queryId: query.id, queryName: query.name }); + } + + if (query.pluginId || query?.plugin?.id) { + queryState[query.name] = { + ...query.plugin.manifestFile.data.source.exposedVariables, + ...this.props.currentState.queries[query.name], + }; + } else { + const dataSourceTypeDetail = DataSourceTypes.find((source) => source.kind === query.kind); + queryState[query.name] = { + ...dataSourceTypeDetail.exposedVariables, + ...this.props.currentState.queries[query.name], + }; + } + }); + } + + if (queryConfirmationList.length !== 0) { + this.updateQueryConfirmationList(queryConfirmationList); + } const variables = await this.fetchOrgEnvironmentVariables(data.slug, data.is_public); const constants = await this.fetchOrgEnvironmentConstants(data.slug, data.is_public); - const pages = Object.entries(data.definition.pages).map(([pageId, page]) => ({ id: pageId, ...page })); - const homePageId = data.definition.homePageId; + const pages = data.pages; + const homePageId = appVersionId ? data.editing_version.homePageId : data?.homePageId; const startingPageHandle = this.props?.params?.pageHandle; const currentPageId = pages.filter((page) => page.handle === startingPageHandle)[0]?.id ?? homePageId; const currentPage = pages.find((page) => page.id === currentPageId); - useDataQueriesStore.getState().actions.setDataQueries(data.data_queries); + useDataQueriesStore.getState().actions.setDataQueries(dataQueries); this.props.setCurrentState({ queries: queryState, components: {}, @@ -148,6 +181,7 @@ class ViewerComponent extends React.Component { ...constants, }); useEditorStore.getState().actions.toggleCurrentLayout(mobileLayoutHasWidgets ? 'mobile' : 'desktop'); + this.props.updateState({ events: data.events ?? [] }); this.setState( { currentUser, @@ -159,21 +193,24 @@ class ViewerComponent extends React.Component { ? `${this.state.deviceWindowWidth}px` : '1292px', selectedComponent: null, - dataQueries: data.data_queries, + dataQueries: dataQueries, currentPageId: currentPage.id, - pages: {}, - homepage: this.state.appDefinition?.pages?.[this.state.appDefinition?.homePageId]?.handle, + homepage: appDefData?.pages?.[this.state.appDefinition?.homePageId]?.handle, + events: data.events ?? [], }, () => { - computeComponentState(this, data?.definition?.pages[currentPage.id]?.components).then(async () => { - this.setState({ initialComputationOfStateDone: true }); + const components = appDefData?.pages[currentPageId]?.components || {}; + + computeComponentState(components).then(async () => { + this.setState({ initialComputationOfStateDone: true, defaultComponentStateComputed: true }); console.log('Default component state computed and set'); - this.runQueries(data.data_queries); - // eslint-disable-next-line no-unsafe-optional-chaining - const { events } = this.state.appDefinition?.pages[this.state.currentPageId]; - for (const event of events ?? []) { - await this.handleEvent(event.eventId, event); - } + this.runQueries(dataQueries); + + const currentPageEvents = this.state.events.filter( + (event) => event.target === 'page' && event.sourceId === this.state.currentPageId + ); + + await this.handleEvent('onPageLoad', currentPageEvents); }); } ); @@ -182,7 +219,7 @@ class ViewerComponent extends React.Component { runQueries = (data_queries) => { data_queries.forEach((query) => { if (query.options.runOnPageLoad && isQueryRunnable(query)) { - runQuery(this, query.id, query.name, undefined, 'view'); + runQuery(this.getViewerRef(), query.id, query.name, undefined, 'view'); } }); }; @@ -239,13 +276,14 @@ class ViewerComponent extends React.Component { }; loadApplicationBySlug = (slug, authentication_failed = false) => { - appsService - .getAppBySlug(slug) + appService + .fetchAppBySlug(slug) .then((data) => { - if (authentication_failed && !data.current_version_id) { - redirectToErrorPage(ERROR_TYPES.URL_UNAVAILABLE, {}); + const isAppPublic = data?.is_public; + if (authentication_failed && !isAppPublic) { + return redirectToErrorPage(ERROR_TYPES.URL_UNAVAILABLE, {}); } - this.setStateForApp(data); + this.setStateForApp(data, true); this.setStateForContainer(data); this.setWindowTitle(data.name); }) @@ -265,11 +303,11 @@ class ViewerComponent extends React.Component { }; loadApplicationByVersion = (appId, versionId) => { - appsService - .getAppByVersion(appId, versionId) + appService + .fetchAppByVersion(appId, versionId) .then((data) => { this.setStateForApp(data); - this.setStateForContainer(data); + this.setStateForContainer(data, versionId); }) .catch(() => { this.setState({ @@ -278,6 +316,9 @@ class ViewerComponent extends React.Component { }); }; + updateQueryConfirmationList = (queryConfirmationList) => + useEditorStore.getState().actions.updateQueryConfirmationList(queryConfirmationList); + setupViewer() { this.subscription = authenticationService.currentSession.subscribe((currentSession) => { const slug = this.props.params.slug; @@ -306,6 +347,7 @@ class ViewerComponent extends React.Component { userVars, versionId, }); + versionId ? this.loadApplicationByVersion(appId, versionId) : this.loadApplicationBySlug(slug); } else if (currentSession?.authentication_failed) { this.loadApplicationBySlug(slug, true); @@ -352,9 +394,13 @@ class ViewerComponent extends React.Component { handlePageSwitchingBasedOnURLparam() { const handleOnURL = this.props.params.pageHandle; - const pageIdCorrespondingToHandleOnURL = handleOnURL - ? this.findPageIdFromHandle(handleOnURL) - : this.state.appDefinition.homePageId; + + const shouldShowPage = handleOnURL ? this.validatePageHandle(handleOnURL) : true; + + if (!shouldShowPage) return this.switchPage(this.state.appDefinition.homePageId); + + const pageIdCorrespondingToHandleOnURL = + handleOnURL && shouldShowPage ? this.findPageIdFromHandle(handleOnURL) : this.state.appDefinition.homePageId; const currentPageId = this.state.currentPageId; if (pageIdCorrespondingToHandleOnURL != this.state.currentPageId) { @@ -388,20 +434,23 @@ class ViewerComponent extends React.Component { name: targetPage.name, }, async () => { - computeComponentState(this, this.state.appDefinition?.pages[this.state.currentPageId].components).then( - async () => { - // eslint-disable-next-line no-unsafe-optional-chaining - const { events } = this.state.appDefinition?.pages[this.state.currentPageId]; - for (const event of events ?? []) { - await this.handleEvent(event.eventId, event); - } - } - ); + computeComponentState(this.state.appDefinition?.pages[this.state.currentPageId].components).then(async () => { + const currentPageEvents = this.state.events.filter( + (event) => event.target === 'page' && event.sourceId === this.state.currentPageId + ); + + await this.handleEvent('onPageLoad', currentPageEvents); + }); } ); } } + validatePageHandle(handle) { + const allPages = this.state.appDefinition.pages; + return Object.values(allPages).some((page) => page.handle === handle && !page.disabled); + } + findPageIdFromHandle(handle) { return ( Object.entries(this.state.appDefinition.pages).filter(([_id, page]) => page.handle === handle)?.[0]?.[0] ?? @@ -466,7 +515,9 @@ class ViewerComponent extends React.Component { ); }; - handleEvent = (eventName, options) => onEvent(this, eventName, options, 'view'); + handleEvent = (eventName, events, options) => { + return onEvent(this.getViewerRef(), eventName, events, options, 'view'); + }; computeCanvasMaxWidth = () => { const { appDefinition } = this.state; @@ -493,11 +544,11 @@ class ViewerComponent extends React.Component { deviceWindowWidth, defaultComponentStateComputed, dataQueries, - queryConfirmationList, canvasWidth, } = this.state; const currentCanvasWidth = canvasWidth; + const queryConfirmationList = this.props?.queryConfirmationList ?? []; const canvasMaxWidth = this.computeCanvasMaxWidth(); @@ -531,8 +582,10 @@ class ViewerComponent extends React.Component { 0} message={'Do you want to run this query?'} - onConfirm={(queryConfirmationData) => onQueryConfirmOrCancel(this, queryConfirmationData, true, 'view')} - onCancel={() => onQueryConfirmOrCancel(this, queryConfirmationList[0], false, 'view')} + onConfirm={(queryConfirmationData) => + onQueryConfirmOrCancel(this.getViewerRef(), queryConfirmationData, true, 'view') + } + onCancel={() => onQueryConfirmOrCancel(this.getViewerRef(), queryConfirmationList[0], false, 'view')} queryConfirmationData={queryConfirmationList[0]} key={queryConfirmationList[0]?.queryName} /> @@ -591,7 +644,7 @@ class ViewerComponent extends React.Component { snapToGrid={true} appLoading={isLoading} darkMode={this.props.darkMode} - onEvent={(eventName, options) => onEvent(this, eventName, options, 'view')} + onEvent={this.handleEvent} mode="view" deviceWindowWidth={deviceWindowWidth} selectedComponent={this.state.selectedComponent} @@ -602,11 +655,9 @@ class ViewerComponent extends React.Component { onComponentClick(this, id, component, 'view'); }} onComponentOptionChanged={(component, optionName, value) => { - return onComponentOptionChanged(this, component, optionName, value); + return onComponentOptionChanged(component, optionName, value); }} - onComponentOptionsChanged={(component, options) => - onComponentOptionsChanged(this, component, options) - } + onComponentOptionsChanged={onComponentOptionsChanged} canvasWidth={this.getCanvasWidth()} dataQueries={dataQueries} currentPageId={this.state.currentPageId} @@ -629,19 +680,23 @@ class ViewerComponent extends React.Component { } const withStore = (Component) => (props) => { const currentState = useCurrentStateStore(); - const { currentLayout } = useEditorStore( + const { currentLayout, queryConfirmationList } = useEditorStore( (state) => ({ currentLayout: state?.currentLayout, + queryConfirmationList: state?.queryConfirmationList, }), shallow ); + const { updateState } = useAppDataActions(); return ( ); }; diff --git a/frontend/src/HomePage/ExportAppModal.jsx b/frontend/src/HomePage/ExportAppModal.jsx index 8942deb386..d523f1d04b 100644 --- a/frontend/src/HomePage/ExportAppModal.jsx +++ b/frontend/src/HomePage/ExportAppModal.jsx @@ -54,7 +54,7 @@ export default function ExportAppModal({ title, show, closeModal, customClassNam const requestBody = { ...appOpts, ...(exportTjDb && { tooljet_database: tables }), - organization_id: app.organization_id, + organization_id: app.organization_id ?? app.organizationId, }; appsService diff --git a/frontend/src/_helpers/appUtils.js b/frontend/src/_helpers/appUtils.js index 130374910b..2ce5c10812 100644 --- a/frontend/src/_helpers/appUtils.js +++ b/frontend/src/_helpers/appUtils.js @@ -32,6 +32,8 @@ import { useDataQueriesStore } from '@/_stores/dataQueriesStore'; import { useQueryPanelStore } from '@/_stores/queryPanelStore'; import { useCurrentStateStore, getCurrentState } from '@/_stores/currentStateStore'; import { useAppVersionStore } from '@/_stores/appVersionStore'; +import { camelizeKeys } from 'humps'; +import { useAppDataStore } from '@/_stores/appDataStore'; import { useEditorStore } from '@/_stores/editorStore'; const ERROR_TYPES = Object.freeze({ @@ -60,7 +62,7 @@ export function setCurrentStateAsync(_ref, changes) { }); } -export function onComponentOptionsChanged(_ref, component, options) { +export function onComponentOptionsChanged(component, options) { const componentName = component.name; const components = getCurrentState().components; let componentData = components[componentName]; @@ -76,7 +78,7 @@ export function onComponentOptionsChanged(_ref, component, options) { return Promise.resolve(); } -export function onComponentOptionChanged(_ref, component, option_name, value) { +export function onComponentOptionChanged(component, option_name, value) { const componentName = component.name; const components = getCurrentState().components; let componentData = components[componentName]; @@ -322,12 +324,11 @@ export async function runTransformation( } } -export async function executeActionsForEventId(_ref, eventId, component, mode, customVariables) { - const events = component?.definition?.events || []; - const filteredEvents = events.filter((event) => event.eventId === eventId); +export async function executeActionsForEventId(_ref, eventId, events = [], mode, customVariables) { + const filteredEvents = events.filter((event) => event?.event.eventId === eventId); for (const event of filteredEvents) { - await executeAction(_ref, event, mode, customVariables); // skipcq: JS-0032 + await executeAction(_ref, event.event, mode, customVariables); // skipcq: JS-0032 } } @@ -336,13 +337,11 @@ export function onComponentClick(_ref, id, component, mode = 'edit') { } export function onQueryConfirmOrCancel(_ref, queryConfirmationData, isConfirm = false, mode = 'edit') { - const filtertedQueryConfirmation = _ref.state?.queryConfirmationList.filter( + const filtertedQueryConfirmation = _ref?.queryConfirmationList.filter( (query) => query.queryId !== queryConfirmationData.queryId ); - _ref.setState({ - queryConfirmationList: filtertedQueryConfirmation, - }); + _ref.updateQueryConfirmationList(filtertedQueryConfirmation, 'check'); isConfirm && runQuery(_ref, queryConfirmationData.queryId, queryConfirmationData.queryName, true, mode); } @@ -362,7 +361,7 @@ function showModal(_ref, modal, show) { return Promise.resolve(); } - const modalMeta = _ref.state.appDefinition.pages[_ref.state.currentPageId].components[modalId]; + const modalMeta = _ref.appDefinition.pages[_ref.currentPageId].components[modalId]; //! NeedToFix const _components = { ...getCurrentState().components, @@ -377,7 +376,7 @@ function showModal(_ref, modal, show) { return Promise.resolve(); } -function logoutAction(_ref) { +function logoutAction() { localStorage.clear(); authenticationService.logout(true); @@ -442,7 +441,7 @@ function executeActionWithDebounce(_ref, event, mode, customVariables) { return runQuery(_ref, queryId, name, undefined, mode, resolvedParams); } case 'logout': { - return logoutAction(_ref); + return logoutAction(); } case 'open-webpage': { @@ -477,7 +476,7 @@ function executeActionWithDebounce(_ref, event, mode, customVariables) { } if (mode === 'view') { - _ref.props.navigate(url); + _ref.navigate(url); } else { if (confirm('The app will be opened in a new tab as the action is triggered from the editor.')) { window.open(urlJoin(window.public_config?.TOOLJET_HOST, url)); @@ -522,7 +521,7 @@ function executeActionWithDebounce(_ref, event, mode, customVariables) { } case 'set-table-page': { - setTablePageIndex(_ref, event.table, event.pageIndex); + setTablePageIndex(event.table, event.pageIndex); break; } @@ -585,12 +584,13 @@ function executeActionWithDebounce(_ref, event, mode, customVariables) { } case 'switch-page': { - const { name, disabled } = _ref.state.appDefinition.pages[event.pageId]; + const { name, disabled } = _ref.appDefinition.pages[event.pageId]; + // Don't allow switching to disabled page in editor as well as viewer if (!disabled) { _ref.switchPage(event.pageId, resolveReferences(event.queryParams, getCurrentState(), [], customVariables)); } - if (_ref.state.appDefinition.pages[event.pageId]) { + if (_ref.appDefinition.pages[event.pageId]) { if (disabled) { const generalProps = { navToDisablePage: { @@ -612,12 +612,15 @@ function executeActionWithDebounce(_ref, event, mode, customVariables) { } } -export async function onEvent(_ref, eventName, options, mode = 'edit') { +export async function onEvent(_ref, eventName, events, options = {}, mode = 'edit') { let _self = _ref; const { customVariables } = options; if (eventName === 'onPageLoad') { - await executeActionsForEventId(_ref, 'onPageLoad', { definition: { events: [options] } }, mode, customVariables); + //hack to make sure that the page is loaded before executing the actions + setTimeout(async () => { + return await executeActionsForEventId(_ref, 'onPageLoad', events, mode, customVariables); + }, 0); } if (eventName === 'onTrigger') { @@ -635,6 +638,7 @@ export async function onEvent(_ref, eventName, options, mode = 'edit') { if (eventName === 'onCalendarEventSelect') { const { component, calendarEvent } = options; + useCurrentStateStore.getState().actions.setCurrentState({ components: { ...getCurrentState().components, @@ -644,7 +648,8 @@ export async function onEvent(_ref, eventName, options, mode = 'edit') { }, }, }); - executeActionsForEventId(_ref, 'onCalendarEventSelect', component, mode, customVariables); + + executeActionsForEventId(_ref, 'onCalendarEventSelect', events, mode, customVariables); } if (eventName === 'onCalendarSlotSelect') { @@ -658,26 +663,18 @@ export async function onEvent(_ref, eventName, options, mode = 'edit') { }, }, }); - executeActionsForEventId(_ref, 'onCalendarSlotSelect', component, mode, customVariables); + + executeActionsForEventId(_ref, 'onCalendarSlotSelect', events, mode, customVariables); } if (eventName === 'onTableActionButtonClicked') { - const { component, data, action, rowId } = options; - useCurrentStateStore.getState().actions.setCurrentState({ - components: { - ...getCurrentState().components, - [component.name]: { - ...getCurrentState().components[component.name], - selectedRow: data, - selectedRowId: rowId, - }, - }, - }); - if (action && action.events) { - for (const event of action.events) { - if (event.actionId) { - // the event param uses a hacky workaround for using same format used by event manager ( multiple handlers ) - await executeAction(_self, { ...event, ...event.options }, mode, customVariables); + const { action, tableActionEvents } = options; + const executeableActions = tableActionEvents.filter((event) => event?.event?.ref === action?.name); + + if (action && executeableActions) { + for (const event of executeableActions) { + if (event?.event?.actionId) { + await executeAction(_self, event.event, mode, customVariables); } } } else { @@ -686,23 +683,12 @@ export async function onEvent(_ref, eventName, options, mode = 'edit') { } if (eventName === 'OnTableToggleCellChanged') { - const { component, column, rowId, row } = options; - useCurrentStateStore.getState().actions.setCurrentState({ - components: { - ...getCurrentState().components, - [component.name]: { - ...getCurrentState().components[component.name], - selectedRow: row, - selectedRowId: rowId, - }, - }, - }); + const { column, tableColumnEvents } = options; - if (column && column.events) { - for (const event of column.events) { - if (event.actionId) { - // the event param uses a hacky workaround for using same format used by event manager ( multiple handlers ) - await executeAction(_self, { ...event, ...event.options }, mode, customVariables); + if (column && tableColumnEvents) { + for (const event of tableColumnEvents) { + if (event?.event?.actionId) { + await executeAction(_self, event.event, mode, customVariables); } } } else { @@ -763,18 +749,15 @@ export async function onEvent(_ref, eventName, options, mode = 'edit') { 'onNewRowsAdded', ].includes(eventName) ) { - const { component } = options; - executeActionsForEventId(_ref, eventName, component, mode, customVariables); + executeActionsForEventId(_ref, eventName, events, mode, customVariables); } if (eventName === 'onBulkUpdate') { - onComponentOptionChanged(_self, options.component, 'isSavingChanges', true); - await executeActionsForEventId(_self, eventName, options.component, mode, customVariables); - onComponentOptionChanged(_self, options.component, 'isSavingChanges', false); + await executeActionsForEventId(_self, eventName, events, mode, customVariables); } if (['onDataQuerySuccess', 'onDataQueryFailure'].includes(eventName)) { - await executeActionsForEventId(_self, eventName, options, mode, customVariables); + await executeActionsForEventId(_self, eventName, events, mode, customVariables); } } @@ -940,6 +923,10 @@ export function previewQuery(_ref, query, calledFromQuery = false, parameters = export function runQuery(_ref, queryId, queryName, confirmed = undefined, mode = 'edit', parameters = {}) { const query = useDataQueriesStore.getState().dataQueries.find((query) => query.id === queryId); + const queryEvents = useAppDataStore + .getState() + .events.filter((event) => event.target === 'data_query' && event.sourceId === queryId); + let dataQuery = {}; if (query) { @@ -951,9 +938,11 @@ export function runQuery(_ref, queryId, queryName, confirmed = undefined, mode = const options = getQueryVariables(dataQuery.options, getCurrentState()); - if (dataQuery.options.requestConfirmation) { - // eslint-disable-next-line no-unsafe-optional-chaining - const queryConfirmationList = _ref.state?.queryConfirmationList ? [..._ref.state?.queryConfirmationList] : []; + if (dataQuery.options?.requestConfirmation) { + const queryConfirmationList = useEditorStore.getState().queryConfirmationList + ? [...useEditorStore.getState().queryConfirmationList] + : []; + const queryConfirmation = { queryId, queryName, @@ -963,9 +952,8 @@ export function runQuery(_ref, queryId, queryName, confirmed = undefined, mode = } if (confirmed === undefined) { - _ref.setState({ - queryConfirmationList, - }); + //!check + _ref.updateQueryConfirmationList(queryConfirmationList); return; } } @@ -1071,9 +1059,7 @@ export function runQuery(_ref, queryId, queryName, confirmed = undefined, mode = }, }); resolve(data); - onEvent(_self, 'onDataQueryFailure', { - definition: { events: dataQuery.options.events }, - }); + onEvent(_self, 'onDataQueryFailure', queryEvents); if (mode !== 'view') { const err = query.kind == 'tooljetdb' ? data?.error || data : _.isEmpty(data.data) ? data : data.data; toast.error(err?.message); @@ -1111,9 +1097,7 @@ export function runQuery(_ref, queryId, queryName, confirmed = undefined, mode = }, }); resolve(finalData); - onEvent(_self, 'onDataQueryFailure', { - definition: { events: dataQuery.options.events }, - }); + onEvent(_self, 'onDataQueryFailure', queryEvents); return; } } @@ -1152,7 +1136,7 @@ export function runQuery(_ref, queryId, queryName, confirmed = undefined, mode = }, }); resolve({ status: 'ok', data: finalData }); - onEvent(_self, 'onDataQuerySuccess', { definition: { events: dataQuery.options.events } }, mode); + onEvent(_self, 'onDataQuerySuccess', queryEvents, mode); } }) .catch(({ error }) => { @@ -1171,7 +1155,7 @@ export function runQuery(_ref, queryId, queryName, confirmed = undefined, mode = }); } -export function setTablePageIndex(_ref, tableId, index) { +export function setTablePageIndex(tableId, index) { if (_.isEmpty(tableId)) { console.log('No table is associated with this event.'); return Promise.resolve(); @@ -1192,52 +1176,69 @@ export function renderTooltip({ props, text }) { ); } -export function computeComponentState(_ref, components = {}) { - let componentState = {}; - const currentComponents = getCurrentState().components; - Object.keys(components).forEach((key) => { - const component = components[key]; - const componentMeta = componentTypes.find((comp) => component.component.component === comp.component); +/* +@computeComponentState: (components = {}) => Promise +This change is made to enhance the code readability by optimizing the logic +for computing component state. It replaces the previous try-catch block with +a more efficient approach, precomputing the parent component types and using +conditional checks for better performance and error handling.*/ - const existingComponentName = Object.keys(currentComponents).find((comp) => currentComponents[comp].id === key); - const existingValues = currentComponents[existingComponentName]; +export function computeComponentState(components = {}) { + try { + let componentState = {}; + const currentComponents = getCurrentState().components; - if (component.parent) { - const parentComponent = components[component.parent]; - let isListView = false, - isForm = false; - try { - isListView = parentComponent.component.component === 'Listview'; - isForm = parentComponent.component.component === 'Form'; - } catch { - console.log('error'); - } + // Precompute parent component types + const parentComponentTypes = {}; + Object.keys(components).forEach((key) => { + const { component } = components[key]; + parentComponentTypes[key] = component.component; + }); - if (!isListView && !isForm) { - componentState[component.component.name] = { + Object.keys(components).forEach((key) => { + if (!components[key]) return; + + const { component } = components[key]; + const componentMeta = componentTypes.find((comp) => component.component === comp.component); + + const existingComponentName = Object.keys(currentComponents).find((comp) => currentComponents[comp].id === key); + const existingValues = currentComponents[existingComponentName]; + + if (component.parent) { + const parentComponentType = parentComponentTypes[component.parent]; + + if (parentComponentType !== 'Listview' && parentComponentType !== 'Form') { + componentState[component.name] = { + ...componentMeta.exposedVariables, + id: key, + ...existingValues, + }; + } + } else { + componentState[component.name] = { ...componentMeta.exposedVariables, id: key, ...existingValues, }; } - } else { - componentState[component.component.name] = { - ...componentMeta.exposedVariables, - id: key, - ...existingValues, - }; - } - }); + }); - useCurrentStateStore.getState().actions.setCurrentState({ - components: { - ...componentState, - }, - }); + useCurrentStateStore.getState().actions.setCurrentState({ + components: { + ...componentState, + }, + }); - return setStateAsync(_ref, { - defaultComponentStateComputed: true, - }); + return new Promise((resolve) => { + useEditorStore.getState().actions.updateEditorState({ + defaultComponentStateComputed: true, + }); + resolve(); + }); + } catch (error) { + console.log(error); + return Promise.reject(error); + } } export const getSvgIcon = (key, height = 50, width = 50, iconFile = undefined, styles = {}) => { @@ -1253,13 +1254,13 @@ export const getSvgIcon = (key, height = 50, width = 50, iconFile = undefined, s }; export const debuggerActions = { - error: (_self, errors) => { + error: (errors) => { useCurrentStateStore.getState().actions.setErrors({ ...errors, }); }, - flush: (_self) => { + flush: () => { useCurrentStateStore.getState().actions.setCurrentState({ errors: {}, }); @@ -1370,105 +1371,148 @@ export const getComponentName = (currentState, id) => { } }; -const updateNewComponents = (pageId, appDefinition, newComponents, updateAppDefinition) => { +const updateNewComponents = (pageId, appDefinition, newComponents, updateAppDefinition, componentMap, isCut) => { const newAppDefinition = JSON.parse(JSON.stringify(appDefinition)); - newComponents.forEach((newComponent) => { - newComponent.component.name = computeComponentName( - newComponent.component.component, - newAppDefinition.pages[pageId].components - ); - newAppDefinition.pages[pageId].components[newComponent.id] = newComponent; - }); - updateAppDefinition(newAppDefinition); + + newAppDefinition.pages[pageId].components = { + ...newAppDefinition.pages[pageId].components, + ...newComponents, + }; + + const opts = { + componentAdded: true, + containerChanges: true, + }; + + if (!isCut) { + opts.cloningComponent = componentMap; + } + + updateAppDefinition(newAppDefinition, opts); }; -export const cloneComponents = (_ref, updateAppDefinition, isCloning = true, isCut = false) => { - const { appDefinition, currentPageId } = _ref.state; - const selectedComponents = useEditorStore.getState().selectedComponents; +export const cloneComponents = ( + selectedComponents, + appDefinition, + currentPageId, + updateAppDefinition, + isCloning = true, + isCut = false +) => { if (selectedComponents.length < 1) return getSelectedText(); + const { components: allComponents } = appDefinition.pages[currentPageId]; + + // if parent is selected, then remove the parent from the selected components + const filteredSelectedComponents = selectedComponents.filter((component) => { + const parentComponentId = component.component?.parent; + if (parentComponentId) { + // Check if the parent component is also selected + const isParentSelected = selectedComponents.some((comp) => comp.id === parentComponentId); + + // If the parent is selected, filter out the child component + if (isParentSelected) { + return false; + } + } + return true; + }); + let newDefinition = _.cloneDeep(appDefinition); let newComponents = [], newComponentObj = {}, addedComponentId = new Set(); - for (let selectedComponent of selectedComponents) { + for (let selectedComponent of filteredSelectedComponents) { if (addedComponentId.has(selectedComponent.id)) continue; const component = { - id: selectedComponent.id, component: allComponents[selectedComponent.id]?.component, layouts: allComponents[selectedComponent.id]?.layouts, parent: allComponents[selectedComponent.id]?.parent, + componentId: selectedComponent.id, }; addedComponentId.add(selectedComponent.id); let clonedComponent = JSON.parse(JSON.stringify(component)); - clonedComponent.parent = undefined; - clonedComponent.children = []; - clonedComponent.children = [...getChildComponents(allComponents, component, clonedComponent, addedComponentId)]; - newComponents = [...newComponents, clonedComponent]; + + newComponents.push(clonedComponent); + const children = getAllChildComponents(allComponents, selectedComponent.id); + + if (children.length > 0) { + newComponents.push(...children); + } + newComponentObj = { newComponents, isCloning, isCut, + currentPageId, }; } + if (isCloning) { - addComponents(currentPageId, appDefinition, updateAppDefinition, undefined, newComponentObj); + const parentId = selectedComponents[0]['component']?.parent ?? undefined; + + addComponents(currentPageId, appDefinition, updateAppDefinition, parentId, newComponentObj, true); toast.success('Component cloned succesfully'); } else if (isCut) { navigator.clipboard.writeText(JSON.stringify(newComponentObj)); - removeSelectedComponent(currentPageId, newDefinition, selectedComponents); - updateAppDefinition(newDefinition); + removeSelectedComponent(currentPageId, newDefinition, selectedComponents, updateAppDefinition); } else { navigator.clipboard.writeText(JSON.stringify(newComponentObj)); const successMessage = newComponentObj.newComponents.length > 1 ? 'Components copied successfully' : 'Component copied successfully'; toast.success(successMessage); } - _ref.setState({ currentSidebarTab: 2 }); + + return new Promise((resolve) => { + useEditorStore.getState().actions.updateEditorState({ + currentSidebarTab: 2, + }); + resolve(); + }); }; -const getChildComponents = (allComponents, component, parentComponent, addedComponentId) => { - let childComponents = [], - selectedChildComponents = []; +const getAllChildComponents = (allComponents, parentId) => { + const childComponents = []; - if (component.component.component === 'Tabs' || component.component.component === 'Calendar') { - childComponents = Object.keys(allComponents).filter((key) => allComponents[key].parent?.startsWith(component.id)); - } else { - childComponents = Object.keys(allComponents).filter((key) => allComponents[key].parent === component.id); - } + Object.keys(allComponents).forEach((componentId) => { + const componentParentId = allComponents[componentId].component?.parent; - childComponents.forEach((componentId) => { - let childComponent = JSON.parse(JSON.stringify(allComponents[componentId])); - childComponent.id = componentId; - const newComponent = JSON.parse( - JSON.stringify({ - id: componentId, - component: allComponents[componentId]?.component, - layouts: allComponents[componentId]?.layouts, - parent: allComponents[componentId]?.parent, - }) - ); - addedComponentId.add(componentId); + const isParentTabORCalendar = + allComponents[parentId]?.component?.component === 'Tabs' || + allComponents[parentId]?.component?.component === 'Calendar'; - if ((component.component.component === 'Tabs') | (component.component.component === 'Calendar')) { - const childTabId = childComponent.parent.split('-').at(-1); - childComponent.parent = `${parentComponent.id}-${childTabId}`; - } else { - childComponent.parent = parentComponent.id; + if (componentParentId && isParentTabORCalendar) { + const childComponent = allComponents[componentId]; + const childTabId = componentParentId.split('-').at(-1); + if (componentParentId === `${parentId}-${childTabId}`) { + childComponent.componentId = componentId; + childComponents.push(childComponent); + + // Recursively find children of the current child component + const childrenOfChild = getAllChildComponents(allComponents, componentId); + childComponents.push(...childrenOfChild); + } + } + + if (componentParentId === parentId) { + const childComponent = allComponents[componentId]; + childComponent.componentId = componentId; + childComponents.push(childComponent); + + // Recursively find children of the current child component + const childrenOfChild = getAllChildComponents(allComponents, componentId); + childComponents.push(...childrenOfChild); } - parentComponent.children = [...(parentComponent.children || []), childComponent]; - childComponent.children = [...getChildComponents(allComponents, newComponent, childComponent, addedComponentId)]; - selectedChildComponents.push(childComponent); }); - return selectedChildComponents; + return childComponents; }; const updateComponentLayout = (components, parentId, isCut = false) => { let prevComponent; components.forEach((component, index) => { Object.keys(component.layouts).map((layout) => { - if (parentId !== undefined) { + if (parentId !== undefined && !component?.component?.parent) { if (index > 0) { component.layouts[layout].top = prevComponent.layouts[layout].top + prevComponent.layouts[layout].height; component.layouts[layout].left = 0; @@ -1477,56 +1521,100 @@ const updateComponentLayout = (components, parentId, isCut = false) => { component.layouts[layout].left = 0; } prevComponent = component; - } else if (!isCut) { + } else if (!isCut && !component.component.parent) { component.layouts[layout].top = component.layouts[layout].top + component.layouts[layout].height; } }); }); }; +// +const isChildOfTabsOrCalendar = (component, allComponents = [], componentParentId = undefined) => { + const parentId = componentParentId ?? component.component?.parent?.split('-').slice(0, -1).join('-'); -export const addComponents = (pageId, appDefinition, appDefinitionChanged, parentId = undefined, newComponentObj) => { - const finalComponents = []; + const parentComponent = allComponents.find((comp) => comp.componentId === parentId); + + if (parentComponent) { + return parentComponent.component.component === 'Tabs' || parentComponent.component.component === 'Calendar'; + } + + return false; +}; + +export const addComponents = ( + pageId, + appDefinition, + appDefinitionChanged, + parentId = undefined, + newComponentObj, + fromClipboard = false +) => { + const finalComponents = {}; + const componentMap = {}; let parentComponent = undefined; - const { isCloning, isCut, newComponents: pastedComponent = [] } = newComponentObj; + const { isCloning, isCut, newComponents: pastedComponents = [], currentPageId } = newComponentObj; if (parentId) { const id = Object.keys(appDefinition.pages[pageId].components).filter((key) => parentId.startsWith(key)); parentComponent = JSON.parse(JSON.stringify(appDefinition.pages[pageId].components[id[0]])); - parentComponent.id = parentId; } - !isCloning && updateComponentLayout(pastedComponent, parentId, isCut); + pastedComponents.forEach((component) => { + const newComponentId = isCut ? component.componentId : uuidv4(); + const componentName = computeComponentName(component.component.component, { + ...appDefinition.pages[pageId].components, + ...finalComponents, + }); - const buildComponents = (components, parentComponent = undefined, skipTabCalendarCheck = false) => { - if (Array.isArray(components) && components.length > 0) { - components.forEach((component) => { - const newComponent = { - id: uuidv4(), - component: component?.component, - layouts: component?.layouts, - }; - if (parentComponent) { - if ( - !skipTabCalendarCheck && - (parentComponent.component.component === 'Tabs' || parentComponent.component.component === 'Calendar') - ) { - const childTabId = component.parent.split('-').at(-1); - newComponent.parent = `${parentComponent.id}-${childTabId}`; - } else { - newComponent.parent = parentComponent.id; - } - } - finalComponents.push(newComponent); - if (component.children.length > 0) { - buildComponents(component.children, newComponent); - } - }); + const isParentTabOrCalendar = isChildOfTabsOrCalendar(component, pastedComponents, parentId); + const parentRef = isParentTabOrCalendar + ? component.component.parent.split('-').slice(0, -1).join('-') + : component.component.parent; + const isParentAlsoCopied = parentRef && componentMap[parentRef]; + + componentMap[component.componentId] = newComponentId; + let isChild = isParentAlsoCopied ? component.component.parent : parentId; + const componentData = JSON.parse(JSON.stringify(component.component)); + + if (isCloning && parentId && !componentData.parent) { + isChild = component.component.parent; } - }; - buildComponents(pastedComponent, parentComponent, true); + if (!parentComponent && !isParentAlsoCopied && fromClipboard) { + isChild = undefined; + componentData.parent = undefined; + } - updateNewComponents(pageId, appDefinition, finalComponents, appDefinitionChanged); + if (!isCloning && parentComponent && fromClipboard) { + componentData.parent = isParentAlsoCopied ?? parentId; + } else if (isChild && isChildOfTabsOrCalendar(component, pastedComponents, parentId)) { + const parentId = component.component.parent.split('-').slice(0, -1).join('-'); + const childTabId = component.component.parent.split('-').at(-1); + + componentData.parent = `${componentMap[parentId]}-${childTabId}`; + } else if (isChild) { + const isParentInMap = componentMap[isChild] !== undefined; + + componentData.parent = isParentInMap ? componentMap[isChild] : isChild; + } + + const newComponent = { + component: { + ...componentData, + name: componentName, + }, + layouts: component.layouts, + }; + + finalComponents[newComponentId] = newComponent; + + // const doesComponentHaveChildren = getAllChildComponents + }); + + if (currentPageId === pageId) { + updateComponentLayout(pastedComponents, parentId, isCut); + } + + updateNewComponents(pageId, appDefinition, finalComponents, appDefinitionChanged, componentMap, isCut); !isCloning && toast.success('Component pasted succesfully'); }; @@ -1594,6 +1682,7 @@ export const addNewWidgetToTheEditor = ( const widgetsWithDefaultComponents = ['Listview', 'Tabs', 'Form', 'Kanban']; + const nonActiveLayout = currentLayout === 'desktop' ? 'mobile' : 'desktop'; const newComponent = { id: uuidv4(), component: componentData, @@ -1604,6 +1693,12 @@ export const addNewWidgetToTheEditor = ( width: defaultWidth, height: defaultHeight, }, + [nonActiveLayout]: { + top: top, + left: left, + width: defaultWidth, + height: defaultHeight, + }, }, withDefaultChildren: widgetsWithDefaultComponents.includes(componentData.component), @@ -1619,26 +1714,38 @@ export function snapToGrid(canvasWidth, x, y) { const snappedY = Math.round(y / 10) * 10; return [snappedX, snappedY]; } -export const removeSelectedComponent = (pageId, newDefinition, selectedComponents) => { - selectedComponents.forEach((component) => { - let childComponents = []; +export const removeSelectedComponent = (pageId, newDefinition, selectedComponents, updateAppDefinition) => { + const toDeleteComponents = []; - if (newDefinition.pages[pageId].components[component.id]?.component?.component === 'Tabs') { - childComponents = Object.keys(newDefinition.pages[pageId].components).filter((key) => - newDefinition.pages[pageId].components[key].parent?.startsWith(component.id) - ); - } else { - childComponents = Object.keys(newDefinition.pages[pageId].components).filter( - (key) => newDefinition.pages[pageId].components[key].parent === component.id - ); + if (selectedComponents.length < 1) return getSelectedText(); + + const { components: allComponents } = newDefinition.pages[pageId]; + + const findAllChildComponents = (componentId) => { + if (!toDeleteComponents.includes(componentId)) { + toDeleteComponents.push(componentId); + + // Find the children of this component + const children = getAllChildComponents(allComponents, componentId).map((child) => child.componentId); + + if (children.length > 0) { + // Recursively find children of children + children.forEach((child) => { + findAllChildComponents(child); + }); + } } + }; - childComponents.forEach((componentId) => { - delete newDefinition.pages[pageId].components[componentId]; - }); - - delete newDefinition.pages[pageId].components[component.id]; + selectedComponents.forEach((component) => { + findAllChildComponents(component.id); }); + + toDeleteComponents.forEach((componentId) => { + delete newDefinition.pages[pageId].components[componentId]; + }); + + updateAppDefinition(newDefinition, { componentDefinitionChanged: true, componentDeleted: true, componentCut: true }); }; const getSelectedText = () => { @@ -1678,7 +1785,7 @@ export const runQueries = (queries, _ref) => { }); }; -export const computeQueryState = (queries, _ref) => { +export const computeQueryState = (queries) => { let queryState = {}; queries.forEach((query) => { if (query.plugin?.plugin_id) { @@ -1705,6 +1812,87 @@ export const computeQueryState = (queries, _ref) => { } }; +export const buildComponentMetaDefinition = (components = {}) => { + for (const componentId in components) { + const currentComponentData = components[componentId]; + + const componentMeta = componentTypes.find((comp) => currentComponentData.component.component === comp.component); + + const mergedDefinition = { + ...componentMeta.definition, + + properties: { + ...componentMeta.definition.properties, + ...currentComponentData?.component.definition.properties, + }, + + styles: { + ...componentMeta.definition.styles, + ...currentComponentData?.component.definition.styles, + }, + generalStyles: { + ...componentMeta.definition.generalStyles, + ...currentComponentData?.component.definition.generalStyles, + }, + validation: { + ...componentMeta.definition.validation, + ...currentComponentData?.component.definition.validation, + }, + others: { + ...componentMeta.definition.others, + ...currentComponentData?.component.definition.others, + }, + general: { + ...componentMeta.definition.general, + ...currentComponentData?.component.definition.general, + }, + }; + + const mergedComponent = { + component: { + ...componentMeta, + ...currentComponentData.component, + }, + layouts: { + ...currentComponentData.layouts, + }, + withDefaultChildren: componentMeta.withDefaultChildren ?? false, + }; + + mergedComponent.component.definition = mergedDefinition; + + components[componentId] = mergedComponent; + } + + return components; +}; + +export const buildAppDefinition = (data) => { + const editingVersion = _.omit(camelizeKeys(data.editing_version), ['definition', 'updatedAt', 'createdAt', 'name']); + + editingVersion['currentVersionId'] = editingVersion.id; + _.unset(editingVersion, 'id'); + + const pages = data.pages.reduce((acc, page) => { + const currentComponents = buildComponentMetaDefinition(_.cloneDeep(page?.components)); + + page.components = currentComponents; + + acc[page.id] = page; + + return acc; + }, {}); + + const appJSON = { + globalSettings: editingVersion.globalSettings, + homePageId: editingVersion.homePageId, + showViewerNavigation: editingVersion.showViewerNavigation ?? true, + pages: pages, + }; + + return appJSON; +}; + export const removeFunctionObjects = (obj) => { for (const key in obj) { if (typeof obj[key] === 'function') { diff --git a/frontend/src/_helpers/utils.js b/frontend/src/_helpers/utils.js index c37cfb423d..3f7795ca06 100644 --- a/frontend/src/_helpers/utils.js +++ b/frontend/src/_helpers/utils.js @@ -570,10 +570,11 @@ export const hightlightMentionedUserInComment = (comment) => { }; export const generateAppActions = (_ref, queryId, mode, isPreview = false) => { - const currentPageId = _ref.state.currentPageId; - const currentComponents = _ref.state?.appDefinition?.pages[currentPageId]?.components - ? Object.entries(_ref.state.appDefinition.pages[currentPageId]?.components) + const currentPageId = _ref.currentPageId; + const currentComponents = _ref.appDefinition?.pages[currentPageId]?.components + ? Object.entries(_ref.appDefinition.pages[currentPageId]?.components) : {}; + const runQuery = (queryName = '', parameters) => { const query = useDataQueriesStore.getState().dataQueries.find((query) => { const isFound = query.name === queryName; @@ -743,7 +744,7 @@ export const generateAppActions = (_ref, queryId, mode, isPreview = false) => { }); return Promise.resolve(); } - const pages = _ref.state.appDefinition.pages; + const pages = _ref.appDefinition.pages; const pageId = Object.keys(pages).find((key) => pages[key].handle === pageHandle); if (!pageId) { diff --git a/frontend/src/_hooks/useDebouncedArrowKeyPress.js b/frontend/src/_hooks/useDebouncedArrowKeyPress.js new file mode 100644 index 0000000000..0e041f5eeb --- /dev/null +++ b/frontend/src/_hooks/useDebouncedArrowKeyPress.js @@ -0,0 +1,37 @@ +// useDebouncedArrowKeyPress.js +import { useEffect, useState } from 'react'; + +function useDebouncedArrowKeyPress(delay) { + const [lastKeyPressTimestamp, setLastKeyPressTimestamp] = useState(0); + + useEffect(() => { + let timer; + + function handleKeyPress(event) { + if ( + event.key === 'ArrowUp' || + event.key === 'ArrowDown' || + event.key === 'ArrowLeft' || + event.key === 'ArrowRight' + ) { + // Arrow key was pressed; debounce the update + clearTimeout(timer); + + timer = setTimeout(() => { + // Trigger the update only after the specified delay + setLastKeyPressTimestamp(Date.now()); + }, delay); + } + } + + document.addEventListener('keydown', handleKeyPress); + + return () => { + document.removeEventListener('keydown', handleKeyPress); + }; + }, [delay]); + + return lastKeyPressTimestamp; +} + +export default useDebouncedArrowKeyPress; diff --git a/frontend/src/_services/app.service.js b/frontend/src/_services/app.service.js index b530bb2b1c..55820bee2b 100644 --- a/frontend/src/_services/app.service.js +++ b/frontend/src/_services/app.service.js @@ -3,6 +3,24 @@ import { authHeader, handleResponse } from '@/_helpers'; export const appService = { getConfig, + getAll, + createApp, + cloneApp, + exportApp, + importApp, + exportResource, + importResource, + cloneResource, + changeIcon, + deleteApp, + getApp, + fetchApp, + getAppBySlug, + fetchAppBySlug, + getAppByVersion, + fetchAppByVersion, + saveApp, + getAppUsers, createAppUser, setPasswordFromToken, acceptInvite, @@ -13,6 +31,142 @@ function getConfig() { return fetch(`${config.apiUrl}/config`, requestOptions).then(handleResponse); } +function getAll(page, folder, searchKey) { + const requestOptions = { method: 'GET', headers: authHeader(), credentials: 'include' }; + if (page === 0) return fetch(`${config.apiUrl}/apps`, requestOptions).then(handleResponse); + else + return fetch( + `${config.apiUrl}/apps?page=${page}&folder=${folder || ''}&searchKey=${searchKey}`, + requestOptions + ).then(handleResponse); +} + +function createApp(body = {}) { + const requestOptions = { method: 'POST', headers: authHeader(), credentials: 'include', body: JSON.stringify(body) }; + return fetch(`${config.apiUrl}/apps`, requestOptions).then(handleResponse); +} + +function cloneApp(id) { + const requestOptions = { method: 'POST', headers: authHeader(), credentials: 'include' }; + return fetch(`${config.apiUrl}/apps/${id}/clone`, requestOptions).then(handleResponse); +} + +function exportApp(id, versionId) { + const requestOptions = { method: 'GET', headers: authHeader(), credentials: 'include' }; + return fetch(`${config.apiUrl}/apps/${id}/export${versionId ? `?versionId=${versionId}` : ''}`, requestOptions).then( + handleResponse + ); +} + +function exportResource(body) { + const requestOptions = { + method: 'POST', + headers: authHeader(), + body: JSON.stringify(body), + credentials: 'include', + }; + + return fetch(`${config.apiUrl}/v2/resources/export`, requestOptions).then(handleResponse); +} + +function importResource(body) { + const requestOptions = { + method: 'POST', + headers: authHeader(), + credentials: 'include', + body: JSON.stringify(body), + }; + return fetch(`${config.apiUrl}/v2/resources/import`, requestOptions).then(handleResponse); +} + +function cloneResource(body) { + const requestOptions = { + method: 'POST', + headers: authHeader(), + body: JSON.stringify(body), + credentials: 'include', + }; + + return fetch(`${config.apiUrl}/v2/resources/clone`, requestOptions).then(handleResponse); +} + +function getVersions(id) { + const requestOptions = { method: 'GET', headers: authHeader(), credentials: 'include' }; + return fetch(`${config.apiUrl}/apps/${id}/versions`, requestOptions).then(handleResponse); +} + +function importApp(body) { + const requestOptions = { method: 'POST', headers: authHeader(), credentials: 'include', body: JSON.stringify(body) }; + return fetch(`${config.apiUrl}/apps/import`, requestOptions).then(handleResponse); +} + +function getTables(id) { + const requestOptions = { method: 'GET', headers: authHeader(), credentials: 'include' }; + return fetch(`${config.apiUrl}/apps/${id}/tables`, requestOptions).then(handleResponse); +} + +function changeIcon(icon, appId) { + const requestOptions = { + method: 'PUT', + headers: authHeader(), + credentials: 'include', + body: JSON.stringify({ icon }), + }; + return fetch(`${config.apiUrl}/apps/${appId}/icons`, requestOptions).then(handleResponse); +} + +function getApp(id, accessType) { + const requestOptions = { method: 'GET', headers: authHeader(), credentials: 'include' }; + return fetch(`${config.apiUrl}/apps/${id}${accessType ? `?access_type=${accessType}` : ''}`, requestOptions).then( + handleResponse + ); +} + +// v2 api for fetching app +function fetchApp(id) { + const requestOptions = { method: 'GET', headers: authHeader(), credentials: 'include' }; + return fetch(`${config.apiUrl}/v2/apps/${id}`, requestOptions).then(handleResponse); +} + +function deleteApp(id) { + const requestOptions = { method: 'DELETE', headers: authHeader(), credentials: 'include' }; + return fetch(`${config.apiUrl}/apps/${id}`, requestOptions).then(handleResponse); +} + +function getAppBySlug(slug) { + const requestOptions = { method: 'GET', headers: authHeader(), credentials: 'include' }; + return fetch(`${config.apiUrl}/apps/slugs/${slug}`, requestOptions).then(handleResponse); +} + +function fetchAppBySlug(slug) { + const requestOptions = { method: 'GET', headers: authHeader(), credentials: 'include' }; + return fetch(`${config.apiUrl}/v2/apps/slugs/${slug}`, requestOptions).then((resp) => handleResponse(resp, true)); +} + +function getAppByVersion(appId, versionId) { + const requestOptions = { method: 'GET', headers: authHeader(), credentials: 'include' }; + return fetch(`${config.apiUrl}/apps/${appId}/versions/${versionId}`, requestOptions).then(handleResponse); +} +function fetchAppByVersion(appId, versionId) { + const requestOptions = { method: 'GET', headers: authHeader(), credentials: 'include' }; + return fetch(`${config.apiUrl}/v2/apps/${appId}/versions/${versionId}`, requestOptions).then(handleResponse); +} + +function saveApp(id, attributes) { + const requestOptions = { + method: 'PUT', + headers: authHeader(), + credentials: 'include', + body: JSON.stringify({ app: attributes }), + }; + return fetch(`${config.apiUrl}/apps/${id}`, requestOptions).then(handleResponse); +} + +function getAppUsers(id) { + const requestOptions = { method: 'GET', headers: authHeader(), credentials: 'include' }; + return fetch(`${config.apiUrl}/apps/${id}/users`, requestOptions).then(handleResponse); +} + function createAppUser(app_id, org_user_id, role) { const body = { app_id, diff --git a/frontend/src/_services/appVersion.service.js b/frontend/src/_services/appVersion.service.js index ab81ba8a9b..85a462a855 100644 --- a/frontend/src/_services/appVersion.service.js +++ b/frontend/src/_services/appVersion.service.js @@ -4,9 +4,16 @@ import { authHeader, handleResponse } from '@/_helpers'; export const appVersionService = { getAll, getOne, + getAppVersionData, create, del, save, + autoSaveApp, + saveAppVersionEventHandlers, + createAppVersionEventHandler, + deleteAppVersionEventHandler, + clonePage, + findAllEventsWithSourceId, }; function getAll(appId) { @@ -18,6 +25,10 @@ function getOne(appId, versionId) { const requestOptions = { method: 'GET', headers: authHeader(), credentials: 'include' }; return fetch(`${config.apiUrl}/apps/${appId}/versions/${versionId}`, requestOptions).then(handleResponse); } +function getAppVersionData(appId, versionId) { + const requestOptions = { method: 'GET', headers: authHeader(), credentials: 'include' }; + return fetch(`${config.apiUrl}/v2/apps/${appId}/versions/${versionId}`, requestOptions).then(handleResponse); +} function create(appId, versionName, versionFromId) { const body = { @@ -47,6 +58,7 @@ function save(appId, versionId, values, isUserSwitchedVersion = false) { const body = { is_user_switched_version: isUserSwitchedVersion }; if (values.definition) body['definition'] = values.definition; if (values.name) body['name'] = values.name; + if (values.diff) body['app_diff'] = values.diff; const requestOptions = { method: 'PUT', @@ -56,3 +68,118 @@ function save(appId, versionId, values, isUserSwitchedVersion = false) { }; return fetch(`${config.apiUrl}/apps/${appId}/versions/${versionId}`, requestOptions).then(handleResponse); } + +function autoSaveApp( + appId, + versionId, + diff, + type, + pageId, + operation, + isUserSwitchedVersion = false, + isComponentCutProcess = false +) { + const OPERATION = { + create: 'POST', + update: 'PUT', + delete: 'DELETE', + }; + + const bodyMappings = { + pages: { + create: { ...diff }, + delete: { ...diff }, + }, + global_settings: { + update: { ...diff }, + }, + }; + + const body = !type + ? { ...diff } + : bodyMappings[type]?.[operation] || { + is_user_switched_version: isUserSwitchedVersion, + pageId, + diff, + }; + + if (type === 'components' && operation === 'delete' && isComponentCutProcess) { + body['is_component_cut'] = true; + } + + const requestOptions = { + method: OPERATION[operation], + headers: authHeader(), + credentials: 'include', + body: JSON.stringify(body), + }; + + const url = `${config.apiUrl}/v2/apps/${appId}/versions/${versionId}/${type ?? ''}`; + + return fetch(url, requestOptions).then(handleResponse); +} + +function saveAppVersionEventHandlers(appId, versionId, events, updateType = 'update') { + const body = { + events, + updateType, + }; + + const requestOptions = { + method: 'PUT', + headers: authHeader(), + credentials: 'include', + body: JSON.stringify(body), + }; + return fetch(`${config.apiUrl}/v2/apps/${appId}/versions/${versionId}/events`, requestOptions).then(handleResponse); +} + +function createAppVersionEventHandler(appId, versionId, event) { + const body = { + ...event, + }; + + const requestOptions = { + method: 'POST', + headers: authHeader(), + credentials: 'include', + body: JSON.stringify(body), + }; + return fetch(`${config.apiUrl}/v2/apps/${appId}/versions/${versionId}/events`, requestOptions).then(handleResponse); +} + +function deleteAppVersionEventHandler(appId, versionId, eventId) { + const requestOptions = { + method: 'DELETE', + headers: authHeader(), + credentials: 'include', + }; + return fetch(`${config.apiUrl}/v2/apps/${appId}/versions/${versionId}/events/${eventId}`, requestOptions).then( + handleResponse + ); +} + +function clonePage(appId, versionId, pageId) { + const requestOptions = { + method: 'POST', + headers: authHeader(), + credentials: 'include', + }; + return fetch(`${config.apiUrl}/v2/apps/${appId}/versions/${versionId}/pages/${pageId}/clone`, requestOptions).then( + handleResponse + ); +} + +function findAllEventsWithSourceId(appId, versionId, sourceId = undefined) { + const requestOptions = { + method: 'GET', + headers: authHeader(), + credentials: 'include', + }; + + return fetch( + `${config.apiUrl}/v2/apps/${appId}/versions/${versionId}/events${sourceId ? `?sourceId=${sourceId}` : ''} + `, + requestOptions + ).then(handleResponse); +} diff --git a/frontend/src/_stores/appDataStore.js b/frontend/src/_stores/appDataStore.js index a3128308aa..1f5d36870f 100644 --- a/frontend/src/_stores/appDataStore.js +++ b/frontend/src/_stores/appDataStore.js @@ -1,19 +1,126 @@ +import { appVersionService } from '@/_services'; import { create, zustandDevTools } from './utils'; const initialState = { editingVersion: null, + currentUser: null, + apps: [], + appName: null, + slug: null, + isPublic: null, + isMaintenanceOn: null, + organizationId: null, + currentVersionId: null, + userId: null, + app: {}, + components: [], + pages: [], + layouts: [], + events: [], + eventHandlers: [], + appDefinitionDiff: null, + appDiffOptions: {}, isSaving: false, appId: null, + areOthersOnSameVersionAndPage: false, + appVersionPreviewLink: null, }; export const useAppDataStore = create( zustandDevTools( - (set) => ({ + (set, get) => ({ ...initialState, actions: { updateEditingVersion: (version) => set(() => ({ editingVersion: version })), + updateApps: (apps) => set(() => ({ apps: apps })), + updateState: (state) => set((prev) => ({ ...prev, ...state })), + updateAppDefinitionDiff: (appDefinitionDiff) => set(() => ({ appDefinitionDiff: appDefinitionDiff })), + updateAppVersion: (appId, versionId, pageId, appDefinitionDiff, isUserSwitchedVersion = false) => { + return new Promise((resolve, reject) => { + useAppDataStore.getState().actions.setIsSaving(true); + const isComponentCutProcess = get().appDiffOptions?.componentCut === true; + + appVersionService + .autoSaveApp( + appId, + versionId, + appDefinitionDiff.updateDiff, + appDefinitionDiff.type, + pageId, + appDefinitionDiff.operation, + isUserSwitchedVersion, + isComponentCutProcess + ) + .then(() => { + useAppDataStore.getState().actions.setIsSaving(false); + }) + .catch((error) => { + useAppDataStore.getState().actions.setIsSaving(false); + reject(error); + }) + .finally(() => resolve()); + }); + }, + updateAppVersionEventHandlers: async (events, updateType = 'update') => { + useAppDataStore.getState().actions.setIsSaving(true); + const appId = get().appId; + const versionId = get().currentVersionId; + + const response = await appVersionService.saveAppVersionEventHandlers(appId, versionId, events, updateType); + + useAppDataStore.getState().actions.setIsSaving(false); + const updatedEvents = get().events; + + updatedEvents.forEach((e, index) => { + const toUpdate = response.find((r) => r.id === e.id); + if (toUpdate) { + updatedEvents[index] = toUpdate; + } + }); + + set(() => ({ events: updatedEvents })); + }, + + createAppVersionEventHandlers: async (event) => { + useAppDataStore.getState().actions.setIsSaving(true); + const appId = get().appId; + const versionId = get().currentVersionId; + + const updatedEvents = get().events; + const response = await appVersionService.createAppVersionEventHandler(appId, versionId, event); + useAppDataStore.getState().actions.setIsSaving(false); + updatedEvents.push(response); + + set(() => ({ events: updatedEvents })); + }, + + deleteAppVersionEventHandler: async (eventId) => { + useAppDataStore.getState().actions.setIsSaving(true); + const appId = get().appId; + const versionId = get().currentVersionId; + + const updatedEvents = get().events; + + const response = await appVersionService.deleteAppVersionEventHandler(appId, versionId, eventId); + useAppDataStore.getState().actions.setIsSaving(false); + if (response?.affected === 1) { + updatedEvents.splice( + updatedEvents.findIndex((e) => e.id === eventId), + 1 + ); + + set(() => ({ events: updatedEvents })); + } + }, + autoUpdateEventStore: async (versionId) => { + const appId = get().appId; + const response = await appVersionService.findAllEventsWithSourceId(appId, versionId); + + set(() => ({ events: response })); + }, setIsSaving: (isSaving) => set(() => ({ isSaving })), setAppId: (appId) => set(() => ({ appId })), + setAppPreviewLink: (appVersionPreviewLink) => set(() => ({ appVersionPreviewLink })), }, }), { name: 'App Data Store' } @@ -23,3 +130,6 @@ export const useAppDataStore = create( export const useEditingVersion = () => useAppDataStore((state) => state.editingVersion); export const useIsSaving = () => useAppDataStore((state) => state.isSaving); export const useUpdateEditingVersion = () => useAppDataStore((state) => state.actions); +export const useCurrentUser = () => useAppDataStore((state) => state.currentUser); +export const useAppInfo = () => useAppDataStore((state) => state); +export const useAppDataActions = () => useAppDataStore((state) => state.actions); diff --git a/frontend/src/_stores/appVersionStore.js b/frontend/src/_stores/appVersionStore.js index 38e456479f..1824c28e87 100644 --- a/frontend/src/_stores/appVersionStore.js +++ b/frontend/src/_stores/appVersionStore.js @@ -5,6 +5,7 @@ const initialState = { isUserEditingTheVersion: false, releasedVersionId: null, isVersionReleased: false, + appVersions: [], }; export const useAppVersionStore = create( @@ -21,8 +22,12 @@ export const useAppVersionStore = create( releasedVersionId: versionId, isVersionReleased: get().editingVersion?.id ? get().editingVersion?.id === versionId : false, }), + setAppVersions: (versions) => set({ appVersions: versions }), }, }), { name: 'App Version Manager Store' } ) ); + +export const useAppVersionActions = () => useAppVersionStore((state) => state.actions); +export const useAppVersionState = () => useAppVersionStore((state) => state); diff --git a/frontend/src/_stores/currentStateStore.js b/frontend/src/_stores/currentStateStore.js index 4a805a8467..2e6c6f3eec 100644 --- a/frontend/src/_stores/currentStateStore.js +++ b/frontend/src/_stores/currentStateStore.js @@ -51,6 +51,7 @@ export const useCurrentState = () => page: state.page, succededQuery: state.succededQuery, constants: state.constants, + layout: state.layout, }; }, shallow); diff --git a/frontend/src/_stores/dataQueriesStore.js b/frontend/src/_stores/dataQueriesStore.js index ebc89dc89c..72f4e22c99 100644 --- a/frontend/src/_stores/dataQueriesStore.js +++ b/frontend/src/_stores/dataQueriesStore.js @@ -1,7 +1,7 @@ import { create, zustandDevTools } from './utils'; import { getDefaultOptions } from './storeHelper'; import { dataqueryService } from '@/_services'; -import debounce from 'lodash/debounce'; +// import debounce from 'lodash/debounce'; import { useAppDataStore } from '@/_stores/appDataStore'; import { useQueryPanelStore } from '@/_stores/queryPanelStore'; import { useAppVersionStore } from '@/_stores/appVersionStore'; @@ -9,6 +9,7 @@ import { runQueries } from '@/_helpers/appUtils'; import { v4 as uuidv4 } from 'uuid'; import { toast } from 'react-hot-toast'; import { isEmpty, throttle } from 'lodash'; +import { useEditorStore } from './editorStore'; const initialState = { dataQueries: [], @@ -30,15 +31,27 @@ export const useDataQueriesStore = create( ...initialState, actions: { // TODO: Remove editor state while changing currentState - fetchDataQueries: async (appId, selectFirstQuery = false, runQueriesOnAppLoad = false, editorRef) => { + fetchDataQueries: async (appVersionId, selectFirstQuery = false, runQueriesOnAppLoad = false, ref) => { set({ loadingDataQueries: true }); - const data = await dataqueryService.getAll(appId); + const data = await dataqueryService.getAll(appVersionId); set((state) => ({ dataQueries: sortByAttribute(data.data_queries, state.sortBy, state.sortOrder), loadingDataQueries: false, })); - // Runs query on loading application - if (runQueriesOnAppLoad) runQueries(data.data_queries, editorRef); + + if (data.data_queries.length !== 0) { + const queryConfirmationList = []; + data.data_queries.forEach(({ id, name, options }) => { + if (options && options?.requestConfirmation && options?.runOnPageLoad) { + queryConfirmationList.push({ queryId: id, queryName: name }); + } + }); + + if (queryConfirmationList.length !== 0) { + useEditorStore.getState().actions.updateQueryConfirmationList(queryConfirmationList); + } + } + // Compute query state to be added in the current state const { actions, selectedQuery } = useQueryPanelStore.getState(); if (selectFirstQuery) { @@ -47,6 +60,9 @@ export const useDataQueriesStore = create( const query = data.data_queries.find((query) => query.id === selectedQuery?.id); actions.setSelectedQuery(query?.id); } + + // Runs query on loading application + if (runQueriesOnAppLoad) runQueries(data.data_queries, ref); }, setDataQueries: (dataQueries) => set({ dataQueries }), deleteDataQueries: (queryId) => { @@ -232,7 +248,7 @@ export const useDataQueriesStore = create( newName = queryToClone.name + '_copy' + count.toString(); } queryToClone.name = newName; - delete queryToClone.id; + useAppDataStore.getState().actions.setIsSaving(true); dataqueryService .create( @@ -250,6 +266,26 @@ export const useDataQueriesStore = create( dataQueries: [{ ...data, data_source_id: queryToClone.data_source_id }, ...state.dataQueries], })); actions.setSelectedQuery(data.id, { ...data, data_source_id: queryToClone.data_source_id }); + + const dataQueryEvents = useAppDataStore + .getState() + .events?.filter((event) => event.target === 'data_query' && event.sourceId === queryToClone.id); + + if (dataQueryEvents?.length === 0) return; + + return Promise.all( + dataQueryEvents.map((event) => { + const newEvent = { + event: { + ...event?.event, + }, + eventType: event?.target, + attachedTo: data.id, + index: event?.index, + }; + useAppDataStore.getState().actions?.createAppVersionEventHandlers(newEvent); + }) + ); }) .catch((error) => { console.error('error', error); diff --git a/frontend/src/_stores/editorStore.js b/frontend/src/_stores/editorStore.js index ba1a1b5269..6140f7d790 100644 --- a/frontend/src/_stores/editorStore.js +++ b/frontend/src/_stores/editorStore.js @@ -1,5 +1,5 @@ import { create, zustandDevTools } from './utils'; - +import { v4 as uuid } from 'uuid'; const STORE_NAME = 'Editor'; const ACTIONS = { @@ -19,53 +19,79 @@ const initialState = { selectionInProgress: false, selectedComponents: [], isEditorActive: false, + currentSidebarTab: 2, + selectedComponent: null, + scrollOptions: { + container: null, + throttleTime: 0, + threshold: 0, + }, + canUndo: false, + canRedo: false, + currentVersion: {}, + noOfVersionsSupported: 100, + appDefinition: {}, + // isSaving: false, + isUpdatingEditorStateInProcess: false, + saveError: false, + isLoading: true, + defaultComponentStateComputed: false, + showLeftSidebar: true, + queryConfirmationList: [], + currentPageId: null, + currentSessionId: uuid(), }; export const useEditorStore = create( - zustandDevTools( - (set, get) => ({ - ...initialState, - actions: { - setShowComments: (showComments) => - set({ showComments }, false, { - type: ACTIONS.SET_HOVERED_COMPONENT, - showComments, - }), - toggleComments: () => - set({ showComments: !get().showComments }, false, { - type: ACTIONS.TOGGLE_COMMENTS, - }), - toggleCurrentLayout: (currentLayout) => - set({ currentLayout }, false, { - type: ACTIONS.TOGGLE_CURRENT_LAYOUT, - currentLayout, - }), - setIsEditorActive: (isEditorActive) => set(() => ({ isEditorActive })), - setHoveredComponent: (hoveredComponent) => - set({ hoveredComponent }, false, { - type: ACTIONS.SET_HOVERED_COMPONENT, - hoveredComponent, - }), - setSelectionInProgress: (isSelectionInProgress) => { - set( - { - isSelectionInProgress, - }, - false, - { type: ACTIONS.SET_SELECTION_IN_PROGRESS } - ); - }, - setSelectedComponents: (selectedComponents) => { - set( - { - selectedComponents, - }, - false, - { type: ACTIONS.SET_SELECTED_COMPONENTS } - ); - }, + // Dev tools for this store are disabled comments since its freezing chrome tab + (set, get) => ({ + ...initialState, + actions: { + setShowComments: (showComments) => + set({ showComments }, false, { + type: ACTIONS.SET_HOVERED_COMPONENT, + showComments, + }), + toggleComments: () => + set({ showComments: !get().showComments }, false, { + type: ACTIONS.TOGGLE_COMMENTS, + }), + toggleCurrentLayout: (currentLayout) => + set({ currentLayout }, false, { + type: ACTIONS.TOGGLE_CURRENT_LAYOUT, + currentLayout, + }), + setIsEditorActive: (isEditorActive) => set(() => ({ isEditorActive })), + updateEditorState: (state) => set((prev) => ({ ...prev, ...state })), + updateQueryConfirmationList: (queryConfirmationList) => set({ queryConfirmationList }), + setHoveredComponent: (hoveredComponent) => + set({ hoveredComponent }, false, { + type: ACTIONS.SET_HOVERED_COMPONENT, + hoveredComponent, + }), + setSelectionInProgress: (isSelectionInProgress) => { + set( + { + isSelectionInProgress, + }, + false, + { type: ACTIONS.SET_SELECTION_IN_PROGRESS } + ); }, - }), - { name: STORE_NAME } - ) + setSelectedComponents: (selectedComponents, isMulti = false) => { + const newSelectedComponents = isMulti + ? [...get().selectedComponents, ...selectedComponents] + : selectedComponents; + + set({ + selectedComponents: newSelectedComponents, + }); + }, + setCurrentPageId: (currentPageId) => set({ currentPageId }), + }, + }), + { name: STORE_NAME } ); + +export const useEditorActions = () => useEditorStore((state) => state.actions); +export const useEditorState = () => useEditorStore((state) => state); diff --git a/frontend/src/_stores/utils.js b/frontend/src/_stores/utils.js index 8b883b726d..db72f51d60 100644 --- a/frontend/src/_stores/utils.js +++ b/frontend/src/_stores/utils.js @@ -1,5 +1,9 @@ import { create as _create } from 'zustand'; import { devtools } from 'zustand/middleware'; +// eslint-disable-next-line import/no-unresolved +import { diff } from 'deep-object-diff'; +import { componentTypes } from '@/Editor/WidgetManager/components'; +import _ from 'lodash'; export const zustandDevTools = (fn, options = {}) => devtools(fn, { ...options, enabled: process.env.NODE_ENV === 'production' ? false : true }); @@ -21,3 +25,304 @@ export const resetAllStores = () => { resetter(); } }; + +const defaultComponent = { + name: '', + properties: {}, + styles: {}, + validation: {}, + type: '', + others: { + showOnDesktop: { value: '{{true}}' }, + showOnMobile: { value: '{{false}}' }, + }, +}; + +const updateType = Object.freeze({ + pageDefinitionChanged: 'pages', + containerChanges: 'components/layout', + componentAdded: 'components', + componentDefinitionChanged: 'components', + componentDeleted: 'components', +}); + +export const computeAppDiff = (appDiff, currentPageId, opts, currentLayout) => { + const { updateDiff, type, operation, error } = updateFor(appDiff, currentPageId, opts, currentLayout); + + return { updateDiff, type, operation, error }; +}; + +// for table column diffs, we need to compute the diff for each column separately and send the the entire column data +function generatePath(obj, targetKey, currentPath = '') { + for (const key in obj) { + const newPath = currentPath ? currentPath + '.' + key : key; + + if (key === targetKey) { + return newPath; + } + + if (typeof obj[key] === 'object' && obj[key] !== null) { + const result = generatePath(obj[key], targetKey, newPath); + if (result) { + return result; + } + } + } + return null; +} + +function getValueFromJson(json, path) { + if (!path || typeof path !== 'string') return null; + + let value = json; + path.split('.').forEach((key) => { + value = value[key]; + }); + return value; +} + +function updateValueInJson(json, path, value) { + let obj = json; + const keys = path?.split('.'); + + if (!keys) { + return null; + } + + const lastKey = keys.pop(); + keys.forEach((key) => { + obj = obj[key]; + }); + obj[lastKey] = value; + return json; +} + +export function isParamFromTableColumn(appDiff, definition) { + const path = generatePath(appDiff, 'columns') || generatePath(appDiff, 'actions'); + if (!path) { + return false; + } + + const value2 = getValueFromJson(definition, path); + + return value2 !== undefined; +} + +export const computeComponentPropertyDiff = (appDiff, definition, opts) => { + if (!opts?.isParamFromTableColumn) { + return appDiff; + } + const columnsPath = generatePath(appDiff, 'columns'); + const actionsPath = generatePath(appDiff, 'actions'); + const deletionHistoryPath = generatePath(appDiff, 'columnDeletionHistory'); + + let _diff = _.cloneDeep(appDiff); + + if (columnsPath) { + const columnsValue = getValueFromJson(definition, columnsPath); + _diff = updateValueInJson(_diff, columnsPath, columnsValue); + } + + if (actionsPath) { + const actionsValue = getValueFromJson(definition, actionsPath); + _diff = updateValueInJson(_diff, actionsPath, actionsValue); + } + + if (deletionHistoryPath) { + const deletionHistoryValue = getValueFromJson(definition, deletionHistoryPath); + _diff = updateValueInJson(_diff, deletionHistoryPath, deletionHistoryValue); + } + + return _diff; +}; + +const updateFor = (appDiff, currentPageId, opts, currentLayout) => { + const updateTypeMappings = [ + { + updateTypes: ['componentAdded', 'componentDefinitionChanged', 'componentDeleted', 'containerChanges'], + processingFunction: computeComponentDiff, + }, + { + updateTypes: ['pageDefinitionChanged', 'pageSortingChanged', 'deletePageRequest', 'addNewPage'], + processingFunction: computePageUpdate, + }, + { + updateTypes: ['homePageChanged'], + processingFunction: () => ({ + updateDiff: appDiff, + type: null, + operation: 'update', + }), + }, + { + updateTypes: ['globalSettings', 'generalAppDefinitionChanged'], + processingFunction: () => ({ + updateDiff: appDiff, + type: 'global_settings', + operation: 'update', + }), + }, + ]; + + const options = _.keys(opts); + + for (const { updateTypes, processingFunction } of updateTypeMappings) { + const optionsTypes = _.intersection(options, updateTypes); + + if (optionsTypes.length > 0) { + try { + return processingFunction(appDiff, currentPageId, optionsTypes, currentLayout); + } catch (error) { + return { error, updateDiff: {}, type: null, operation: null }; + } + } + } + + return null; +}; + +const computePageUpdate = (appDiff, currentPageId, opts) => { + let type; + let updateDiff; + let operation = 'update'; + + if (opts.includes('deletePageRequest')) { + const deletePageId = _.keys(appDiff?.pages).map((pageId) => { + if (appDiff?.pages[pageId]?.pageId === undefined) { + return pageId; + } + })[0]; + + updateDiff = { + pageId: deletePageId, + }; + + type = updateType.pageDefinitionChanged; + operation = 'delete'; + } else if (opts.includes('pageSortingChanged')) { + updateDiff = appDiff?.pages; + + type = updateType.pageDefinitionChanged; + } else if (opts.includes('pageDefinitionChanged')) { + updateDiff = appDiff?.pages[currentPageId]; + + type = updateType.pageDefinitionChanged; + + if (opts.includes('addNewPage')) { + operation = 'create'; + } + } + + return { updateDiff, type, operation }; +}; + +const computeComponentDiff = (appDiff, currentPageId, opts, currentLayout) => { + let type; + let updateDiff; + let operation = 'update'; + + if (opts.includes('componentDeleted')) { + const currentPageComponents = appDiff?.pages[currentPageId]?.components; + + updateDiff = _.keys(currentPageComponents); + + type = updateType.componentDeleted; + + operation = 'delete'; + } else if (opts.includes('componentAdded')) { + const currentPageComponents = appDiff?.pages[currentPageId]?.components; + + updateDiff = _.toPairs(currentPageComponents ?? []).reduce((result, [id, component]) => { + if (_.keys(component).length === 1 && component.withDefaultChildren !== undefined) { + return result; + } + + const componentMeta = componentTypes.find((comp) => comp.component === component.component.component); + + if (!componentMeta) { + return result; + } + + const metaDiff = diff(componentMeta, component.component); + + result[id] = _.defaultsDeep(metaDiff, defaultComponent); + + if (metaDiff.definition && !_.isEmpty(metaDiff.definition)) { + const metaAttributes = _.keys(metaDiff.definition); + + metaAttributes.forEach((attribute) => { + const doesActionsExist = + metaDiff.definition[attribute]?.actions && !_.isEmpty(metaDiff.definition[attribute]?.actions?.value); + const doesColumnsExist = + metaDiff.definition[attribute]?.columns && !_.isEmpty(metaDiff.definition[attribute]?.columns?.value); + + if (doesActionsExist || doesColumnsExist) { + const actions = _.toArray(metaDiff.definition[attribute]?.actions?.value) || []; + const columns = _.toArray(metaDiff.definition[attribute]?.columns?.value) || []; + + metaDiff.definition = { + ...metaDiff.definition, + [attribute]: { + ...metaDiff.definition[attribute], + actions: { + value: actions, + }, + columns: { + value: columns, + }, + }, + }; + } + result[id][attribute] = metaDiff.definition[attribute]; + }); + } + + const currentDisplayPreference = currentLayout; + + if (currentDisplayPreference === 'mobile') { + result[id].others.showOnMobile = { value: '{{true}}' }; + result[id].others.showOnDesktop = { value: '{{false}}' }; + } + + if (result[id]?.definition) { + delete result[id].definition; + } + + result[id].type = componentMeta.component; + result[id].parent = component.component.parent ?? null; + result[id].layouts = appDiff.pages[currentPageId].components[id].layouts; + + operation = 'create'; + + return result; + }, {}); + + type = updateType.componentDefinitionChanged; + } else if ( + (opts.includes('containerChanges') || opts.includes('componentDefinitionChanged')) && + !opts.includes('componentAdded') + ) { + const currentPageComponents = appDiff?.pages[currentPageId]?.components; + + updateDiff = toRemoveExposedvariablesFromComponentDiff(currentPageComponents); + + type = opts.includes('containerChanges') ? updateType.containerChanges : updateType.componentDefinitionChanged; + } + + return { updateDiff, type, operation }; +}; + +function toRemoveExposedvariablesFromComponentDiff(object) { + const copy = _.cloneDeep(object); + const componentIds = _.keys(copy); + + componentIds.forEach((componentId) => { + const { component } = copy[componentId]; + + if (component?.exposedVariables) { + delete component.exposedVariables; + } + }); + + return copy; +} diff --git a/frontend/src/_ui/Layout/index.jsx b/frontend/src/_ui/Layout/index.jsx index 4f1de55694..da59085ebe 100644 --- a/frontend/src/_ui/Layout/index.jsx +++ b/frontend/src/_ui/Layout/index.jsx @@ -1,4 +1,4 @@ -import React, { useCallback, useState } from 'react'; +import React from 'react'; import { Link } from 'react-router-dom'; import useRouter from '@/_hooks/use-router'; import { ToolTip } from '@/_components/ToolTip'; diff --git a/server/.version b/server/.version index e9763f6bfe..ad2261920c 100644 --- a/server/.version +++ b/server/.version @@ -1 +1 @@ -2.23.0 +2.24.0 diff --git a/server/data-migrations/1688977149516-ListviewDefaultMode.ts b/server/data-migrations/1688977149516-ListviewDefaultMode.ts index d8e1d503cd..672774fcf3 100644 --- a/server/data-migrations/1688977149516-ListviewDefaultMode.ts +++ b/server/data-migrations/1688977149516-ListviewDefaultMode.ts @@ -6,39 +6,28 @@ export class ListviewDefaultMode1688977149516 implements MigrationInterface { const entityManager = queryRunner.manager; const appVersions = await entityManager.find(AppVersion); for (const version of appVersions) { - const definition = version['definition']; + const definition = JSON.parse(JSON.stringify(version?.definition)); if (definition) { const pages = definition['pages']; - if (pages) { + if (Object.keys(pages).length > 0) { for (const pageId of Object.keys(pages)) { const components = definition['pages'][pageId]['components']; - if (components) { + if (Object.keys(components).length > 0) { for (const componentId of Object.keys(components)) { const component = components[componentId]; - if (component?.component?.component === 'Listview') { - component['component']['definition']['properties']['mode'] = { - value: 'list', - }; - - components[componentId] = { - ...component, - component: { - ...component.component, - definition: { - ...component.component.definition, - }, - }, - }; + if ( + component?.component?.component === 'Listview' && + component.component?.definition?.properties?.mode + ) { + component.component.definition.properties.mode['value'] = 'list'; } } } - - definition['components'] = components; - version.definition = definition; } } + version.definition = definition; await entityManager.update(AppVersion, { id: version.id }, { definition }); } } diff --git a/server/data-migrations/1692973078520-CellSizeRegularCondensed.ts b/server/data-migrations/1692973078520-CellSizeRegularCondensed.ts index 0833ebb753..76709c348b 100644 --- a/server/data-migrations/1692973078520-CellSizeRegularCondensed.ts +++ b/server/data-migrations/1692973078520-CellSizeRegularCondensed.ts @@ -4,25 +4,23 @@ import { AppVersion } from '../src/entities/app_version.entity'; export class CellSizeRegularCondensed1692973078520 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise { const entityManager = queryRunner.manager; - const queryBuilder = queryRunner.connection.createQueryBuilder(); + const appVersionRepository = entityManager.getRepository(AppVersion); const appVersions = await appVersionRepository.find(); for (const version of appVersions) { - const definition = version?.['definition']; + const definition = JSON.parse(JSON.stringify(version?.definition)); if (definition) { const pages = definition?.['pages']; - if (pages) { + if (Object.keys(pages).length > 0) { for (const pageId of Object.keys(pages)) { - const components = pages?.[pageId]?.['components']; - if (components) { + const components = pages[pageId]?.['components']; + + if (Object.keys(components).length > 0) { for (const componentId of Object.keys(components)) { const component = components[componentId]; - if (component?.component?.component === 'Table') { - component.component.definition.styles.cellSize = { - value: 'regular', - }; + if (component?.component?.component === 'Table' && component.component?.definition?.styles?.cellSize) { component.component.styles.cellSize = { ...component.component.styles.cellSize, options: [ @@ -30,27 +28,19 @@ export class CellSizeRegularCondensed1692973078520 implements MigrationInterface { name: 'Regular', value: 'regular' }, ], }; - components[componentId] = { - ...component, - component: { - ...component.component, - definition: { - ...component.component.definition, - }, - }, + + component.component.definition.styles.cellSize = { + value: 'regular', }; } } - pages[pageId]['components'] = components; } } } - definition['pages'] = pages; - version.definition = definition; - await queryBuilder.update(AppVersion).set({ definition }).where('id = :id', { id: version.id }).execute(); + await entityManager.update(AppVersion, { id: version.id }, { definition }); } } } diff --git a/server/data-migrations/1692974311591-TableRowCellStyle.ts b/server/data-migrations/1692974311591-TableRowCellStyle.ts index 114152b059..ca79b042e1 100644 --- a/server/data-migrations/1692974311591-TableRowCellStyle.ts +++ b/server/data-migrations/1692974311591-TableRowCellStyle.ts @@ -4,20 +4,20 @@ import { AppVersion } from '../src/entities/app_version.entity'; export class TableRowCellStyle1692974311591 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise { const entityManager = queryRunner.manager; - const queryBuilder = queryRunner.connection.createQueryBuilder(); + const appVersionRepository = entityManager.getRepository(AppVersion); const appVersions = await appVersionRepository.find(); for (const version of appVersions) { - const definition = version['definition']; + const definition = JSON.parse(JSON.stringify(version?.definition)); if (definition) { const pages = definition['pages']; - if (pages) { + if (Object.keys(pages).length > 0) { for (const pageId of Object.keys(pages)) { const components = pages[pageId]['components']; - if (components) { + if (Object.keys(components).length > 0) { for (const componentId of Object.keys(components)) { const component = components[componentId]; if (component?.component?.component === 'Table') { @@ -32,27 +32,15 @@ export class TableRowCellStyle1692974311591 implements MigrationInterface { { name: 'Striped', value: 'table-striped' }, ], }; - components[componentId] = { - ...component, - component: { - ...component.component, - definition: { - ...component.component.definition, - }, - }, - }; } } } - pages[pageId]['components'] = components; } } - definition['pages'] = pages; - version.definition = definition; - await queryBuilder.update(AppVersion).set({ definition }).where('id = :id', { id: version.id }).execute(); + await entityManager.update(AppVersion, { id: version.id }, { definition }); } } } diff --git a/server/data-migrations/1697473340856-MigrateAppsDefinitionSchemaTransition.ts b/server/data-migrations/1697473340856-MigrateAppsDefinitionSchemaTransition.ts new file mode 100644 index 0000000000..fef4a4f74e --- /dev/null +++ b/server/data-migrations/1697473340856-MigrateAppsDefinitionSchemaTransition.ts @@ -0,0 +1,361 @@ +import { In, MigrationInterface, QueryRunner, EntityManager } from 'typeorm'; +import { AppVersion } from '../src/entities/app_version.entity'; +import { Component } from 'src/entities/component.entity'; +import { Page } from 'src/entities/page.entity'; +import { Layout } from 'src/entities/layout.entity'; +import { EventHandler, Target } from 'src/entities/event_handler.entity'; +import { DataQuery } from 'src/entities/data_query.entity'; +import { MigrationProgress, processDataInBatches } from 'src/helpers/utils.helper'; +import { v4 as uuid } from 'uuid'; + +interface AppResourceMappings { + pagesMapping: Record; + componentsMapping: Record; +} + +export class MigrateAppsDefinitionSchemaTransition1697473340856 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + const entityManager = queryRunner.manager; + const appVersionRepository = entityManager.getRepository(AppVersion); + const appVersions = await appVersionRepository.find(); + const totalVersions = appVersions.length; + + const migrationProgress = new MigrationProgress( + 'MigrateAppsDefinitionSchemaTransition1697473340856', + totalVersions + ); + + const batchSize = 100; // Number of apps to migrate at a time + + await processDataInBatches( + entityManager, + async (entityManager: EntityManager, skip: number, take: number) => { + return entityManager.find(AppVersion, { + where: { id: In(appVersions.map((appVersion) => appVersion.id)) }, + take, + skip, + }); + }, + async (entityManager: EntityManager, versions: AppVersion[]) => { + await this.processVersions(entityManager, versions, migrationProgress); + }, + batchSize + ); + } + + private async processVersions( + entityManager: EntityManager, + versions: AppVersion[], + migrationProgress: MigrationProgress + ) { + for (const version of versions) { + const definition = version['definition']; + + if (!definition) return; + + const dataQueriesRepository = entityManager.getRepository(DataQuery); + const dataQueries = await dataQueriesRepository.find({ + where: { appVersionId: version.id }, + }); + + let updateHomepageId = null; + + const appResourceMappings: AppResourceMappings = { + pagesMapping: {}, + componentsMapping: {}, + }; + if (definition?.pages) { + for (const pageId of Object.keys(definition?.pages)) { + const page = definition.pages[pageId]; + const pagePositionInTheList = Object.keys(definition?.pages).indexOf(pageId); + const pageEvents = page.events || []; + const pageComponents = page.components; + + const isHomepage = (definition['homePageId'] as any) === pageId; + + const componentEvents = []; + const componentLayouts = []; + const transformedComponents = this.transformComponentData( + pageComponents, + componentEvents, + appResourceMappings.componentsMapping + ); + + const newPage = entityManager.create(Page, { + name: page.name, + handle: page.handle, + appVersionId: version.id, + disabled: page.disabled || false, + hidden: page.hidden || false, + index: pagePositionInTheList, + }); + + const pageCreated = await entityManager.save(newPage); + + appResourceMappings.pagesMapping[pageId] = pageCreated.id; + + transformedComponents.forEach((component) => { + component.page = pageCreated; + }); + + const savedComponents = await entityManager.save(Component, transformedComponents); + + for (const componentId in pageComponents) { + const componentLayout = pageComponents[componentId]['layouts']; + + if (componentLayout && appResourceMappings.componentsMapping[componentId]) { + for (const type in componentLayout) { + const layout = componentLayout[type]; + const newLayout = new Layout(); + newLayout.type = type; + newLayout.top = layout.top; + newLayout.left = layout.left; + newLayout.width = layout.width; + newLayout.height = layout.height; + newLayout.componentId = appResourceMappings.componentsMapping[componentId]; + + componentLayouts.push(newLayout); + } + } + } + + await entityManager.save(Layout, componentLayouts); + + if (pageEvents.length > 0) { + pageEvents.forEach(async (event, index) => { + const newEvent = { + name: event.eventId, + sourceId: pageCreated.id, + target: Target.page, + event: event, + index: pageEvents.index || index, + appVersionId: version.id, + }; + + await entityManager.save(EventHandler, newEvent); + }); + } + + componentEvents.forEach((eventObj) => { + if (eventObj.event?.length === 0) return; + + eventObj.event.forEach(async (event, index) => { + const newEvent = { + name: event.eventId, + sourceId: appResourceMappings.componentsMapping[eventObj.componentId], + target: Target.component, + event: event, + index: eventObj.index || index, + appVersionId: version.id, + }; + + await entityManager.save(EventHandler, newEvent); + }); + }); + + savedComponents.forEach(async (component) => { + if (component.type === 'Table') { + const tableActions = component.properties?.actions?.value || []; + const tableColumns = component.properties?.columns?.value || []; + const tableActionAndColumnEvents = []; + + tableActions.forEach((action) => { + const actionEvents = action.events || []; + + actionEvents.forEach((event, index) => { + tableActionAndColumnEvents.push({ + name: event.eventId, + sourceId: component.id, + target: Target.tableAction, + event: { ...event, ref: action.name }, + index: event.index ?? index, + appVersionId: version.id, + }); + }); + }); + + tableColumns.forEach((column) => { + if (column?.columnType !== 'toggle') return; + const columnEvents = column.events || []; + + columnEvents.forEach((event, index) => { + tableActionAndColumnEvents.push({ + name: event.eventId, + sourceId: component.id, + target: Target.tableColumn, + event: { ...event, ref: column.name }, + index: event.index ?? index, + appVersionId: version.id, + }); + }); + }); + + await entityManager.save(EventHandler, tableActionAndColumnEvents); + } + }); + + if (isHomepage) { + updateHomepageId = pageCreated.id; + } + } + } + + for (const dataQuery of dataQueries) { + const queryEvents = dataQuery?.options?.events || []; + + if (queryEvents.length > 0) { + queryEvents.forEach(async (event, index) => { + const newEvent = { + name: event.eventId, + sourceId: dataQuery.id, + target: Target.dataQuery, + event: event, + index: queryEvents.index || index, + appVersionId: version.id, + }; + + await entityManager.save(EventHandler, newEvent); + }); + } + } + + await entityManager.update( + AppVersion, + { id: version.id }, + { + homePageId: updateHomepageId, + showViewerNavigation: definition?.showViewerNavigation || true, + globalSettings: definition.globalSettings, + } + ); + + await this.updateEventActionsForNewVersionWithNewMappingIds( + entityManager, + version.id, + appResourceMappings.componentsMapping, + appResourceMappings.pagesMapping + ); + + migrationProgress.show(); + } + } + + async updateEventActionsForNewVersionWithNewMappingIds( + manager: EntityManager, + versionId: string, + oldComponentToNewComponentMapping: Record, + oldPageToNewPageMapping: Record + ) { + const allEvents = await manager.find(EventHandler, { + where: { appVersionId: versionId }, + }); + + for (const event of allEvents) { + const eventDefinition = event.event; + + if (eventDefinition?.actionId === 'switch-page') { + eventDefinition.pageId = oldPageToNewPageMapping[eventDefinition.pageId]; + } + + if (eventDefinition?.actionId === 'control-component') { + eventDefinition.componentId = oldComponentToNewComponentMapping[eventDefinition.componentId]; + } + + if (eventDefinition?.actionId == 'show-modal' || eventDefinition?.actionId === 'close-modal') { + eventDefinition.modal = oldComponentToNewComponentMapping[eventDefinition.modal]; + } + + event.event = eventDefinition; + + await manager.save(event); + } + } + + private transformComponentData( + data: object, + componentEvents: any[], + componentsMapping: Record + ): Component[] { + const transformedComponents: Component[] = []; + + const allComponents = Object.keys(data).map((key) => { + return { + id: key, + ...data[key], + }; + }); + + for (const componentId in data) { + const component = data[componentId]; + const componentData = component['component']; + + let skipComponent = false; + const transformedComponent: Component = new Component(); + + let parentId = component.parent ? component.parent : null; + + const isParentTabOrCalendar = this.isChildOfTabsOrCalendar(component, allComponents, parentId); + + if (isParentTabOrCalendar) { + const childTabId = component.parent.split('-')[component.parent.split('-').length - 1]; + const _parentId = component?.parent?.split('-').slice(0, -1).join('-'); + const mappedParentId = componentsMapping[_parentId]; + + parentId = `${mappedParentId}-${childTabId}`; + } else { + if (component.parent && !componentsMapping[parentId]) { + skipComponent = true; + } + parentId = componentsMapping[parentId]; + } + + if (!skipComponent) { + transformedComponent.id = uuid(); + transformedComponent.name = componentData.name; + transformedComponent.type = componentData.component; + transformedComponent.properties = componentData.definition.properties || {}; + transformedComponent.styles = componentData.definition.styles || {}; + transformedComponent.validation = componentData.definition.validation || {}; + transformedComponent.general = componentData.definition.general || {}; + transformedComponent.generalStyles = componentData.definition.generalStyles || {}; + transformedComponent.displayPreferences = componentData.definition.others || {}; + transformedComponent.parent = component.parent ? parentId : null; + + transformedComponents.push(transformedComponent); + + componentEvents.push({ + componentId: componentId, + event: componentData.definition.events, + }); + componentsMapping[componentId] = transformedComponent.id; + } + } + + return transformedComponents; + } + + isChildOfTabsOrCalendar = (component, allComponents = [], componentParentId = undefined) => { + if (componentParentId) { + const parentId = component?.parent?.split('-').slice(0, -1).join('-'); + + const parentComponent = allComponents.find((comp) => comp.id === parentId); + + if (parentComponent) { + return parentComponent.component.component === 'Tabs' || parentComponent.component.component === 'Calendar'; + } + } + + return false; + }; + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query('DELETE FROM page'); + await queryRunner.query('DELETE FROM component'); + await queryRunner.query('DELETE FROM layout'); + await queryRunner.query('DELETE FROM event_handler'); + + await queryRunner.query('ALTER TABLE app_version DROP COLUMN IF EXISTS homePageId'); + await queryRunner.query('ALTER TABLE app_version DROP COLUMN IF EXISTS globalSettings'); + await queryRunner.query('ALTER TABLE app_version DROP COLUMN IF EXISTS showViewerNavigation'); + } +} diff --git a/server/migrations/1691004576222-UpdateAppVersionEntity.ts b/server/migrations/1691004576222-UpdateAppVersionEntity.ts new file mode 100644 index 0000000000..0ef8eb2d6f --- /dev/null +++ b/server/migrations/1691004576222-UpdateAppVersionEntity.ts @@ -0,0 +1,41 @@ +import { MigrationInterface, QueryRunner, TableColumn } from 'typeorm'; + +export class UpdateAppVersionEntity1691006886222 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + // Add the new columns to the app_versions table + await queryRunner.addColumn( + 'app_versions', + new TableColumn({ + name: 'global_settings', + type: 'json', + isNullable: true, + }) + ); + + await queryRunner.addColumn( + 'app_versions', + new TableColumn({ + name: 'show_viewer_navigation', + type: 'boolean', + default: true, + isNullable: false, + }) + ); + + await queryRunner.addColumn( + 'app_versions', + new TableColumn({ + name: 'home_page_id', + type: 'uuid', + isNullable: true, + }) + ); + } + + public async down(queryRunner: QueryRunner): Promise { + // Remove the new columns from the app_versions table (if necessary) + await queryRunner.dropColumn('app_versions', 'global_settings'); + await queryRunner.dropColumn('app_versions', 'show_viewer_navigation'); + await queryRunner.dropColumn('app_versions', 'home_page_id'); + } +} diff --git a/server/migrations/1691004576333-CreatePageTable.ts b/server/migrations/1691004576333-CreatePageTable.ts new file mode 100644 index 0000000000..09831a9929 --- /dev/null +++ b/server/migrations/1691004576333-CreatePageTable.ts @@ -0,0 +1,76 @@ +import { MigrationInterface, QueryRunner, Table, TableForeignKey } from 'typeorm'; + +export class CreatePageTable1691004576333 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.createTable( + new Table({ + name: 'pages', + columns: [ + { + name: 'id', + type: 'uuid', + isPrimary: true, + default: 'gen_random_uuid()', + }, + { + name: 'name', + type: 'varchar', + isNullable: false, + }, + { + name: 'index', + type: 'int', + isNullable: false, + }, + { + name: 'page_handle', + type: 'varchar', + isNullable: false, + }, + { + name: 'disabled', + type: 'boolean', + isNullable: true, + }, + { + name: 'hidden', + type: 'boolean', + isNullable: true, + }, + { + name: 'app_version_id', + type: 'uuid', + isNullable: false, + }, + { + name: 'created_at', + type: 'timestamp', + isNullable: true, + default: 'now()', + }, + { + name: 'updated_at', + type: 'timestamp', + isNullable: true, + default: 'now()', + }, + ], + }) + ); + + // Add foreign key to relate Page with AppVersion + await queryRunner.createForeignKey( + 'pages', + new TableForeignKey({ + columnNames: ['app_version_id'], + referencedColumnNames: ['id'], + referencedTableName: 'app_versions', + onDelete: 'CASCADE', + }) + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.dropTable('pages'); + } +} diff --git a/server/migrations/1691004706564-CreateEventHandlerTable.ts b/server/migrations/1691004706564-CreateEventHandlerTable.ts new file mode 100644 index 0000000000..027d91840d --- /dev/null +++ b/server/migrations/1691004706564-CreateEventHandlerTable.ts @@ -0,0 +1,78 @@ +import { MigrationInterface, QueryRunner, Table, TableForeignKey } from 'typeorm'; + +export class CreateEventHandlerTable1691004706564 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.createTable( + new Table({ + name: 'event_handlers', + columns: [ + { + name: 'id', + type: 'uuid', + isPrimary: true, + default: 'gen_random_uuid()', + }, + { + name: 'name', + type: 'varchar', + isNullable: false, + }, + { + name: 'index', + type: 'int', + isNullable: false, + }, + { + name: 'event', + type: 'jsonb', + isNullable: false, + }, + { + name: 'app_version_id', + type: 'uuid', + isNullable: false, + }, + { + name: 'source_id', + type: 'varchar', + isNullable: false, + }, + { + name: 'target', + type: 'enum', + enum: ['page', 'component', 'data_query', 'table_column', 'table_action'], + default: "'page'", + isNullable: false, + }, + { + name: 'created_at', + type: 'timestamp', + isNullable: true, + default: 'now()', + }, + { + name: 'updated_at', + type: 'timestamp', + isNullable: true, + default: 'now()', + }, + ], + }) + ); + + // Add foreign key to relate EventHandler with AppVersion + await queryRunner.createForeignKey( + 'event_handlers', + new TableForeignKey({ + columnNames: ['app_version_id'], + referencedColumnNames: ['id'], + referencedTableName: 'app_versions', + onDelete: 'CASCADE', + }) + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.dropTable('event_handlers'); + } +} diff --git a/server/migrations/1691006952074-CreateComponentTable.ts b/server/migrations/1691006952074-CreateComponentTable.ts new file mode 100644 index 0000000000..5bcfd18b88 --- /dev/null +++ b/server/migrations/1691006952074-CreateComponentTable.ts @@ -0,0 +1,108 @@ +import { MigrationInterface, QueryRunner, Table, TableForeignKey, TableIndex } from 'typeorm'; + +export class CreateComponentTable1691006952074 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.createTable( + new Table({ + name: 'components', + columns: [ + { + name: 'id', + type: 'uuid', + isPrimary: true, + default: 'gen_random_uuid()', + }, + { + name: 'name', + type: 'varchar', + isNullable: false, + }, + { + name: 'type', + type: 'varchar', + isNullable: false, + }, + { + name: 'page_id', + type: 'uuid', + isNullable: false, + }, + { + name: 'parent', + type: 'varchar', + isNullable: true, + }, + { + name: 'properties', + type: 'json', + isNullable: true, + }, + { + name: 'general_properties', + type: 'json', + isNullable: true, + }, + { + name: 'styles', + type: 'json', + isNullable: true, + }, + { + name: 'general_styles', + type: 'json', + isNullable: true, + }, + { + name: 'display_preferences', + type: 'json', + isNullable: true, + }, + { + name: 'validation', + type: 'json', + isNullable: true, + }, + { + name: 'created_at', + type: 'timestamp', + isNullable: true, + default: 'now()', + }, + { + name: 'updated_at', + type: 'timestamp', + isNullable: true, + default: 'now()', + }, + ], + }) + ); + + await queryRunner.createForeignKey( + 'components', + new TableForeignKey({ + columnNames: ['page_id'], + referencedColumnNames: ['id'], + referencedTableName: 'pages', + onDelete: 'CASCADE', + }) + ); + + await queryRunner.createIndex('components', new TableIndex({ columnNames: ['name'] })); + await queryRunner.createIndex('components', new TableIndex({ columnNames: ['type'] })); + await queryRunner.createIndex('components', new TableIndex({ columnNames: ['page_id'] })); + } + + public async down(queryRunner: QueryRunner): Promise { + // Drop indexes + await queryRunner.dropIndex('components', 'IDX_COMPONENT_NAME'); + await queryRunner.dropIndex('components', 'IDX_COMPONENT_TYPE'); + await queryRunner.dropIndex('components', 'IDX_COMPONENT_PAGE'); + + // Drop foreign key + await queryRunner.dropForeignKey('components', 'FK_COMPONENT_PAGE'); + + // Drop table + await queryRunner.dropTable('components'); + } +} diff --git a/server/migrations/1691007037021-CreateLayoutTable.ts b/server/migrations/1691007037021-CreateLayoutTable.ts new file mode 100644 index 0000000000..a6b9900df1 --- /dev/null +++ b/server/migrations/1691007037021-CreateLayoutTable.ts @@ -0,0 +1,66 @@ +import { MigrationInterface, QueryRunner, Table, TableForeignKey } from 'typeorm'; + +export class CreateLayoutTable1691007037021 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.createTable( + new Table({ + name: 'layouts', + columns: [ + { + name: 'id', + type: 'uuid', + isPrimary: true, + default: 'gen_random_uuid()', + }, + { + name: 'type', + type: 'enum', + enumName: 'layput_type', + enum: ['desktop', 'mobile'], + isNullable: false, + }, + { + name: 'top', + type: 'double precision', + isNullable: false, + }, + { + name: 'left', + type: 'double precision', + isNullable: false, + }, + { + name: 'width', + type: 'double precision', + isNullable: false, + }, + { + name: 'height', + type: 'double precision', + isNullable: false, + }, + { + name: 'component_id', + type: 'uuid', + isNullable: false, + }, + ], + }) + ); + + // Add foreign key to relate Layout with Component + await queryRunner.createForeignKey( + 'layouts', + new TableForeignKey({ + columnNames: ['component_id'], + referencedColumnNames: ['id'], + referencedTableName: 'components', + onDelete: 'CASCADE', + }) + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.dropTable('layouts'); + } +} diff --git a/server/package-lock.json b/server/package-lock.json index 9b6d518c17..9c849e112f 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -55,7 +55,7 @@ "request-ip": "^3.3.0", "rxjs": "^7.2.0", "sanitize-html": "^2.7.0", - "semver": "^7.3.5", + "semver": "^7.5.4", "ts-node": "^10.0.0", "tsconfig-paths": "^3.10.1", "typeorm": "^0.2.38", @@ -11858,8 +11858,9 @@ } }, "node_modules/semver": { - "version": "7.3.5", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -22603,8 +22604,9 @@ } }, "semver": { - "version": "7.3.5", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "requires": { "lru-cache": "^6.0.0" } diff --git a/server/package.json b/server/package.json index 327c6c4b02..7b977b5f82 100644 --- a/server/package.json +++ b/server/package.json @@ -80,7 +80,7 @@ "request-ip": "^3.3.0", "rxjs": "^7.2.0", "sanitize-html": "^2.7.0", - "semver": "^7.3.5", + "semver": "^7.5.4", "ts-node": "^10.0.0", "tsconfig-paths": "^3.10.1", "typeorm": "^0.2.38", diff --git a/server/src/controllers/apps.controller.v2.ts b/server/src/controllers/apps.controller.v2.ts new file mode 100644 index 0000000000..45944e7210 --- /dev/null +++ b/server/src/controllers/apps.controller.v2.ts @@ -0,0 +1,502 @@ +import { + Controller, + ForbiddenException, + Get, + Param, + Post, + Put, + Delete, + Query, + UseGuards, + Body, + BadRequestException, + UseInterceptors, +} from '@nestjs/common'; +import { JwtAuthGuard } from '../../src/modules/auth/jwt-auth.guard'; +import { AppAuthGuard } from 'src/modules/auth/app-auth.guard'; +import { AppsService } from '../services/apps.service'; +import { camelizeKeys, decamelizeKeys } from 'humps'; +import { AppsAbilityFactory } from 'src/modules/casl/abilities/apps-ability.factory'; + +import { App } from 'src/entities/app.entity'; +import { User } from 'src/decorators/user.decorator'; + +import { CreatePageDto, DeletePageDto } from '@dto/pages.dto'; +import { CreateComponentDto, DeleteComponentDto, UpdateComponentDto, LayoutUpdateDto } from '@dto/component.dto'; + +import { ValidAppInterceptor } from 'src/interceptors/valid.app.interceptor'; +import { AppDecorator } from 'src/decorators/app.decorator'; + +import { ComponentsService } from '@services/components.service'; +import { PageService } from '@services/page.service'; +import { EventsService } from '@services/events_handler.service'; +import { AppVersionUpdateDto } from '@dto/app-version-update.dto'; +import { CreateEventHandlerDto, UpdateEventHandlerDto } from '@dto/event-handler.dto'; + +@Controller({ + path: 'apps', + version: '2', +}) +export class AppsControllerV2 { + constructor( + private appsService: AppsService, + private componentsService: ComponentsService, + private pageService: PageService, + private eventsService: EventsService, + private eventService: EventsService, + private appsAbilityFactory: AppsAbilityFactory + ) {} + + @UseGuards(JwtAuthGuard) + @UseInterceptors(ValidAppInterceptor) + @Get(':id') + async show(@User() user, @AppDecorator() app: App, @Query('access_type') accessType: string) { + const ability = await this.appsAbilityFactory.appsActions(user, app.id); + if (!ability.can('viewApp', app)) { + throw new ForbiddenException( + JSON.stringify({ + organizationId: app.organizationId, + }) + ); + } + + if (accessType === 'edit' && !ability.can('editApp', app)) { + throw new ForbiddenException( + JSON.stringify({ + organizationId: app.organizationId, + }) + ); + } + + const response = decamelizeKeys(app); + + const seralizedQueries = []; + const dataQueriesForVersion = app.editingVersion + ? await this.appsService.findDataQueriesForVersion(app.editingVersion.id) + : []; + + const pagesForVersion = app.editingVersion ? await this.pageService.findPagesForVersion(app.editingVersion.id) : []; + const eventsForVersion = app.editingVersion + ? await this.eventsService.findEventsForVersion(app.editingVersion.id) + : []; + + // serialize queries + for (const query of dataQueriesForVersion) { + const decamelizedQuery = decamelizeKeys(query); + decamelizedQuery['options'] = query.options; + seralizedQueries.push(decamelizedQuery); + } + + response['data_queries'] = seralizedQueries; + response['definition'] = app.editingVersion?.definition; + response['pages'] = pagesForVersion; + response['events'] = eventsForVersion; + + //! if editing version exists, camelize the definition + if (app.editingVersion && app.editingVersion.definition) { + response['editing_version'] = { + ...response['editing_version'], + definition: camelizeKeys(app.editingVersion.definition), + }; + } + + return response; + } + + @UseGuards(AppAuthGuard) // This guard will allow access for unauthenticated user if the app is public + @Get('slugs/:slug') + async appFromSlug(@User() user, @AppDecorator() app: App) { + if (user) { + const ability = await this.appsAbilityFactory.appsActions(user, app.id); + + if (!ability.can('viewApp', app)) { + throw new ForbiddenException( + JSON.stringify({ + organizationId: app.organizationId, + }) + ); + } + } + + const versionToLoad = app.currentVersionId + ? await this.appsService.findVersion(app.currentVersionId) + : await this.appsService.findVersion(app.editingVersion?.id); + + const pagesForVersion = app.editingVersion ? await this.pageService.findPagesForVersion(versionToLoad.id) : []; + const eventsForVersion = app.editingVersion ? await this.eventsService.findEventsForVersion(versionToLoad.id) : []; + + // serialize + return { + current_version_id: app['currentVersionId'], + data_queries: versionToLoad?.dataQueries, + definition: versionToLoad?.definition, + is_public: app.isPublic, + is_maintenance_on: app.isMaintenanceOn, + name: app.name, + slug: app.slug, + events: eventsForVersion, + pages: pagesForVersion, + homePageId: versionToLoad.homePageId, + globalSettings: versionToLoad.globalSettings, + showViewerNavigation: versionToLoad.showViewerNavigation, + }; + } + + @UseGuards(JwtAuthGuard) + @UseInterceptors(ValidAppInterceptor) + @Get(':id/versions/:versionId') + async version(@User() user, @Param('id') id, @Param('versionId') versionId) { + const appVersion = await this.appsService.findVersion(versionId); + const app = appVersion.app; + + if (app.id !== id) { + throw new BadRequestException(); + } + const ability = await this.appsAbilityFactory.appsActions(user, app.id); + + if (!ability.can('fetchVersions', app)) { + throw new ForbiddenException( + JSON.stringify({ + organizationId: app.organizationId, + }) + ); + } + + const pagesForVersion = await this.pageService.findPagesForVersion(versionId); + const eventsForVersion = await this.eventsService.findEventsForVersion(versionId); + + const appCurrentEditingVersion = JSON.parse(JSON.stringify(appVersion)); + + delete appCurrentEditingVersion['app']; + + const appData = { + ...app, + }; + + delete appData['editingVersion']; + + return { + ...appData, + editing_version: camelizeKeys(appCurrentEditingVersion), + pages: pagesForVersion, + events: eventsForVersion, + }; + } + + @UseGuards(JwtAuthGuard) + @UseInterceptors(ValidAppInterceptor) + @Put(':id/versions/:versionId') + async updateVersion( + @User() user, + @Param('id') id, + @Param('versionId') versionId, + @Body() appVersionUpdateDto: AppVersionUpdateDto + ) { + const version = await this.appsService.findVersion(versionId); + const app = version.app; + + if (app.id !== id) { + throw new BadRequestException(); + } + const ability = await this.appsAbilityFactory.appsActions(user, id); + + if (!ability.can('updateVersions', app)) { + throw new ForbiddenException('You do not have permissions to perform this action'); + } + + return await this.appsService.updateAppVersion(version, appVersionUpdateDto); + } + + @UseGuards(JwtAuthGuard) + @UseInterceptors(ValidAppInterceptor) + @Put(':id/versions/:versionId/global_settings') + async updateGlobalSettings( + @User() user, + @Param('id') id, + @Param('versionId') versionId, + @Body() appVersionUpdateDto: AppVersionUpdateDto + ) { + const version = await this.appsService.findVersion(versionId); + const app = version.app; + + if (app.id !== id) { + throw new BadRequestException(); + } + const ability = await this.appsAbilityFactory.appsActions(user, id); + + if (!ability.can('updateVersions', app)) { + throw new ForbiddenException('You do not have permissions to perform this action'); + } + + return await this.appsService.updateAppVersion(version, appVersionUpdateDto); + } + + //components api + @UseGuards(JwtAuthGuard) + @UseInterceptors(ValidAppInterceptor) + @Post(':id/versions/:versionId/components') + async createComponent( + @User() user, + @Param('id') id, + @Param('versionId') versionId, + @Body() createComponentDto: CreateComponentDto + ) { + const version = await this.appsService.findVersion(versionId); + const app = version.app; + + if (app.id !== id) { + throw new BadRequestException(); + } + const ability = await this.appsAbilityFactory.appsActions(user, id); + + if (!ability.can('updateVersions', app)) { + throw new ForbiddenException('You do not have permissions to perform this action'); + } + + await this.componentsService.create(createComponentDto.diff, createComponentDto.pageId, versionId); + } + + @UseGuards(JwtAuthGuard) + @UseInterceptors(ValidAppInterceptor) + @Put(':id/versions/:versionId/components') + async updateComponent( + @User() user, + @Param('id') id, + @Param('versionId') versionId, + @Body() updateComponentDto: UpdateComponentDto + ) { + const version = await this.appsService.findVersion(versionId); + const app = version.app; + + if (app.id !== id) { + throw new BadRequestException(); + } + const ability = await this.appsAbilityFactory.appsActions(user, id); + + if (!ability.can('updateVersions', app)) { + throw new ForbiddenException('You do not have permissions to perform this action'); + } + + await this.componentsService.update(updateComponentDto.diff, versionId); + } + + @UseGuards(JwtAuthGuard) + @UseInterceptors(ValidAppInterceptor) + @Delete(':id/versions/:versionId/components') + async deleteComponents( + @User() user, + @Param('id') id, + @Param('versionId') versionId, + @Body() deleteComponentDto: DeleteComponentDto + ) { + const version = await this.appsService.findVersion(versionId); + const app = version.app; + + if (app.id !== id) { + throw new BadRequestException(); + } + const ability = await this.appsAbilityFactory.appsActions(user, id); + + if (!ability.can('updateVersions', app)) { + throw new ForbiddenException('You do not have permissions to perform this action'); + } + + await this.componentsService.delete(deleteComponentDto.diff, versionId, deleteComponentDto.is_component_cut); + } + + @UseGuards(JwtAuthGuard) + @UseInterceptors(ValidAppInterceptor) + @Put(':id/versions/:versionId/components/layout') + async updateComponentLayout( + @User() user, + @Param('id') id, + @Param('versionId') versionId, + @Body() updateComponentLayout: LayoutUpdateDto + ) { + const version = await this.appsService.findVersion(versionId); + const app = version.app; + + if (app.id !== id) { + throw new BadRequestException(); + } + const ability = await this.appsAbilityFactory.appsActions(user, id); + + if (!ability.can('updateVersions', app)) { + throw new ForbiddenException('You do not have permissions to perform this action'); + } + + await this.componentsService.componentLayoutChange(updateComponentLayout.diff, versionId); + } + + // pages api + @UseGuards(JwtAuthGuard) + @UseInterceptors(ValidAppInterceptor) + @Post(':id/versions/:versionId/pages') + async createPages( + @User() user, + @Param('id') id, + @Param('versionId') versionId, + @Body() createPageDto: CreatePageDto + ) { + const version = await this.appsService.findVersion(versionId); + const app = version.app; + + if (app.id !== id) { + throw new BadRequestException(); + } + const ability = await this.appsAbilityFactory.appsActions(user, id); + + if (!ability.can('updateVersions', app)) { + throw new ForbiddenException('You do not have permissions to perform this action'); + } + + await this.pageService.createPage(createPageDto, versionId); + } + + @UseGuards(JwtAuthGuard) + @UseInterceptors(ValidAppInterceptor) + @Post(':id/versions/:versionId/pages/:pageId/clone') + async clonePage(@User() user, @Param('id') id, @Param('versionId') versionId, @Param('pageId') pageId) { + const version = await this.appsService.findVersion(versionId); + const app = version.app; + const ability = await this.appsAbilityFactory.appsActions(user, id); + + if (!ability.can('updateVersions', app)) { + throw new ForbiddenException('You do not have permissions to perform this action'); + } + + return await this.pageService.clonePage(pageId, versionId); + } + + @UseGuards(JwtAuthGuard) + @UseInterceptors(ValidAppInterceptor) + @Put(':id/versions/:versionId/pages') + async updatePages(@User() user, @Param('id') id, @Param('versionId') versionId, @Body() updatePageDto) { + const version = await this.appsService.findVersion(versionId); + const app = version.app; + + if (app.id !== id) { + throw new BadRequestException(); + } + const ability = await this.appsAbilityFactory.appsActions(user, id); + + if (!ability.can('updateVersions', app)) { + throw new ForbiddenException('You do not have permissions to perform this action'); + } + + await this.pageService.updatePage(updatePageDto, versionId); + } + + @UseGuards(JwtAuthGuard) + @UseInterceptors(ValidAppInterceptor) + @Delete(':id/versions/:versionId/pages') + async deletePage(@User() user, @Param('id') id, @Param('versionId') versionId, @Body() deletePageDto: DeletePageDto) { + const version = await this.appsService.findVersion(versionId); + const app = version.app; + + if (app.id !== id) { + throw new BadRequestException(); + } + const ability = await this.appsAbilityFactory.appsActions(user, id); + + if (!ability.can('updateVersions', app)) { + throw new ForbiddenException('You do not have permissions to perform this action'); + } + + await this.pageService.deletePage(deletePageDto.pageId, versionId); + } + + // events api + + @UseGuards(JwtAuthGuard) + @UseInterceptors(ValidAppInterceptor) + @Get(':id/versions/:versionId/events') + async getEvents(@User() user, @Param('id') id, @Param('versionId') versionId, @Query('sourceId') sourceId) { + const version = await this.appsService.findVersion(versionId); + const app = version.app; + + if (app.id !== id) { + throw new BadRequestException(); + } + + const ability = await this.appsAbilityFactory.appsActions(user, id); + + if (!ability.can('viewApp', app)) { + throw new ForbiddenException('You do not have permissions to perform this action'); + } + + if (!sourceId) { + return this.eventService.findEventsForVersion(versionId); + } + + return this.eventService.findAllEventsWithSourceId(sourceId); + } + + @UseGuards(JwtAuthGuard) + @UseInterceptors(ValidAppInterceptor) + @Post(':id/versions/:versionId/events') + async createEvent( + @User() user, + @Param('id') id, + @Param('versionId') versionId, + @Body() createEventHandlerDto: CreateEventHandlerDto + ) { + const version = await this.appsService.findVersion(versionId); + const app = version.app; + + if (app.id !== id) { + throw new BadRequestException(); + } + const ability = await this.appsAbilityFactory.appsActions(user, id); + + if (!ability.can('updateVersions', app)) { + throw new ForbiddenException('You do not have permissions to perform this action'); + } + + return this.eventService.createEvent(createEventHandlerDto, versionId); + } + @UseGuards(JwtAuthGuard) + @UseInterceptors(ValidAppInterceptor) + @Put(':id/versions/:versionId/events') + async updateEvents( + @User() user, + @Param('id') id, + @Param('versionId') versionId, + @Body() updateEventHandlerDto: UpdateEventHandlerDto + ) { + const version = await this.appsService.findVersion(versionId); + const app = version.app; + + if (app.id !== id) { + throw new BadRequestException(); + } + const ability = await this.appsAbilityFactory.appsActions(user, id); + + if (!ability.can('updateVersions', app)) { + throw new ForbiddenException('You do not have permissions to perform this action'); + } + + const { events, updateType } = updateEventHandlerDto; + + return await this.eventService.updateEvent(events, updateType, versionId); + } + + @UseGuards(JwtAuthGuard) + @UseInterceptors(ValidAppInterceptor) + @Delete(':id/versions/:versionId/events/:eventId') + async deleteEvents(@User() user, @Param('id') id, @Param('versionId') versionId, @Param('eventId') eventId) { + const version = await this.appsService.findVersion(versionId); + const app = version.app; + + if (app.id !== id) { + throw new BadRequestException(); + } + const ability = await this.appsAbilityFactory.appsActions(user, id); + + if (!ability.can('updateVersions', app)) { + throw new ForbiddenException('You do not have permissions to perform this action'); + } + + return await this.eventService.deleteEvent(eventId, versionId); + } +} diff --git a/server/src/dto/app-version-update.dto.ts b/server/src/dto/app-version-update.dto.ts new file mode 100644 index 0000000000..53d79b387a --- /dev/null +++ b/server/src/dto/app-version-update.dto.ts @@ -0,0 +1,26 @@ +import { IsBoolean, IsNotEmpty, IsOptional, IsString, IsUUID, MaxLength } from 'class-validator'; +import { Transform } from 'class-transformer'; +import { sanitizeInput } from '../helpers/utils.helper'; + +export class AppVersionUpdateDto { + @IsString() + @IsOptional() + @Transform(({ value }) => { + const newValue = sanitizeInput(value); + return newValue.trim(); + }) + @IsNotEmpty() + @MaxLength(50, { message: 'Maximum length has been reached.' }) + name: string; + + @IsBoolean() + @IsOptional() + showViewerNavigation: boolean; + + @IsUUID() + @IsOptional() + homePageId: string; + + @IsOptional() + globalSettings: any; +} diff --git a/server/src/dto/component.dto.ts b/server/src/dto/component.dto.ts new file mode 100644 index 0000000000..1c12fa8ce5 --- /dev/null +++ b/server/src/dto/component.dto.ts @@ -0,0 +1,159 @@ +import { Type } from 'class-transformer'; +import { + IsArray, + IsBoolean, + IsNotEmpty, + IsNumber, + IsObject, + IsOptional, + IsString, + IsUUID, + ValidationArguments, + ValidatorConstraint, + ValidatorConstraintInterface, + Validate, +} from 'class-validator'; + +export class ComponentLayoutDto { + @IsNumber() + @IsOptional() + top?: number; + + @IsNumber() + @IsOptional() + left?: number; + + @IsNumber() + @IsOptional() + width?: number; + + @IsNumber() + @IsOptional() + height?: number; +} + +export class LayoutData { + @IsObject() + @IsOptional() + desktop?: ComponentLayoutDto; + + @IsObject() + @IsOptional() + mobile?: ComponentLayoutDto; +} + +@ValidatorConstraint({ name: 'LayoutDataValidator', async: false }) +class LayoutDataValidator implements ValidatorConstraintInterface { + validate(value: any) { + if (value) { + for (const key in value) { + if (!value[key] || typeof value[key] !== 'object' || !value[key].layouts) { + return false; + } + } + } + return true; + } + + defaultMessage(args: ValidationArguments) { + return `Each key in "diff" must have the structure { layouts: LayoutData }`; + } +} + +export class LayoutUpdateDto { + @IsBoolean() + is_user_switched_version: boolean; + + @IsUUID() + pageId: string; + + @IsObject() + @IsNotEmpty() + @Validate(LayoutDataValidator, { each: true }) + diff: Record; +} + +class ComponentDto { + @IsString() + name: string; + + @IsObject() + properties: Record; + + @IsObject() + styles: Record; + + @IsObject() + validation: Record; + + @IsString() + type: string; + + @IsObject() + others: Record; + + @IsOptional() + @Type(() => ComponentLayoutDto) + layouts: ComponentLayoutDto; + + @IsOptional() + parent: string; +} + +@ValidatorConstraint({ name: 'CreateComponentDtoValidator', async: false }) +class CreateComponentDtoValidator implements ValidatorConstraintInterface { + validate(value: any, args: ValidationArguments) { + // Check if the diff structure is valid + for (const key in value.diff) { + if (!value.diff[key] || typeof value.diff[key] !== 'object') { + return false; + } + // You can add additional checks for the component structure here + } + + return true; + } + + defaultMessage(args: ValidationArguments) { + return `Invalid structure in diff for CreateComponentDto`; + } +} + +export class CreateComponentDto { + @IsBoolean() + is_user_switched_version: boolean; + + @IsUUID() + pageId: string; + + @IsObject() + @Validate(CreateComponentDtoValidator) + diff: Record; +} + +export class UpdateComponentDto { + @IsBoolean() + is_user_switched_version: boolean; + + @IsUUID() + pageId: string; + + @IsObject() + @Validate(CreateComponentDtoValidator) + diff: Record; +} + +export class DeleteComponentDto { + @IsBoolean() + is_user_switched_version: boolean; + + @IsUUID() + pageId: string; + + @IsArray() + diff: string[]; + + @IsBoolean() + @IsOptional() + is_component_cut: boolean; +} diff --git a/server/src/dto/event-handler.dto.ts b/server/src/dto/event-handler.dto.ts new file mode 100644 index 0000000000..80623de736 --- /dev/null +++ b/server/src/dto/event-handler.dto.ts @@ -0,0 +1,44 @@ +import { IsArray, IsIn, IsNumber, IsObject, IsString, IsUUID, ValidateNested } from 'class-validator'; +import { Target } from 'src/entities/event_handler.entity'; + +export class CreateEventHandlerDto { + @IsObject() + event: any; + + @IsString() + eventType: Target; + + @IsString() + attachedTo: string; + + @IsNumber() + index: number; +} + +class UpdateEventDiff { + @IsString() + name: string; + + @IsNumber() + index: number; + + @IsObject() + @ValidateNested() + event: any; +} + +export class UpdateEvent { + @IsUUID() + event_id: string; + + @IsObject() + diff: UpdateEventDiff; +} + +export class UpdateEventHandlerDto { + @IsArray() + events: UpdateEvent[]; + + @IsIn(['update', 'reorder']) + updateType: 'update' | 'reorder'; +} diff --git a/server/src/dto/pages.dto.ts b/server/src/dto/pages.dto.ts new file mode 100644 index 0000000000..875585b3ce --- /dev/null +++ b/server/src/dto/pages.dto.ts @@ -0,0 +1,38 @@ +import { IsNotEmpty, IsNumber, IsOptional, IsString, IsUUID, MaxLength } from 'class-validator'; + +export class CreatePageDto { + @IsUUID() + @IsNotEmpty() + id: string; + + @IsString() + @IsNotEmpty() + @MaxLength(32) + name: string; + + @IsString() + @IsNotEmpty() + @MaxLength(50) + handle: string; + + @IsNumber() + @IsNotEmpty() + index: number; + + @IsOptional() + disabled: boolean; + + @IsOptional() + hidden: boolean; +} + +export class DeletePageDto { + @IsUUID() + @IsNotEmpty() + pageId: string; +} + +export class UpdatePageDto { + pageId: string; + diff: Partial; +} diff --git a/server/src/dto/version-edit.dto.ts b/server/src/dto/version-edit.dto.ts index 5bbdf2a90c..2736652982 100644 --- a/server/src/dto/version-edit.dto.ts +++ b/server/src/dto/version-edit.dto.ts @@ -20,4 +20,11 @@ export class VersionEditDto { @IsOptional() @IsBoolean() is_user_switched_version: boolean; + + @IsOptional() + diff: any; + + @IsOptional() + @IsString() + pageId: string; } diff --git a/server/src/entities/app_version.entity.ts b/server/src/entities/app_version.entity.ts index 17027de2f5..683d7f7c5d 100644 --- a/server/src/entities/app_version.entity.ts +++ b/server/src/entities/app_version.entity.ts @@ -13,6 +13,8 @@ import { import { App } from './app.entity'; import { DataQuery } from './data_query.entity'; import { DataSource } from './data_source.entity'; +import { Page } from './page.entity'; +import { EventHandler } from './event_handler.entity'; @Entity({ name: 'app_versions' }) @Unique(['name', 'appId']) @@ -26,6 +28,15 @@ export class AppVersion extends BaseEntity { @Column('simple-json', { name: 'definition' }) definition; + @Column('simple-json', { name: 'global_settings' }) + globalSettings; + + @Column({ name: 'show_viewer_navigation' }) + showViewerNavigation: boolean; + + @Column({ name: 'home_page_id' }) + homePageId: string; + @Column({ name: 'app_id' }) appId: string; @@ -47,4 +58,12 @@ export class AppVersion extends BaseEntity { @OneToMany(() => DataQuery, (dataQuery) => dataQuery.appVersion) dataQueries: DataQuery[]; + + @OneToMany(() => Page, (page) => page.appVersion, { onDelete: 'CASCADE' }) + pages: Page[]; + + @OneToMany(() => EventHandler, (eventHandler) => eventHandler.appVersion, { + onDelete: 'CASCADE', + }) + eventHandlers: EventHandler[]; } diff --git a/server/src/entities/component.entity.ts b/server/src/entities/component.entity.ts new file mode 100644 index 0000000000..bb22c7dcd4 --- /dev/null +++ b/server/src/entities/component.entity.ts @@ -0,0 +1,63 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + ManyToOne, + OneToMany, + JoinColumn, + Index, + CreateDateColumn, + UpdateDateColumn, +} from 'typeorm'; +import { Page } from './page.entity'; +import { Layout } from './layout.entity'; + +@Entity({ name: 'components' }) +@Index('idx_component_page_id', ['pageId']) +export class Component { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column() + name: string; + + @Column() + type: string; + + @Column({ name: 'page_id' }) + pageId: string; + + @Column({ nullable: true }) + parent: string; + + @Column('simple-json') + properties: any; + + @Column('simple-json', { name: 'general_properties', nullable: true }) + general: any; + + @Column('simple-json') + styles: any; + + @Column('simple-json', { name: 'general_styles', nullable: true }) + generalStyles: any; + + @Column('simple-json', { name: 'display_preferences', nullable: true }) + displayPreferences: any; + + @Column('simple-json') + validation: any; + + @CreateDateColumn({ default: () => 'now()', name: 'created_at' }) + createdAt: Date; + + @UpdateDateColumn({ default: () => 'now()', name: 'updated_at' }) + updatedAt: Date; + + @ManyToOne(() => Page, (page) => page.components) + @JoinColumn({ name: 'page_id' }) + page: Page; + + @OneToMany(() => Layout, (layout) => layout.component) + layouts: Layout[]; +} diff --git a/server/src/entities/event_handler.entity.ts b/server/src/entities/event_handler.entity.ts new file mode 100644 index 0000000000..6585d640e0 --- /dev/null +++ b/server/src/entities/event_handler.entity.ts @@ -0,0 +1,52 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + ManyToOne, + JoinColumn, + CreateDateColumn, + UpdateDateColumn, +} from 'typeorm'; +import { AppVersion } from './app_version.entity'; + +export enum Target { + page = 'page', + component = 'component', + dataQuery = 'data_query', + tableColumn = 'table_column', + tableAction = 'table_action', +} + +@Entity({ name: 'event_handlers' }) +export class EventHandler { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column() + name: string; + + @Column() + index: number; + + @Column('simple-json') + event: any; + + @Column({ name: 'source_id' }) + sourceId: string; + + @Column() + target: Target; + + @Column({ name: 'app_version_id' }) + appVersionId: string; + + @CreateDateColumn({ default: () => 'now()', name: 'created_at' }) + createdAt: Date; + + @UpdateDateColumn({ default: () => 'now()', name: 'updated_at' }) + updatedAt: Date; + + @ManyToOne(() => AppVersion, (appVersion) => appVersion.pages) + @JoinColumn({ name: 'app_version_id' }) + appVersion: AppVersion; +} diff --git a/server/src/entities/layout.entity.ts b/server/src/entities/layout.entity.ts new file mode 100644 index 0000000000..42beecdd62 --- /dev/null +++ b/server/src/entities/layout.entity.ts @@ -0,0 +1,30 @@ +import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from 'typeorm'; +import { Component } from './component.entity'; + +@Entity({ name: 'layouts' }) +export class Layout { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column({ type: 'enum', enumName: 'layout_type', enum: ['desktop', 'mobile'] }) + type: string; + + @Column({ type: 'double precision' }) + top: number; + + @Column({ type: 'double precision' }) + left: number; + + @Column({ type: 'double precision' }) + width: number; + + @Column({ type: 'double precision' }) + height: number; + + @Column({ name: 'component_id' }) + componentId: string; + + @ManyToOne(() => Component, (component) => component.layouts) + @JoinColumn({ name: 'component_id' }) + component: Component; +} diff --git a/server/src/entities/page.entity.ts b/server/src/entities/page.entity.ts new file mode 100644 index 0000000000..2731d301e7 --- /dev/null +++ b/server/src/entities/page.entity.ts @@ -0,0 +1,49 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + ManyToOne, + OneToMany, + JoinColumn, + CreateDateColumn, + UpdateDateColumn, +} from 'typeorm'; +import { AppVersion } from './app_version.entity'; +import { Component } from './component.entity'; + +@Entity({ name: 'pages' }) +export class Page { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column() + name: string; + + @Column({ name: 'page_handle' }) + handle: string; + + @Column() + index: number; + + @Column() + disabled: boolean; + + @Column() + hidden: boolean; + + @CreateDateColumn({ default: () => 'now()', name: 'created_at' }) + createdAt: Date; + + @UpdateDateColumn({ default: () => 'now()', name: 'updated_at' }) + updatedAt: Date; + + @Column({ name: 'app_version_id' }) + appVersionId: string; + + @ManyToOne(() => AppVersion, (appVersion) => appVersion.pages) + @JoinColumn({ name: 'app_version_id' }) + appVersion: AppVersion; + + @OneToMany(() => Component, (component) => component.page) + components: Component[]; +} diff --git a/server/src/helpers/utils.helper.ts b/server/src/helpers/utils.helper.ts index ff16e9337d..471b88c52a 100644 --- a/server/src/helpers/utils.helper.ts +++ b/server/src/helpers/utils.helper.ts @@ -5,6 +5,7 @@ import { isEmpty } from 'lodash'; import { ConflictException } from '@nestjs/common'; import { DataBaseConstraints } from './db_constraints.constants'; const protobuf = require('protobufjs'); +const semver = require('semver'); export function maybeSetSubPath(path) { const hasSubPath = process.env.SUB_PATH !== undefined; @@ -80,6 +81,26 @@ export async function dbTransactionWrap(operation: (...args) => any, manager?: E } } +export const updateTimestampForAppVersion = async (manager, appVersionId) => { + const appVersion = await manager.findOne('app_versions', appVersionId); + if (appVersion) { + await manager.update('app_versions', appVersionId, { updatedAt: new Date() }); + } +}; + +export async function dbTransactionForAppVersionAssociationsUpdate( + operation: (...args) => any, + appVersionId: string +): Promise { + return await getManager().transaction(async (manager) => { + const result = await operation(manager); + + await updateTimestampForAppVersion(manager, appVersionId); + + return result; + }); +} + type DbContraintAndMsg = { dbConstraint: DataBaseConstraints; message: string; @@ -196,3 +217,21 @@ export const generateOrgInviteURL = (organizationToken: string, organizationId?: organizationId ? `?oid=${organizationId}` : '' }`; }; + +export function extractMajorVersion(version) { + return semver.valid(semver.coerce(version)); +} + +/** + * Checks if a given Tooljet version is compatible with normalized app definition schemas. + * + * This function uses the 'semver' library to compare the provided version with a minimum version requirement + * for normalized app definition schemas (2.24.1). It returns true if the version is greater than or equal to + * the required version, indicating compatibility. + * + * @param {string} version - The Tooljet version to check. + * @returns {boolean} - True if the version is compatible, false otherwise. + */ +export function isTooljetVersionWithNormalizedAppDefinitionSchem(version) { + return semver.satisfies(semver.coerce(version), '>= 2.24.0'); +} diff --git a/server/src/modules/apps/apps.module.ts b/server/src/modules/apps/apps.module.ts index 400ad7d44b..551d81de68 100644 --- a/server/src/modules/apps/apps.module.ts +++ b/server/src/modules/apps/apps.module.ts @@ -3,6 +3,7 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { App } from '../../entities/app.entity'; import { File } from '../../entities/file.entity'; import { AppsController } from '../../controllers/apps.controller'; +import { AppsControllerV2 } from '../../controllers/apps.controller.v2'; import { AppsService } from '../../services/apps.service'; import { AppVersion } from '../../../src/entities/app_version.entity'; import { DataQuery } from '../../../src/entities/data_query.entity'; @@ -33,6 +34,15 @@ import { Plugin } from 'src/entities/plugin.entity'; import { PluginsHelper } from 'src/helpers/plugins.helper'; import { AppEnvironmentService } from '@services/app_environments.service'; +import { Component } from 'src/entities/component.entity'; +import { Page } from 'src/entities/page.entity'; +import { EventHandler } from 'src/entities/event_handler.entity'; +import { Layout } from 'src/entities/layout.entity'; + +import { ComponentsService } from '@services/components.service'; +import { PageService } from '@services/page.service'; +import { EventsService } from '@services/events_handler.service'; + @Module({ imports: [ TypeOrmModule.forFeature([ @@ -52,6 +62,10 @@ import { AppEnvironmentService } from '@services/app_environments.service'; Credential, File, Plugin, + Component, + Page, + EventHandler, + Layout, ]), CaslModule, ], @@ -68,7 +82,10 @@ import { AppEnvironmentService } from '@services/app_environments.service'; PluginsService, PluginsHelper, AppEnvironmentService, + ComponentsService, + PageService, + EventsService, ], - controllers: [AppsController, AppUsersController, AppsImportExportController], + controllers: [AppsController, AppsControllerV2, AppUsersController, AppsImportExportController], }) export class AppsModule {} diff --git a/server/src/services/app_environments.service.ts b/server/src/services/app_environments.service.ts index 1223ec1f93..461f486e1c 100644 --- a/server/src/services/app_environments.service.ts +++ b/server/src/services/app_environments.service.ts @@ -114,6 +114,7 @@ export class AppEnvironmentService { order: { createdAt: 'DESC', }, + select: ['id', 'name', 'appId'], }); }); } diff --git a/server/src/services/app_import_export.service.ts b/server/src/services/app_import_export.service.ts index 9708bf6283..aed78b38a9 100644 --- a/server/src/services/app_import_export.service.ts +++ b/server/src/services/app_import_export.service.ts @@ -11,7 +11,13 @@ import { GroupPermission } from 'src/entities/group_permission.entity'; import { User } from 'src/entities/user.entity'; import { EntityManager } from 'typeorm'; import { DataSourcesService } from './data_sources.service'; -import { dbTransactionWrap, defaultAppEnvironments, catchDbException } from 'src/helpers/utils.helper'; +import { + dbTransactionWrap, + defaultAppEnvironments, + catchDbException, + extractMajorVersion, + isTooljetVersionWithNormalizedAppDefinitionSchem, +} from 'src/helpers/utils.helper'; import { AppEnvironmentService } from './app_environments.service'; import { convertAppDefinitionFromSinglePageToMultiPage } from '../../lib/single-page-to-and-from-multipage-definition-conversion'; import { DataSourceScopes, DataSourceTypes } from 'src/helpers/data_source.constants'; @@ -19,6 +25,11 @@ import { Organization } from 'src/entities/organization.entity'; import { DataBaseConstraints } from 'src/helpers/db_constraints.constants'; import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity'; import { Plugin } from 'src/entities/plugin.entity'; +import { Page } from 'src/entities/page.entity'; +import { Component } from 'src/entities/component.entity'; +import { Layout } from 'src/entities/layout.entity'; +import { EventHandler, Target } from 'src/entities/event_handler.entity'; +import { v4 as uuid } from 'uuid'; interface AppResourceMappings { defaultDataSourceIdMapping: Record; @@ -26,6 +37,8 @@ interface AppResourceMappings { appVersionMapping: Record; appEnvironmentMapping: Record; appDefaultEnvironmentMapping: Record; + pagesMapping: Record; + componentsMapping: Record; } type DefaultDataSourceKind = 'restapi' | 'runjs' | 'runpy' | 'tooljetdb' | 'workflows'; @@ -137,6 +150,37 @@ export class AppImportExportService { }); } + const pages = await manager + .createQueryBuilder(Page, 'pages') + .where('pages.appVersionId IN(:...versionId)', { + versionId: appVersions.map((v) => v.id), + }) + .orderBy('pages.created_at', 'ASC') + .getMany(); + + const components = + pages.length > 0 + ? await manager + .createQueryBuilder(Component, 'components') + .leftJoinAndSelect('components.layouts', 'layouts') + .where('components.pageId IN(:...pageId)', { + pageId: pages.map((v) => v.id), + }) + .orderBy('components.created_at', 'ASC') + .getMany() + : []; + + const events = await manager + .createQueryBuilder(EventHandler, 'event_handlers') + .where('event_handlers.appVersionId IN(:...versionId)', { + versionId: appVersions.map((v) => v.id), + }) + .orderBy('event_handlers.created_at', 'ASC') + .getMany(); + + appToExport['components'] = components; + appToExport['pages'] = pages; + appToExport['events'] = events; appToExport['dataQueries'] = dataQueries; appToExport['dataSources'] = dataSources; appToExport['appVersions'] = appVersions; @@ -152,7 +196,14 @@ export class AppImportExportService { }); } - async import(user: User, appParamsObj: any, appName: string, externalResourceMappings = {}): Promise { + async import( + user: User, + appParamsObj: any, + appName: string, + externalResourceMappings = {}, + tooljetVersion = '', + cloning = false + ): Promise { if (typeof appParamsObj !== 'object') { throw new BadRequestException('Invalid params for app import'); } @@ -167,24 +218,27 @@ export class AppImportExportService { throw new BadRequestException('Invalid params for app import'); } - let importedApp: App; - const schemaUnifiedAppParams = appParams?.schemaDetails?.multiPages ? appParams : convertSinglePageSchemaToMultiPageSchema(appParams); schemaUnifiedAppParams.name = appName; - await dbTransactionWrap(async (manager) => { - importedApp = await this.createImportedAppForUser(manager, schemaUnifiedAppParams, user); - await this.setupImportedAppAssociations( - manager, - importedApp, - schemaUnifiedAppParams, - user, - externalResourceMappings - ); - await this.createAdminGroupPermissions(manager, importedApp); - }); + const importedAppTooljetVersion = !cloning && extractMajorVersion(tooljetVersion); + const isNormalizedAppDefinitionSchema = cloning + ? true + : isTooljetVersionWithNormalizedAppDefinitionSchem(importedAppTooljetVersion); + + const importedApp = await this.createImportedAppForUser(this.entityManager, schemaUnifiedAppParams, user); + + await this.setupImportedAppAssociations( + this.entityManager, + importedApp, + schemaUnifiedAppParams, + user, + externalResourceMappings, + isNormalizedAppDefinitionSchema + ); + await this.createAdminGroupPermissions(this.entityManager, importedApp); // NOTE: App slug updation callback doesn't work while wrapped in transaction // hence updating slug explicitly @@ -207,6 +261,7 @@ export class AppImportExportService { createdAt: new Date(), updatedAt: new Date(), }); + await manager.save(importedApp); return importedApp; }, [{ dbConstraint: DataBaseConstraints.APP_NAME_UNIQUE, message: 'This app name is already taken.' }]); @@ -219,6 +274,9 @@ export class AppImportExportService { importingAppEnvironments: AppEnvironment[]; importingDataSourceOptions: DataSourceOptions[]; importingDefaultAppEnvironmentId: string; + importingPages: Page[]; + importingComponents: Component[]; + importingEvents: EventHandler[]; } { const importingDataSources = appParams?.dataSources || []; const importingDataQueries = appParams?.dataQueries || []; @@ -229,6 +287,10 @@ export class AppImportExportService { (env: { isDefault: any }) => env.isDefault )?.id; + const importingPages = appParams?.pages || []; + const importingComponents = appParams?.components || []; + const importingEvents = appParams?.events || []; + return { importingDataSources, importingDataQueries, @@ -236,6 +298,9 @@ export class AppImportExportService { importingAppEnvironments, importingDataSourceOptions, importingDefaultAppEnvironmentId, + importingPages, + importingComponents, + importingEvents, }; } @@ -248,7 +313,8 @@ export class AppImportExportService { importedApp: App, appParams: any, user: User, - externalResourceMappings: Record + externalResourceMappings: Record, + isNormalizedAppDefinitionSchema: boolean ) { // Old version without app version // Handle exports prior to 0.12.0 @@ -265,8 +331,9 @@ export class AppImportExportService { appVersionMapping: {}, appEnvironmentMapping: {}, appDefaultEnvironmentMapping: {}, + pagesMapping: {}, + componentsMapping: {}, }; - const { importingDataSources, importingDataQueries, @@ -274,6 +341,9 @@ export class AppImportExportService { importingAppEnvironments, importingDataSourceOptions, importingDefaultAppEnvironmentId, + importingPages, + importingComponents, + importingEvents, } = this.extractImportDataFromAppParams(appParams); const { appDefaultEnvironmentMapping, appVersionMapping } = await this.createAppVersionsForImportedApp( @@ -281,7 +351,8 @@ export class AppImportExportService { user, importedApp, importingAppVersions, - appResourceMappings + appResourceMappings, + isNormalizedAppDefinitionSchema ); appResourceMappings.appDefaultEnvironmentMapping = appDefaultEnvironmentMapping; appResourceMappings.appVersionMapping = appVersionMapping; @@ -296,24 +367,201 @@ export class AppImportExportService { importingDataSources, importingDataSourceOptions, importingDataQueries, - importingDefaultAppEnvironmentId + importingDefaultAppEnvironmentId, + importingPages, + importingComponents, + importingEvents ); - for (const importingAppVersion of importingAppVersions) { - const updatedDefinition = this.replaceDataQueryIdWithinDefinitions( - importingAppVersion.definition, - appResourceMappings.dataQueryMapping - ); - await manager.update( - AppVersion, - { id: appResourceMappings.appVersionMapping[importingAppVersion.id] }, - { - definition: updatedDefinition, + if (!isNormalizedAppDefinitionSchema) { + for (const importingAppVersion of importingAppVersions) { + const updatedDefinition = this.replaceDataQueryIdWithinDefinitions( + importingAppVersion.definition, + appResourceMappings.dataQueryMapping + ); + + let updateHomepageId = null; + + if (updatedDefinition?.pages) { + for (const pageId of Object.keys(updatedDefinition?.pages)) { + const page = updatedDefinition.pages[pageId]; + + const pageEvents = page.events || []; + const componentEvents = []; + + const pagePostionIntheList = Object.keys(updatedDefinition?.pages).indexOf(pageId); + + const isHompage = (updatedDefinition['homePageId'] as any) === pageId; + + const pageComponents = page.components; + + const mappedComponents = transformComponentData( + pageComponents, + componentEvents, + appResourceMappings.componentsMapping + ); + + const componentLayouts = []; + + const newPage = manager.create(Page, { + name: page.name, + handle: page.handle, + appVersionId: appResourceMappings.appVersionMapping[importingAppVersion.id], + index: pagePostionIntheList, + disabled: page.disabled || false, + hidden: page.hidden || false, + }); + const pageCreated = await manager.save(newPage); + + appResourceMappings.pagesMapping[pageId] = pageCreated.id; + + mappedComponents.forEach((component) => { + component.page = pageCreated; + }); + + const savedComponents = await manager.save(Component, mappedComponents); + + for (const componentId in pageComponents) { + const componentLayout = pageComponents[componentId]['layouts']; + + if (componentLayout && appResourceMappings.componentsMapping[componentId]) { + for (const type in componentLayout) { + const layout = componentLayout[type]; + const newLayout = new Layout(); + newLayout.type = type; + newLayout.top = layout.top; + newLayout.left = layout.left; + newLayout.width = layout.width; + newLayout.height = layout.height; + newLayout.componentId = appResourceMappings.componentsMapping[componentId]; + + componentLayouts.push(newLayout); + } + } + } + + await manager.save(Layout, componentLayouts); + + //Event handlers + + if (pageEvents.length > 0) { + pageEvents.forEach(async (event, index) => { + const newEvent = { + name: event.eventId, + sourceId: pageCreated.id, + target: Target.page, + event: event, + index: pageEvents.index || index, + appVersionId: appResourceMappings.appVersionMapping[importingAppVersion.id], + }; + + await manager.save(EventHandler, newEvent); + }); + } + + componentEvents.forEach((eventObj) => { + if (eventObj.event?.length === 0) return; + + eventObj.event.forEach(async (event, index) => { + const newEvent = await manager.create(EventHandler, { + name: event.eventId, + sourceId: appResourceMappings.componentsMapping[eventObj.componentId], + target: Target.component, + event: event, + index: eventObj.index || index, + appVersionId: appResourceMappings.appVersionMapping[importingAppVersion.id], + }); + + await manager.save(EventHandler, newEvent); + }); + }); + + savedComponents.forEach(async (component) => { + if (component.type === 'Table') { + const tableActions = component.properties?.actions?.value || []; + const tableColumns = component.properties?.columns?.value || []; + + const tableActionAndColumnEvents = []; + + tableActions.forEach((action) => { + const actionEvents = action.events || []; + + actionEvents.forEach((event, index) => { + tableActionAndColumnEvents.push({ + name: event.eventId, + sourceId: component.id, + target: Target.tableAction, + event: { ...event, ref: action.name }, + index: event.index ?? index, + appVersionId: appResourceMappings.appVersionMapping[importingAppVersion.id], + }); + }); + }); + + tableColumns.forEach((column) => { + if (column?.columnType !== 'toggle') return; + const columnEvents = column.events || []; + + columnEvents.forEach((event, index) => { + tableActionAndColumnEvents.push({ + name: event.eventId, + sourceId: component.id, + target: Target.tableColumn, + event: { ...event, ref: column.name }, + index: event.index ?? index, + appVersionId: appResourceMappings.appVersionMapping[importingAppVersion.id], + }); + }); + }); + + await manager.save(EventHandler, tableActionAndColumnEvents); + } + }); + + if (isHompage) { + updateHomepageId = pageCreated.id; + } + } } - ); + + await manager.update( + AppVersion, + { id: appResourceMappings.appVersionMapping[importingAppVersion.id] }, + { + definition: updatedDefinition, + homePageId: updateHomepageId, + } + ); + + await this.updateEventActionsForNewVersionWithNewMappingIds( + manager, + appResourceMappings.appVersionMapping[importingAppVersion.id], + appResourceMappings.dataQueryMapping, + appResourceMappings.componentsMapping, + appResourceMappings.pagesMapping, + isNormalizedAppDefinitionSchema + ); + } + } + + if (isNormalizedAppDefinitionSchema) { + const appVersionIds = Object.values(appResourceMappings.appVersionMapping); + + for (const appVersionId of appVersionIds) { + await this.updateEventActionsForNewVersionWithNewMappingIds( + manager, + appVersionId, + appResourceMappings.dataQueryMapping, + appResourceMappings.componentsMapping, + appResourceMappings.pagesMapping, + isNormalizedAppDefinitionSchema + ); + } } await this.setEditingVersionAsLatestVersion(manager, appResourceMappings.appVersionMapping, importingAppVersions); + + return appResourceMappings; } async setupAppVersionAssociations( @@ -326,11 +574,17 @@ export class AppImportExportService { importingDataSources: DataSource[], importingDataSourceOptions: DataSourceOptions[], importingDataQueries: DataQuery[], - importingDefaultAppEnvironmentId: string + importingDefaultAppEnvironmentId: string, + importingPages: Page[], + importingComponents: Component[], + importingEvents: EventHandler[] ): Promise { appResourceMappings = { ...appResourceMappings }; for (const importingAppVersion of importingAppVersions) { + let isHomePage = false; + let updateHomepageId = null; + const { appEnvironmentMapping } = await this.associateAppEnvironmentsToAppVersion( manager, user, @@ -417,18 +671,185 @@ export class AppImportExportService { appResourceMappings.dataQueryMapping = dataQueryMapping; } + const pagesOfAppVersion = importingPages.filter((page) => page.appVersionId === importingAppVersion.id); + + for (const page of pagesOfAppVersion) { + const newPage = manager.create(Page, { + name: page.name, + handle: page.handle, + appVersionId: appResourceMappings.appVersionMapping[importingAppVersion.id], + index: page.index, + disabled: page.disabled || false, + hidden: page.hidden || false, + }); + + const pageCreated = await manager.save(newPage); + + appResourceMappings.pagesMapping[page.id] = pageCreated.id; + + isHomePage = importingAppVersion.homePageId === page.id; + + if (isHomePage) { + updateHomepageId = pageCreated.id; + } + + const pageComponents = importingComponents.filter((component) => component.pageId === page.id); + + const newComponentIdsMap = {}; + + for (const component of pageComponents) { + newComponentIdsMap[component.id] = uuid(); + } + + for (const component of pageComponents) { + let skipComponent = false; + const newComponent = new Component(); + + let parentId = component.parent ? component.parent : null; + + const isParentTabOrCalendar = isChildOfTabsOrCalendar(component, pageComponents, parentId); + + if (isParentTabOrCalendar) { + const childTabId = component.parent.split('-')[component.parent.split('-').length - 1]; + const _parentId = component?.parent?.split('-').slice(0, -1).join('-'); + const mappedParentId = newComponentIdsMap[_parentId]; + + parentId = `${mappedParentId}-${childTabId}`; + } else { + if (component.parent && !newComponentIdsMap[parentId]) { + skipComponent = true; + } + + parentId = newComponentIdsMap[parentId]; + } + + if (!skipComponent) { + newComponent.id = newComponentIdsMap[component.id]; + newComponent.name = component.name; + newComponent.type = component.type; + newComponent.properties = component.properties; + newComponent.styles = component.styles; + newComponent.validation = component.validation; + newComponent.parent = component.parent ? parentId : null; + + newComponent.page = pageCreated; + + const savedComponent = await manager.save(newComponent); + + appResourceMappings.componentsMapping[component.id] = savedComponent.id; + const componentLayout = component.layouts; + + componentLayout.forEach(async (layout) => { + const newLayout = new Layout(); + newLayout.type = layout.type; + newLayout.top = layout.top; + newLayout.left = layout.left; + newLayout.width = layout.width; + newLayout.height = layout.height; + newLayout.component = savedComponent; + + await manager.save(newLayout); + }); + + const componentEvents = importingEvents.filter((event) => event.sourceId === component.id); + + if (componentEvents.length > 0) { + componentEvents.forEach(async (componentEvent) => { + const newEvent = new EventHandler(); + newEvent.name = componentEvent.name; + newEvent.sourceId = savedComponent.id; + newEvent.target = componentEvent.target; + newEvent.event = componentEvent.event; + newEvent.index = componentEvent.index; + newEvent.appVersionId = appResourceMappings.appVersionMapping[importingAppVersion.id]; + + await manager.save(EventHandler, newEvent); + }); + } + } + } + + const pageEvents = importingEvents.filter((event) => event.sourceId === page.id); + + if (pageEvents.length > 0) { + pageEvents.forEach(async (pageEvent) => { + const newEvent = { + name: pageEvent.name, + sourceId: pageCreated.id, + target: pageEvent.target, + event: pageEvent.event, + index: pageEvent.index, + appVersionId: appResourceMappings.appVersionMapping[importingAppVersion.id], + }; + + await manager.save(EventHandler, newEvent); + }); + } + } + const newDataQueries = await manager.find(DataQuery, { where: { appVersionId: appResourceMappings.appVersionMapping[importingAppVersion.id] }, }); - for (const newQuery of newDataQueries) { - const newOptions = this.replaceDataQueryOptionsWithNewDataQueryIds( - newQuery.options, - appResourceMappings.dataQueryMapping + for (const importedDataQuery of importingDataQueriesForAppVersion) { + const mappedNewDataQuery = newDataQueries.find( + (dq) => dq.id === appResourceMappings.dataQueryMapping[importedDataQuery.id] ); - newQuery.options = newOptions; - await manager.save(newQuery); + + if (!mappedNewDataQuery) continue; + + const importingQueryEvents = importingEvents.filter( + (event) => event.target === Target.dataQuery && event.sourceId === importedDataQuery.id + ); + + if (importingQueryEvents.length > 0) { + importingQueryEvents.forEach(async (dataQueryEvent) => { + const newEvent = { + name: dataQueryEvent.name, + sourceId: mappedNewDataQuery.id, + target: dataQueryEvent.target, + event: dataQueryEvent.event, + index: dataQueryEvent.index, + appVersionId: appResourceMappings.appVersionMapping[importingAppVersion.id], + }; + + await manager.save(EventHandler, newEvent); + }); + } else { + this.replaceDataQueryOptionsWithNewDataQueryIds( + mappedNewDataQuery?.options, + appResourceMappings.dataQueryMapping + ); + const queryEvents = mappedNewDataQuery?.options?.events || []; + + delete mappedNewDataQuery?.options?.events; + + if (queryEvents.length > 0) { + queryEvents.forEach(async (event, index) => { + const newEvent = { + name: event.eventId, + sourceId: mappedNewDataQuery.id, + target: Target.dataQuery, + event: event, + index: queryEvents.index || index, + appVersionId: mappedNewDataQuery.appVersionId, + }; + + await manager.save(EventHandler, newEvent); + }); + } + } + + await manager.save(mappedNewDataQuery); } + + await manager.update( + AppVersion, + { id: appResourceMappings.appVersionMapping[importingAppVersion.id] }, + { + homePageId: updateHomepageId, + } + ); } return appResourceMappings; @@ -489,6 +910,7 @@ export class AppImportExportService { dataSourceId: dataSourceForAppVersion.id, appVersionId: appResourceMappings.appVersionMapping[importingAppVersion.id], }); + await manager.save(newQuery); appResourceMappings.dataQueryMapping[importingQuery.id] = newQuery.id; } @@ -718,7 +1140,8 @@ export class AppImportExportService { user: User, importedApp: App, appVersions: AppVersion[], - appResourceMappings: AppResourceMappings + appResourceMappings: AppResourceMappings, + isNormalizedAppDefinitionSchema: boolean ) { appResourceMappings = { ...appResourceMappings }; const { appVersionMapping, appDefaultEnvironmentMapping } = appResourceMappings; @@ -739,7 +1162,7 @@ export class AppImportExportService { currentEnvironmentId = organization.appEnvironments.find((env) => env.priority === 1)?.id; } - const version = manager.create(AppVersion, { + const version = await manager.create(AppVersion, { appId: importedApp.id, definition: appVersion.definition, name: appVersion.name, @@ -747,6 +1170,30 @@ export class AppImportExportService { createdAt: new Date(), updatedAt: new Date(), }); + + if (isNormalizedAppDefinitionSchema) { + version.showViewerNavigation = appVersion.showViewerNavigation; + version.homePageId = appVersion.homePageId; + version.globalSettings = appVersion.globalSettings; + } else { + version.showViewerNavigation = appVersion.definition.showViewerNavigation || true; + version.homePageId = appVersion.definition?.homePageId; + + if (!appVersion.definition?.globalSettings) { + version.globalSettings = { + hideHeader: false, + appInMaintenance: false, + canvasMaxWidth: 100, + canvasMaxWidthType: '%', + canvasMaxHeight: 2400, + canvasBackgroundColor: '#edeff5', + backgroundFxQuery: '', + }; + } else { + version.globalSettings = appVersion.definition?.globalSettings; + } + } + await manager.save(version); appDefaultEnvironmentMapping[appVersion.id] = appEnvIds; @@ -1031,8 +1478,24 @@ export class AppImportExportService { for (const newQuery of newDataQueries) { const newOptions = this.replaceDataQueryOptionsWithNewDataQueryIds(newQuery.options, dataQueryMapping); + const queryEvents = newQuery.options?.events || []; + delete newOptions?.events; + newQuery.options = newOptions; await manager.save(newQuery); + + queryEvents.forEach(async (event, index) => { + const newEvent = { + name: event.eventId, + sourceId: newQuery.id, + target: Target.dataQuery, + event: event, + index: queryEvents.index || index, + appVersionId: newQuery.appVersionId, + }; + + await manager.save(EventHandler, newEvent); + }); } await manager.update( @@ -1045,6 +1508,43 @@ export class AppImportExportService { replaceTooljetDbTableIds(queryOptions: any, tooljetDatabaseMapping: any) { return { ...queryOptions, table_id: tooljetDatabaseMapping[queryOptions.table_id]?.id }; } + + async updateEventActionsForNewVersionWithNewMappingIds( + manager: EntityManager, + versionId: string, + oldDataQueryToNewMapping: Record, + oldComponentToNewComponentMapping: Record, + oldPageToNewPageMapping: Record, + isNormalizedAppDefinitionSchema: boolean + ) { + const allEvents = await manager.find(EventHandler, { + where: { appVersionId: versionId }, + }); + + for (const event of allEvents) { + const eventDefinition = event.event; + + if (isNormalizedAppDefinitionSchema && eventDefinition?.actionId === 'run-query') { + eventDefinition.queryId = oldDataQueryToNewMapping[eventDefinition.queryId]; + } + + if (eventDefinition?.actionId === 'control-component') { + eventDefinition.componentId = oldComponentToNewComponentMapping[eventDefinition.componentId]; + } + + if (eventDefinition?.actionId === 'switch-page') { + eventDefinition.pageId = oldPageToNewPageMapping[eventDefinition.pageId]; + } + + if (eventDefinition?.actionId == 'show-modal' || eventDefinition?.actionId === 'close-modal') { + eventDefinition.modal = oldComponentToNewComponentMapping[eventDefinition.modal]; + } + + event.event = eventDefinition; + + await manager.save(event); + } + } } function convertSinglePageSchemaToMultiPageSchema(appParams: any) { @@ -1057,3 +1557,84 @@ function convertSinglePageSchemaToMultiPageSchema(appParams: any) { }; return appParamsWithMultipageSchema; } + +function transformComponentData( + data: object, + componentEvents: any[], + componentsMapping: Record +): Component[] { + const transformedComponents: Component[] = []; + + const allComponents = Object.keys(data).map((key) => { + return { + id: key, + ...data[key], + }; + }); + + for (const componentId in data) { + const component = data[componentId]; + const componentData = component['component']; + + let skipComponent = false; + const transformedComponent: Component = new Component(); + + let parentId = component.parent ? component.parent : null; + + const isParentTabOrCalendar = isChildOfTabsOrCalendar(component, allComponents, parentId); + + if (isParentTabOrCalendar) { + const childTabId = component.parent.split('-')[component.parent.split('-').length - 1]; + const _parentId = component?.parent?.split('-').slice(0, -1).join('-'); + const mappedParentId = componentsMapping[_parentId]; + + parentId = `${mappedParentId}-${childTabId}`; + } else { + if (component.parent && !componentsMapping[parentId]) { + skipComponent = true; + } + parentId = componentsMapping[parentId]; + } + + if (!skipComponent) { + transformedComponent.id = uuid(); + transformedComponent.name = componentData.name; + transformedComponent.type = componentData.component; + transformedComponent.properties = componentData.definition.properties || {}; + transformedComponent.styles = componentData.definition.styles || {}; + transformedComponent.validation = componentData.definition.validation || {}; + transformedComponent.general = componentData.definition.general || {}; + transformedComponent.generalStyles = componentData.definition.generalStyles || {}; + transformedComponent.displayPreferences = componentData.definition.others || {}; + transformedComponent.parent = component.parent ? parentId : null; + + transformedComponents.push(transformedComponent); + + componentEvents.push({ + componentId: componentId, + event: componentData.definition.events, + }); + componentsMapping[componentId] = transformedComponent.id; + } + } + + // if (skippedComponents.length) { + + // } + + return transformedComponents; +} + +const isChildOfTabsOrCalendar = (component, allComponents = [], componentParentId = undefined) => { + if (componentParentId) { + const parentId = component?.parent?.split('-').slice(0, -1).join('-'); + + const parentComponent = allComponents.find((comp) => comp.id === parentId); + + if (parentComponent) { + return parentComponent.type === 'Tabs' || parentComponent.type === 'Calendar'; + } + } + + return false; +}; diff --git a/server/src/services/apps.service.ts b/server/src/services/apps.service.ts index 223e6c0d8e..4f3727e82b 100644 --- a/server/src/services/apps.service.ts +++ b/server/src/services/apps.service.ts @@ -22,7 +22,14 @@ import { AppEnvironmentService } from './app_environments.service'; import { decode } from 'js-base64'; import { DataSourceScopes } from 'src/helpers/data_source.constants'; import { DataBaseConstraints } from 'src/helpers/db_constraints.constants'; +import { Page } from 'src/entities/page.entity'; +import { AppVersionUpdateDto } from '@dto/app-version-update.dto'; +import { Layout } from 'src/entities/layout.entity'; +import { Component } from 'src/entities/component.entity'; +import { EventHandler } from 'src/entities/event_handler.entity'; + +const uuid = require('uuid'); @Injectable() export class AppsService { constructor( @@ -117,7 +124,30 @@ export class AppsService { ); //create default app version - await this.createVersion(user, app, 'v1', null, null, manager); + const appVersion = await this.createVersion(user, app, 'v1', null, null, manager); + + const defaultHomePage = await manager.save( + manager.create(Page, { + name: 'Home', + handle: 'home', + appVersionId: appVersion.id, + index: 1, + }) + ); + + // Set default values for app version + appVersion.showViewerNavigation = true; + appVersion.homePageId = defaultHomePage.id; + appVersion.globalSettings = { + hideHeader: false, + appInMaintenance: false, + canvasMaxWidth: 100, + canvasMaxWidthType: '%', + canvasMaxHeight: 2400, + canvasBackgroundColor: '#edeff5', + backgroundFxQuery: '', + }; + await manager.save(appVersion); await manager.save( manager.create(AppUser, { @@ -345,11 +375,221 @@ export class AppsService { }) ); - await this.createNewDataSourcesAndQueriesForVersion(manager, appVersion, versionFrom, organizationId); + if (versionFrom) { + (appVersion.showViewerNavigation = versionFrom.showViewerNavigation), + (appVersion.globalSettings = versionFrom.globalSettings), + await manager.save(appVersion); + + const oldDataQueryToNewMapping = await this.createNewDataSourcesAndQueriesForVersion( + manager, + appVersion, + versionFrom, + organizationId + ); + + const { oldComponentToNewComponentMapping, oldPageToNewPageMapping } = + await this.createNewPagesAndComponentsForVersion(manager, appVersion, versionFrom.id, versionFrom.homePageId); + + await this.updateEventActionsForNewVersionWithNewMappingIds( + manager, + appVersion.id, + oldDataQueryToNewMapping, + oldComponentToNewComponentMapping, + oldPageToNewPageMapping + ); + } + return appVersion; }, manager); } + async updateEventActionsForNewVersionWithNewMappingIds( + manager: EntityManager, + versionId: string, + oldDataQueryToNewMapping: Record, + oldComponentToNewComponentMapping: Record, + oldPageToNewPageMapping: Record + ) { + const allEvents = await manager.find(EventHandler, { + where: { appVersionId: versionId }, + }); + + for (const event of allEvents) { + const eventDefinition = event.event; + + if (eventDefinition?.actionId === 'run-query') { + eventDefinition.queryId = oldDataQueryToNewMapping[eventDefinition.queryId]; + } + + if (eventDefinition?.actionId === 'control-component') { + eventDefinition.componentId = oldComponentToNewComponentMapping[eventDefinition.componentId]; + } + + if (eventDefinition?.actionId === 'switch-page') { + eventDefinition.pageId = oldPageToNewPageMapping[eventDefinition.pageId]; + } + + if (eventDefinition?.actionId == 'show-modal' || eventDefinition?.actionId === 'close-modal') { + eventDefinition.modal = oldComponentToNewComponentMapping[eventDefinition.modal]; + } + + event.event = eventDefinition; + + await manager.save(event); + } + } + + async createNewPagesAndComponentsForVersion( + manager: EntityManager, + appVersion: AppVersion, + versionFromId: string, + prevHomePagePage: string + ) { + const pages = await manager + .createQueryBuilder(Page, 'page') + .leftJoinAndSelect('page.components', 'component') + .leftJoinAndSelect('component.layouts', 'layout') + .where('page.appVersionId = :appVersionId', { appVersionId: versionFromId }) + .getMany(); + + const allEvents = await manager.find(EventHandler, { + where: { appVersionId: versionFromId }, + }); + + let homePageId = prevHomePagePage; + + const newComponents = []; + const newComponentLayouts = []; + const oldComponentToNewComponentMapping = {}; + const oldPageToNewPageMapping = {}; + + const isChildOfTabsOrCalendar = (component, allComponents = [], componentParentId = undefined) => { + if (componentParentId) { + const parentId = component?.parent?.split('-').slice(0, -1).join('-'); + + const parentComponent = allComponents.find((comp) => comp.id === parentId); + + if (parentComponent) { + return parentComponent.type === 'Tabs' || parentComponent.type === 'Calendar'; + } + } + + return false; + }; + + for (const page of pages) { + const savedPage = await manager.save( + manager.create(Page, { + name: page.name, + handle: page.handle, + index: page.index, + disabled: page.disabled, + hidden: page.hidden, + appVersionId: appVersion.id, + }) + ); + oldPageToNewPageMapping[page.id] = savedPage.id; + if (page.id === prevHomePagePage) { + homePageId = savedPage.id; + } + + const pageEvents = allEvents.filter((event) => event.sourceId === page.id); + + pageEvents.forEach(async (event, index) => { + const newEvent = new EventHandler(); + + newEvent.id = uuid.v4(); + newEvent.name = event.name; + newEvent.sourceId = savedPage.id; + newEvent.target = event.target; + newEvent.event = event.event; + newEvent.index = event.index ?? index; + newEvent.appVersionId = appVersion.id; + + await manager.save(newEvent); + }); + + page.components.forEach(async (component) => { + const newComponent = new Component(); + const componentEvents = allEvents.filter((event) => event.sourceId === component.id); + + newComponent.id = uuid.v4(); + + oldComponentToNewComponentMapping[component.id] = newComponent.id; + + newComponent.name = component.name; + newComponent.type = component.type; + newComponent.pageId = savedPage.id; + newComponent.properties = component.properties; + newComponent.styles = component.styles; + newComponent.validation = component.validation; + newComponent.general = component.general; + newComponent.generalStyles = component.generalStyles; + newComponent.displayPreferences = component.displayPreferences; + newComponent.parent = component.parent; + newComponent.page = savedPage; + + newComponents.push(newComponent); + + component.layouts.forEach((layout) => { + const newLayout = new Layout(); + newLayout.id = uuid.v4(); + newLayout.type = layout.type; + newLayout.top = layout.top; + newLayout.left = layout.left; + newLayout.width = layout.width; + newLayout.height = layout.height; + newLayout.componentId = layout.componentId; + + newLayout.component = newComponent; + + newComponentLayouts.push(newLayout); + }); + + componentEvents.forEach(async (event, index) => { + const newEvent = new EventHandler(); + + newEvent.id = uuid.v4(); + newEvent.name = event.name; + newEvent.sourceId = newComponent.id; + newEvent.target = event.target; + newEvent.event = event.event; + newEvent.index = event.index ?? index; + newEvent.appVersionId = appVersion.id; + + await manager.save(newEvent); + }); + }); + + newComponents.forEach((component) => { + let parentId = component.parent ? component.parent : null; + + if (!parentId) return; + + const isParentTabOrCalendar = isChildOfTabsOrCalendar(component, page.components, parentId); + + if (isParentTabOrCalendar) { + const childTabId = component.parent.split('-')[component.parent.split('-').length - 1]; + const _parentId = component?.parent?.split('-').slice(0, -1).join('-'); + const mappedParentId = oldComponentToNewComponentMapping[_parentId]; + + parentId = `${mappedParentId}-${childTabId}`; + } else { + parentId = oldComponentToNewComponentMapping[parentId]; + } + + component.parent = parentId; + }); + + await manager.save(newComponents); + await manager.save(newComponentLayouts); + } + + await manager.update(AppVersion, { id: appVersion.id }, { homePageId }); + + return { oldComponentToNewComponentMapping, oldPageToNewPageMapping }; + } + async deleteVersion(app: App, version: AppVersion): Promise { if (app.currentVersionId === version.id) { throw new BadRequestException('You cannot delete a released version'); @@ -399,8 +639,11 @@ export class AppsService { const dataSources = versionFrom?.dataSources; const dataSourceMapping = {}; const newDataQueries = []; + const allEvents = await manager.find(EventHandler, { + where: { appVersionId: versionFrom?.id, target: 'data_query' }, + }); - if (dataSources?.length) { + if (dataSources?.length > 0) { for (const dataSource of dataSources) { const dataSourceParams: Partial = { name: dataSource.name, @@ -420,64 +663,92 @@ export class AppsService { dataSourceId: newDataSource.id, appVersionId: appVersion.id, }; - const newQuery = await manager.save(manager.create(DataQuery, dataQueryParams)); + + const dataQueryEvents = allEvents.filter((event) => event.sourceId === dataQuery.id); + + dataQueryEvents.forEach(async (event, index) => { + const newEvent = new EventHandler(); + + newEvent.id = uuid.v4(); + newEvent.name = event.name; + newEvent.sourceId = newQuery.id; + newEvent.target = event.target; + newEvent.event = event.event; + newEvent.index = event.index ?? index; + newEvent.appVersionId = appVersion.id; + + await manager.save(newEvent); + }); + oldDataQueryToNewMapping[dataQuery.id] = newQuery.id; newDataQueries.push(newQuery); } } + } - if (globalQueries?.length) { - for (const globalQuery of globalQueries) { - const dataQueryParams = { - name: globalQuery.name, - options: globalQuery.options, - dataSourceId: globalQuery.dataSourceId, - appVersionId: appVersion.id, - }; + if (globalQueries?.length > 0) { + for (const globalQuery of globalQueries) { + const dataQueryParams = { + name: globalQuery.name, + options: globalQuery.options, + dataSourceId: globalQuery.dataSourceId, + appVersionId: appVersion.id, + }; - const newQuery = await manager.save(manager.create(DataQuery, dataQueryParams)); - oldDataQueryToNewMapping[globalQuery.id] = newQuery.id; - newDataQueries.push(newQuery); - } + const newQuery = await manager.save(manager.create(DataQuery, dataQueryParams)); + const dataQueryEvents = allEvents.filter((event) => event.sourceId === globalQuery.id); + + dataQueryEvents.forEach(async (event, index) => { + const newEvent = new EventHandler(); + + newEvent.id = uuid.v4(); + newEvent.name = event.name; + newEvent.sourceId = newQuery.id; + newEvent.target = event.target; + newEvent.event = event.event; + newEvent.index = event.index ?? index; + newEvent.appVersionId = appVersion.id; + + await manager.save(newEvent); + }); + oldDataQueryToNewMapping[globalQuery.id] = newQuery.id; + newDataQueries.push(newQuery); } + } - for (const newQuery of newDataQueries) { - const newOptions = this.replaceDataQueryOptionsWithNewDataQueryIds( - newQuery.options, - oldDataQueryToNewMapping + for (const newQuery of newDataQueries) { + const newOptions = this.replaceDataQueryOptionsWithNewDataQueryIds(newQuery.options, oldDataQueryToNewMapping); + newQuery.options = newOptions; + + await manager.save(newQuery); + } + + appVersion.definition = this.replaceDataQueryIdWithinDefinitions(appVersion.definition, oldDataQueryToNewMapping); + await manager.save(appVersion); + + for (const appEnvironment of appEnvironments) { + for (const dataSource of dataSources) { + const dataSourceOption = await manager.findOneOrFail(DataSourceOptions, { + where: { dataSourceId: dataSource.id, environmentId: appEnvironment.id }, + }); + + const convertedOptions = this.convertToArrayOfKeyValuePairs(dataSourceOption.options); + const newOptions = await this.dataSourcesService.parseOptionsForCreate(convertedOptions, false, manager); + await this.setNewCredentialValueFromOldValue(newOptions, convertedOptions, manager); + + await manager.save( + manager.create(DataSourceOptions, { + options: newOptions, + dataSourceId: dataSourceMapping[dataSource.id], + environmentId: appEnvironment.id, + }) ); - newQuery.options = newOptions; - await manager.save(newQuery); - } - - appVersion.definition = this.replaceDataQueryIdWithinDefinitions( - appVersion.definition, - oldDataQueryToNewMapping - ); - await manager.save(appVersion); - - for (const appEnvironment of appEnvironments) { - for (const dataSource of dataSources) { - const dataSourceOption = await manager.findOneOrFail(DataSourceOptions, { - where: { dataSourceId: dataSource.id, environmentId: appEnvironment.id }, - }); - - const convertedOptions = this.convertToArrayOfKeyValuePairs(dataSourceOption.options); - const newOptions = await this.dataSourcesService.parseOptionsForCreate(convertedOptions, false, manager); - await this.setNewCredentialValueFromOldValue(newOptions, convertedOptions, manager); - - await manager.save( - manager.create(DataSourceOptions, { - options: newOptions, - dataSourceId: dataSourceMapping[dataSource.id], - environmentId: appEnvironment.id, - }) - ); - } } } } + + return oldDataQueryToNewMapping; } private async createEnvironments(appEnvironments: any[], manager: EntityManager, organizationId: string) { @@ -643,6 +914,31 @@ export class AppsService { return await this.appVersionsRepository.update(version.id, editableParams); } + async updateAppVersion(version: AppVersion, body: AppVersionUpdateDto) { + const editableParams = {}; + + const { globalSettings, homePageId } = await this.appVersionsRepository.findOne({ + where: { id: version.id }, + }); + + if (body?.homePageId && homePageId !== body.homePageId) { + editableParams['homePageId'] = body.homePageId; + } + + if (body?.globalSettings) { + editableParams['globalSettings'] = { + ...globalSettings, + ...body.globalSettings, + }; + } + + if (typeof body?.showViewerNavigation === 'boolean') { + editableParams['showViewerNavigation'] = body.showViewerNavigation; + } + + return await this.appVersionsRepository.update(version.id, editableParams); + } + convertToArrayOfKeyValuePairs(options): Array { if (!options) return; return Object.keys(options).map((key) => { diff --git a/server/src/services/components.service.ts b/server/src/services/components.service.ts new file mode 100644 index 0000000000..713a885f81 --- /dev/null +++ b/server/src/services/components.service.ts @@ -0,0 +1,254 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { EntityManager, Repository } from 'typeorm'; +import { Component } from 'src/entities/component.entity'; +import { Layout } from 'src/entities/layout.entity'; +import { Page } from 'src/entities/page.entity'; +import { dbTransactionForAppVersionAssociationsUpdate, dbTransactionWrap } from 'src/helpers/utils.helper'; + +import { EventsService } from './events_handler.service'; +import { LayoutData } from '@dto/component.dto'; + +@Injectable() +export class ComponentsService { + constructor( + private eventHandlerService: EventsService, + + @InjectRepository(Component) + private componentsRepository: Repository + ) {} + + async findOne(id: string): Promise { + return this.componentsRepository.findOne(id); + } + + async create(componentDiff: object, pageId: string, appVersionId: string) { + return dbTransactionForAppVersionAssociationsUpdate(async (manager: EntityManager) => { + const page = await manager.findOne(Page, { + where: { appVersionId, id: pageId }, + }); + + const newComponents = this.transformComponentData(componentDiff); + + const componentLayouts = []; + + newComponents.forEach((component) => { + component.page = page; + }); + + const savedComponents = await manager.save(Component, newComponents); + + savedComponents.forEach((component) => { + const componentLayout = componentDiff[component.id].layouts; + + if (componentLayout) { + for (const type in componentLayout) { + const layout = componentLayout[type]; + const newLayout = new Layout(); + newLayout.type = type; + newLayout.top = layout.top; + newLayout.left = layout.left; + newLayout.width = layout.width; + newLayout.height = layout.height; + newLayout.component = component; + + componentLayouts.push(newLayout); + } + } + }); + + await manager.save(Layout, componentLayouts); + + return {}; + }, appVersionId); + } + + async update(componentDiff: object, appVersionId: string) { + return dbTransactionForAppVersionAssociationsUpdate(async (manager) => { + for (const componentId in componentDiff) { + const { component } = componentDiff[componentId]; + + const componentData = await manager.findOne(Component, componentId); + + if (!componentData) { + return { + error: { + message: `Component with id ${componentId} does not exist`, + }, + }; + } + + const isComponentDefinitionChanged = component.definition ? true : false; + + if (isComponentDefinitionChanged) { + const updatedDefinition = component.definition; + const columnsUpdated = Object.keys(updatedDefinition); + + const newComponentsData = columnsUpdated.reduce((acc, column) => { + const newColumnData = { + ...componentData[column], + ...updatedDefinition[column], + }; + + if (column === 'others') { + acc['displayPreferences'] = newColumnData; + } else { + acc[column] = newColumnData; + } + + return acc; + }, {}); + + await manager.update(Component, componentId, newComponentsData); + return; + } + + await manager.update(Component, componentId, component); + + return; + } + }, appVersionId); + } + + async delete(componentIds: string[], appVersionId: string, isComponentCut = false) { + return dbTransactionForAppVersionAssociationsUpdate(async (manager: EntityManager) => { + const components = await manager.findByIds(Component, componentIds); + + if (!components.length) { + return { + error: { + message: `Components with ids ${componentIds} do not exist`, + }, + }; + } + + if (!isComponentCut) { + components.forEach((component) => { + this.eventHandlerService.cascadeDeleteEvents(component.id); + }); + } + + await manager.delete(Component, componentIds); + }, appVersionId); + } + + async componentLayoutChange(componenstLayoutDiff: Record, appVersionId: string) { + return dbTransactionForAppVersionAssociationsUpdate(async (manager: EntityManager) => { + for (const componentId in componenstLayoutDiff) { + const doesComponentExist = await manager.findAndCount(Component, { id: componentId }); + + if (!doesComponentExist[1]) { + return { + error: { + message: `Component with id ${componentId} does not exist`, + }, + }; + } + + const { layouts } = componenstLayoutDiff[componentId]; + + for (const type in layouts) { + const componentLayout = await manager.findOne(Layout, { componentId, type }); + + if (componentLayout) { + const layout = { + ...layouts[type], + } as Partial; + + await manager.update(Layout, { id: componentLayout.id }, layout); + } + } + } + }, appVersionId); + } + + async getAllComponents(pageId: string) { + // need to get all components for a page with their layouts + + return dbTransactionWrap(async (manager: EntityManager) => { + return manager + .createQueryBuilder(Component, 'component') + .leftJoinAndSelect('component.layouts', 'layout') + .where('component.pageId = :pageId', { pageId }) + .getMany() + .then((components) => { + return components.reduce((acc, component) => { + const componentId = component.id; + const componentData = component; + const componentLayout = component.layouts; + + const transformedData = this.createComponentWithLayout(componentData, componentLayout); + + acc[componentId] = transformedData[componentId]; + + return acc; + }, {}); + }); + }); + } + + transformComponentData(data: object): Component[] { + const transformedComponents: Component[] = []; + + for (const componentId in data) { + const componentData = data[componentId]; + + const transformedComponent: Component = new Component(); + transformedComponent.id = componentId; + transformedComponent.name = componentData.name; + transformedComponent.type = componentData.type; + transformedComponent.parent = componentData.parent || null; + transformedComponent.properties = componentData.properties || {}; + transformedComponent.styles = componentData.styles || {}; + transformedComponent.validation = componentData.validation || {}; + transformedComponent.displayPreferences = componentData.others || null; + transformedComponent.general = componentData.general || null; + transformedComponent.generalStyles = componentData.generalStyles || null; + + transformedComponents.push(transformedComponent); + } + + return transformedComponents; + } + + createComponentWithLayout(componentData: Component, layoutData = []) { + const { id, name, properties, styles, generalStyles, validation, parent, displayPreferences, general } = + componentData; + + const layouts = {}; + + layoutData.forEach((layout) => { + const { type, top, left, width, height } = layout; + + layouts[type] = { + top, + left, + width, + height, + }; + }); + + const componentWithLayout = { + [id]: { + component: { + name, + component: componentData.type, + definition: { + properties, + styles, + generalStyles, + validation, + general, + others: displayPreferences, + }, + parent, + }, + layouts: { + ...layouts, + }, + }, + }; + + return componentWithLayout; + } +} diff --git a/server/src/services/data_queries.service.ts b/server/src/services/data_queries.service.ts index 26572067c3..16cc796238 100644 --- a/server/src/services/data_queries.service.ts +++ b/server/src/services/data_queries.service.ts @@ -15,6 +15,7 @@ import { App } from 'src/entities/app.entity'; import { AppEnvironmentService } from './app_environments.service'; import { dbTransactionWrap } from 'src/helpers/utils.helper'; import { DataSourceScopes } from 'src/helpers/data_source.constants'; +import { EventHandler } from 'src/entities/event_handler.entity'; @Injectable() export class DataQueriesService { @@ -74,9 +75,21 @@ export class DataQueriesService { } async delete(dataQueryId: string) { + await this.deleteDataQueryEvents(dataQueryId); + return await this.dataQueriesRepository.delete(dataQueryId); } + async deleteDataQueryEvents(dataQueryId: string) { + return await dbTransactionWrap(async (manager: EntityManager) => { + const allEvents = await manager.find(EventHandler, { + where: { sourceId: dataQueryId }, + }); + + return await manager.remove(allEvents); + }); + } + async update(dataQueryId: string, name: string, options: object): Promise { const dataQuery = this.dataQueriesRepository.save({ id: dataQueryId, diff --git a/server/src/services/events_handler.service.ts b/server/src/services/events_handler.service.ts new file mode 100644 index 0000000000..be4216fbeb --- /dev/null +++ b/server/src/services/events_handler.service.ts @@ -0,0 +1,137 @@ +import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { EntityManager, Repository } from 'typeorm'; +import { EventHandler } from 'src/entities/event_handler.entity'; +import { dbTransactionWrap, dbTransactionForAppVersionAssociationsUpdate } from 'src/helpers/utils.helper'; +import { CreateEventHandlerDto, UpdateEvent } from '@dto/event-handler.dto'; + +@Injectable() +export class EventsService { + constructor( + @InjectRepository(EventHandler) + private eventsRepository: Repository + ) {} + + async findEventsForVersion(appVersionId: string): Promise { + return dbTransactionWrap(async (manager: EntityManager) => { + const allEvents = await manager.find(EventHandler, { + where: { appVersionId }, + }); + return allEvents; + }); + } + + async findAllEventsWithSourceId(sourceId: string): Promise { + return this.eventsRepository.find({ + where: { sourceId }, + }); + } + + async cascadeDeleteEvents(sourceId: string) { + return dbTransactionWrap(async (manager: EntityManager) => { + const allEvents = await manager.find(EventHandler, { + where: { sourceId }, + }); + + return await manager.remove(allEvents); + }); + } + + async createEvent(eventHandler: CreateEventHandlerDto, versionId) { + if (!eventHandler.attachedTo) { + throw new BadRequestException('No attachedTo found'); + } + + if (!eventHandler.eventType) { + throw new BadRequestException('No eventType found'); + } + + if (!eventHandler.event) { + throw new BadRequestException('No event found'); + } + + return await dbTransactionForAppVersionAssociationsUpdate(async (manager: EntityManager) => { + const newEvent = new EventHandler(); + newEvent.name = eventHandler.event.eventId; + newEvent.sourceId = eventHandler.attachedTo; + newEvent.target = eventHandler.eventType; + newEvent.event = eventHandler.event; + newEvent.index = eventHandler.index; + newEvent.appVersionId = versionId; + + const event = await manager.save(EventHandler, newEvent); + return event; + }, versionId); + } + + async updateEvent(events: UpdateEvent[], updateType: 'update' | 'reorder', appVersionId: string) { + return await dbTransactionForAppVersionAssociationsUpdate(async (manager: EntityManager) => { + return await Promise.all( + events.map(async (event) => { + const { event_id, diff } = event; + + const eventDiff = diff?.event; + const eventToUpdate = await manager.findOne(EventHandler, { + where: { id: event_id }, + }); + + if (!eventToUpdate) { + return new BadRequestException('No event found'); + } + + const updatedEvent = { + ...eventToUpdate, + }; + + if (updateType === 'update') { + updatedEvent.name = eventDiff?.eventId; + updatedEvent.event = eventDiff; + } + + if (updateType === 'reorder') { + updatedEvent.index = diff.index; + } + + return await manager.save(EventHandler, updatedEvent); + }) + ); + }, appVersionId); + } + + async updateEventsOrderOnDelete(sourceId: string, deletedIndex: number) { + const allEvents = await this.findAllEventsWithSourceId(sourceId); + + const eventsToUpdate = allEvents.filter((event) => event.index > deletedIndex); + + return await dbTransactionWrap(async (manager: EntityManager) => { + return await Promise.all( + eventsToUpdate.map(async (event) => { + return await manager.update(EventHandler, { id: event.id }, { index: event.index - 1 }); + }) + ); + }); + } + + async deleteEvent(eventId: string, appVersionId: string) { + return await dbTransactionForAppVersionAssociationsUpdate(async (manager: EntityManager) => { + const event = await manager.findOne(EventHandler, { + where: { id: eventId }, + }); + + const sourceId = event.sourceId; + const deletedIndex = event.index; + + if (!event) { + return new BadRequestException('No event found'); + } + + const deleteResponse = await manager.delete(EventHandler, event.id); + + if (!deleteResponse?.affected) { + throw new NotFoundException(); + } + await this.updateEventsOrderOnDelete(sourceId, deletedIndex); + return deleteResponse; + }, appVersionId); + } +} diff --git a/server/src/services/import_export_resources.service.ts b/server/src/services/import_export_resources.service.ts index 30e6740064..d320e89156 100644 --- a/server/src/services/import_export_resources.service.ts +++ b/server/src/services/import_export_resources.service.ts @@ -67,7 +67,9 @@ export class ImportExportResourcesService { appImportDto.appName, { tooljet_database: tableNameMapping, - } + }, + importResourcesDto.tooljet_version, + cloning ); imports.app.push({ id: createdApp.id, name: createdApp.name }); } diff --git a/server/src/services/page.service.ts b/server/src/services/page.service.ts new file mode 100644 index 0000000000..805e7ea1c9 --- /dev/null +++ b/server/src/services/page.service.ts @@ -0,0 +1,288 @@ +import { Injectable } from '@nestjs/common'; +import { EntityManager, Repository } from 'typeorm'; +import { InjectRepository } from '@nestjs/typeorm'; + +import { Page } from 'src/entities/page.entity'; +import { ComponentsService } from './components.service'; +import { CreatePageDto, UpdatePageDto } from '@dto/pages.dto'; +import { AppsService } from './apps.service'; +import { dbTransactionWrap, dbTransactionForAppVersionAssociationsUpdate } from 'src/helpers/utils.helper'; +import { EventsService } from './events_handler.service'; +import { Component } from 'src/entities/component.entity'; +import { Layout } from 'src/entities/layout.entity'; +import { EventHandler } from 'src/entities/event_handler.entity'; + +@Injectable() +export class PageService { + constructor( + @InjectRepository(Page) + private readonly pageRepository: Repository, + + private componentsService: ComponentsService, + private eventHandlerService: EventsService, + private appService: AppsService + ) {} + + async findPagesForVersion(appVersionId: string): Promise { + const allPages = await this.pageRepository.find({ appVersionId }); + + const pagesWithComponents = await Promise.all( + allPages.map(async (page) => { + const components = await this.componentsService.getAllComponents(page.id); + delete page.appVersionId; + return { ...page, components }; + }) + ); + + return pagesWithComponents; + } + + async findOne(id: string): Promise { + return this.pageRepository.findOne(id); + } + + async createPage(page: CreatePageDto, appVersionId: string): Promise { + return dbTransactionForAppVersionAssociationsUpdate(async (manager) => { + const newPage = new Page(); + newPage.id = page.id; + newPage.name = page.name; + newPage.handle = page.handle; + newPage.index = page.index; + newPage.appVersionId = appVersionId; + + return await manager.save(Page, newPage); + }, appVersionId); + } + + async clonePage(pageId: string, appVersionId: string) { + return dbTransactionForAppVersionAssociationsUpdate(async (manager) => { + const pageToClone = await manager.findOne(Page, pageId); + + if (!pageToClone) { + throw new Error('Page not found'); + } + + let pageName = `${pageToClone.name} (copy)`; + let pageHandle = `${pageToClone.handle}-copy`; + + const allPages = await this.pageRepository.find({ appVersionId }); + + const pageNameORHandleExists = allPages.filter((page) => { + return page.name.includes(pageName) || page.handle.includes(pageHandle); + }); + + if (pageNameORHandleExists.length > 0) { + pageName = `${pageToClone.name} (copy ${pageNameORHandleExists.length})`; + pageHandle = `${pageToClone.handle}-copy-${pageNameORHandleExists.length}`; + } + + const newPage = new Page(); + newPage.name = pageName; + newPage.handle = pageHandle; + newPage.index = pageToClone.index + 1; + newPage.appVersionId = appVersionId; + + const clonedpage = await this.pageRepository.save(newPage); + + await this.clonePageEventsAndComponents(pageId, clonedpage.id); + + const pages = await this.findPagesForVersion(appVersionId); + const events = await this.eventHandlerService.findEventsForVersion(appVersionId); + + return { pages, events }; + }, appVersionId); + } + + async clonePageEventsAndComponents(pageId: string, clonePageId: string) { + return dbTransactionWrap(async (manager: EntityManager) => { + const pageComponents = await manager.find(Component, { pageId }); + const pageEvents = await this.eventHandlerService.findAllEventsWithSourceId(pageId); + const componentsIdMap = {}; + + // Clone events + await Promise.all( + pageEvents.map(async (event) => { + const eventDefinition = event.event; + + if (eventDefinition?.actionId === 'control-component') { + eventDefinition.componentId = componentsIdMap[eventDefinition.componentId]; + } + + if (eventDefinition?.actionId == 'show-modal' || eventDefinition?.actionId === 'hide-modal') { + eventDefinition.modal = componentsIdMap[eventDefinition.modal]; + } + + event.event = eventDefinition; + + const clonedEvent = new EventHandler(); + clonedEvent.event = event.event; + clonedEvent.index = event.index; + clonedEvent.name = event.name; + clonedEvent.sourceId = clonePageId; + clonedEvent.target = event.target; + clonedEvent.appVersionId = event.appVersionId; + + await manager.save(EventHandler, clonedEvent); + }) + ); + + // Clone components + const clonedComponents = await Promise.all( + pageComponents.map(async (component) => { + const clonedComponent = { ...component, id: undefined, pageId: clonePageId }; + const newComponent = await manager.save(manager.create(Component, clonedComponent)); + + componentsIdMap[component.id] = newComponent.id; + const componentLayouts = await manager.find(Layout, { componentId: component.id }); + const clonedLayouts = componentLayouts.map((layout) => ({ + ...layout, + id: undefined, + componentId: newComponent.id, + })); + + // Clone component events + const clonedComponentEvents = await this.eventHandlerService.findAllEventsWithSourceId(component.id); + const clonedEvents = clonedComponentEvents.map((event) => { + const eventDefinition = event.event; + + if (eventDefinition?.actionId === 'control-component') { + eventDefinition.componentId = componentsIdMap[eventDefinition.componentId]; + } + + if (eventDefinition?.actionId == 'show-modal' || eventDefinition?.actionId === 'hide-modal') { + eventDefinition.modal = componentsIdMap[eventDefinition.modal]; + } + + event.event = eventDefinition; + + const clonedEvent = new EventHandler(); + clonedEvent.event = event.event; + clonedEvent.index = event.index; + clonedEvent.name = event.name; + clonedEvent.sourceId = newComponent.id; + clonedEvent.target = event.target; + clonedEvent.appVersionId = event.appVersionId; + + return clonedEvent; + }); + + await manager.save(Layout, clonedLayouts); + await manager.save(EventHandler, clonedEvents); + + return newComponent; + }) + ); + + const isChildOfTabsOrCalendar = (component, allComponents = [], componentParentId = undefined) => { + if (componentParentId) { + const parentId = component?.parent?.split('-').slice(0, -1).join('-'); + + const parentComponent = allComponents.find((comp) => comp.id === parentId); + + if (parentComponent) { + return parentComponent.type === 'Tabs' || parentComponent.type === 'Calendar'; + } + } + + return false; + }; + + for (const component of clonedComponents) { + let parentId = component.parent ? component.parent : null; + + const isParentTabOrCalendar = isChildOfTabsOrCalendar(component, pageComponents, parentId); + + if (isParentTabOrCalendar) { + const childTabId = component.parent.split('-')[component.parent.split('-').length - 1]; + const _parentId = component?.parent?.split('-').slice(0, -1).join('-'); + const mappedParentId = componentsIdMap[_parentId]; + + parentId = `${mappedParentId}-${childTabId}`; + } else { + parentId = componentsIdMap[parentId]; + } + + if (parentId) { + await manager.update(Component, component.id, { parent: parentId }); + } + } + }); + } + + async updatePage(pageUpdates: UpdatePageDto, appVersionId: string) { + if (Object.keys(pageUpdates.diff).length > 1) { + return this.updatePagesOrder(pageUpdates.diff, appVersionId); + } + + const currentPage = await this.pageRepository.findOne(pageUpdates.pageId); + + if (!currentPage) { + throw new Error('Page not found'); + } + return this.pageRepository.update(pageUpdates.pageId, pageUpdates.diff); + } + + async updatePagesOrder(pages, appVersionId: string) { + const pagesToPage = Object.keys(pages).map((pageId) => { + return { + id: pageId, + index: pages[pageId].index, + }; + }); + + return await dbTransactionForAppVersionAssociationsUpdate(async (manager: EntityManager) => { + await Promise.all( + pagesToPage.map(async (page) => { + await manager.update(Page, page.id, page); + }) + ); + }, appVersionId); + } + + async deletePage(pageId: string, appVersionId: string) { + const { editingVersion } = await this.appService.findAppFromVersion(appVersionId); + return dbTransactionForAppVersionAssociationsUpdate(async (manager: EntityManager) => { + const pageExists = await manager.findOne(Page, pageId); + + if (!pageExists) { + throw new Error('Page not found'); + } + + if (editingVersion?.homePageId === pageId) { + throw new Error('Cannot delete home page'); + } + this.eventHandlerService.cascadeDeleteEvents(pageExists.id); + const pageDeletedIndex = pageExists.index; + const pageDeleted = await this.pageRepository.delete(pageId); + + if (pageDeleted.affected === 0) { + throw new Error('Page not deleted'); + } + + const pages = await this.pageRepository.find({ appVersionId: pageExists.appVersionId }); + + const rearrangedPages = this.rearrangePagesOnDelete(pages, pageDeletedIndex); + + return await Promise.all( + rearrangedPages.map(async (page) => { + await manager.update(Page, page.id, page); + }) + ); + }, appVersionId); + } + + rearrangePagesOnDelete(pages: Page[], pageDeletedIndex: number) { + const rearrangedPages = pages.map((page, index) => { + if (index + 1 >= pageDeletedIndex) { + return { + ...page, + index: page.index - 1, + }; + } + + return page; + }); + + return rearrangedPages; + } +} diff --git a/server/test/controllers/apps.e2e-spec.ts b/server/test/controllers/apps.e2e-spec.ts index 278654f13d..60fdf2093a 100644 --- a/server/test/controllers/apps.e2e-spec.ts +++ b/server/test/controllers/apps.e2e-spec.ts @@ -508,7 +508,7 @@ describe('apps controller', () => { }); }); - describe('POST /api/apps/:id/clone', () => { + describe('POST /api/v2/resources/clone', () => { it('should be able to clone the app if user group is admin', async () => { const adminUserData = await createUser(app, { email: 'admin@tooljet.io', @@ -543,31 +543,37 @@ describe('apps controller', () => { await createApplicationVersion(app, application); + const payload = { + app: [{ id: application.id, name: `${application.name}_Copy` }], + organization_id: application.organizationId, + }; + let response = await request(app.getHttpServer()) - .post(`/api/apps/${application.id}/clone`) + .post('/api/v2/resources/clone') .set('tj-workspace-id', adminUserData.user.defaultOrganizationId) .set('Cookie', adminUserData['tokenCookie']) - .send({ name: 'App to clone_Copy' }); + .send(payload); expect(response.statusCode).toBe(201); + expect(response.body.success).toBe(true); - const appId = response.body.id; + const appId = response.body['imports']['app'][0]['id']; const clonedApplication = await App.findOneOrFail({ where: { id: appId } }); expect(clonedApplication.name).toContain('App to clone'); response = await request(app.getHttpServer()) - .post(`/api/apps/${application.id}/clone`) + .post('/api/v2/resources/clone') .set('tj-workspace-id', developerUserData.user.defaultOrganizationId) .set('Cookie', developerUserData['tokenCookie']) - .send({ name: 'App to clone_Copy' }); + .send(payload); expect(response.statusCode).toBe(403); response = await request(app.getHttpServer()) - .post(`/api/apps/${application.id}/clone`) + .post('/api/v2/resources/clone') .set('tj-workspace-id', viewerUserData.user.defaultOrganizationId) .set('Cookie', viewerUserData['tokenCookie']) - .send({ name: 'App to clone_Copy' }); + .send(payload); expect(response.statusCode).toBe(403); diff --git a/server/test/test.helper.ts b/server/test/test.helper.ts index 8b374b0a5a..261f3c6dba 100644 --- a/server/test/test.helper.ts +++ b/server/test/test.helper.ts @@ -8,7 +8,7 @@ import { User } from 'src/entities/user.entity'; import { App } from 'src/entities/app.entity'; import { File } from 'src/entities/file.entity'; import { Plugin } from 'src/entities/plugin.entity'; -import { INestApplication, ValidationPipe } from '@nestjs/common'; +import { INestApplication, ValidationPipe, VersioningType, VERSION_NEUTRAL } from '@nestjs/common'; import { Test } from '@nestjs/testing'; import { AppModule } from 'src/app.module'; import { AppVersion } from 'src/entities/app_version.entity'; @@ -51,6 +51,10 @@ export async function createNestAppInstance(): Promise { app.useGlobalFilters(new AllExceptionsFilter(moduleRef.get(Logger))); app.useWebSocketAdapter(new WsAdapter(app)); app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true })); + app.enableVersioning({ + type: VersioningType.URI, + defaultVersion: VERSION_NEUTRAL, + }); await app.init(); return app;