Browse Source

Merge branch 'develop' into feature/#410-import-scenario

trgiangdo 1 năm trước cách đây
mục cha
commit
53c8607d2c
100 tập tin đã thay đổi với 1220 bổ sung756 xóa
  1. 1 1
      .github/actions/install/action.yml
  2. 33 0
      .github/scripts/link-workflow-to-pr.js
  3. 10 0
      .github/scripts/run-workflow.js
  4. 1 0
      .github/workflows/build-and-release-single-package.yml
  5. 31 10
      .github/workflows/dependencies-management.yml
  6. 1 1
      .github/workflows/frontend.yml
  7. 5 5
      .github/workflows/overall-tests.yml
  8. 1 1
      .github/workflows/packaging.yml
  9. 6 6
      .github/workflows/partial-tests.yml
  10. 1 1
      .github/workflows/publish.yml
  11. 3 2
      Pipfile
  12. 1 1
      README.md
  13. 16 4
      frontend/taipy-gui/base/src/app.ts
  14. 1 0
      frontend/taipy-gui/base/src/dataManager.ts
  15. 7 2
      frontend/taipy-gui/base/src/utils.ts
  16. 263 234
      frontend/taipy-gui/package-lock.json
  17. 2 1
      frontend/taipy-gui/src/components/Taipy/AutoLoadingTable.tsx
  18. 1 1
      frontend/taipy-gui/src/components/Taipy/Chart.tsx
  19. 2 1
      frontend/taipy-gui/src/components/Taipy/Menu.tsx
  20. 29 7
      frontend/taipy-gui/src/components/Taipy/PaginatedTable.tsx
  21. 1 3
      frontend/taipy-gui/src/components/pages/TaipyRendered.tsx
  22. 2 0
      frontend/taipy-gui/src/utils/index.ts
  23. 123 130
      frontend/taipy/package-lock.json
  24. 23 5
      frontend/taipy/src/CoreSelector.tsx
  25. 1 1
      frontend/taipy/src/ScenarioSelector.tsx
  26. 3 0
      frontend/taipy/src/utils.ts
  27. 6 6
      taipy/config/_config.py
  28. 2 2
      taipy/config/_config_comparator/_config_comparator.py
  29. 4 4
      taipy/config/checker/issue_collector.py
  30. 4 4
      taipy/config/config.pyi
  31. 1 1
      taipy/config/setup.py
  32. 1 1
      taipy/core/_core.py
  33. 13 8
      taipy/core/_entity/_entity_ids.py
  34. 1 1
      taipy/core/_entity/_reload.py
  35. 0 3
      taipy/core/_manager/_manager.py
  36. 1 1
      taipy/core/_version/_version_fs_repository.py
  37. 1 1
      taipy/core/_version/_version_sql_repository.py
  38. 12 12
      taipy/core/config/task_config.py
  39. 1 1
      taipy/core/cycle/_cycle_fs_repository.py
  40. 1 1
      taipy/core/cycle/_cycle_sql_repository.py
  41. 1 1
      taipy/core/data/_data_fs_repository.py
  42. 1 1
      taipy/core/data/_data_manager.py
  43. 1 1
      taipy/core/data/_data_model.py
  44. 1 1
      taipy/core/data/_data_sql_repository.py
  45. 4 4
      taipy/core/data/_file_datanode_mixin.py
  46. 9 7
      taipy/core/data/data_node.py
  47. 1 1
      taipy/core/job/_job_fs_repository.py
  48. 1 1
      taipy/core/job/_job_sql_repository.py
  49. 5 5
      taipy/core/notification/core_event_consumer.py
  50. 1 1
      taipy/core/scenario/_scenario_fs_repository.py
  51. 1 1
      taipy/core/scenario/_scenario_sql_repository.py
  52. 1 1
      taipy/core/submission/_submission_fs_repository.py
  53. 1 1
      taipy/core/submission/_submission_sql_repository.py
  54. 2 2
      taipy/core/submission/submission.py
  55. 1 1
      taipy/core/task/_task_fs_repository.py
  56. 3 2
      taipy/core/task/_task_manager.py
  57. 1 1
      taipy/core/task/_task_sql_repository.py
  58. 7 7
      taipy/core/task/task.py
  59. 1 1
      taipy/gui/_page.py
  60. 32 29
      taipy/gui/_renderers/builder.py
  61. 8 12
      taipy/gui/_renderers/factory.py
  62. 2 2
      taipy/gui/_renderers/json.py
  63. 1 1
      taipy/gui/builder/_api_generator.py
  64. 1 1
      taipy/gui/builder/_context_manager.py
  65. 2 2
      taipy/gui/builder/_element.py
  66. 1 1
      taipy/gui/config.py
  67. 8 11
      taipy/gui/gui.py
  68. 1 0
      taipy/gui/types.py
  69. 46 8
      taipy/gui/utils/_adapter.py
  70. 1 1
      taipy/gui/utils/_runtime_manager.py
  71. 17 10
      taipy/gui_core/_context.py
  72. 3 3
      taipy/rest/api/exceptions/exceptions.py
  73. 1 1
      taipy/rest/app.py
  74. 4 2
      taipy/rest/rest.py
  75. 2 3
      tests/core/_orchestrator/_dispatcher/test_dispatcher__needs_to_run.py
  76. 6 4
      tests/core/_orchestrator/test_orchestrator__submit.py
  77. 1 1
      tests/core/_orchestrator/test_orchestrator__submit_task.py
  78. 10 3
      tests/core/cycle/test_cycle_repositories.py
  79. 16 3
      tests/core/data/test_data_repositories.py
  80. 6 5
      tests/core/notification/test_events_published.py
  81. 14 3
      tests/core/scenario/test_scenario_repositories.py
  82. 108 76
      tests/core/task/test_task_repositories.py
  83. 193 0
      tests/core/test_taipy/test_export_with_sql_repo.py
  84. 2 2
      tests/gui/builder/control/test_menu.py
  85. 1 1
      tests/gui/builder/control/test_navbar.py
  86. 2 2
      tests/gui/builder/control/test_selector.py
  87. 2 2
      tests/gui/builder/control/test_toggle.py
  88. 4 4
      tests/gui/control/test_menu.py
  89. 2 2
      tests/gui/control/test_navbar.py
  90. 2 2
      tests/gui/control/test_selector.py
  91. 4 4
      tests/gui/control/test_toggle.py
  92. 1 1
      tests/gui/e2e/page_scopes/assets2_class_scopes/page1.py
  93. 1 1
      tests/gui/e2e/page_scopes/assets2_class_scopes/page2.py
  94. 2 2
      tests/gui/gui_specific/test_expression.py
  95. 2 0
      tests/gui_core/test_context_is_deletable.py
  96. 2 0
      tests/gui_core/test_context_is_editable.py
  97. 2 0
      tests/gui_core/test_context_is_readable.py
  98. 16 16
      tools/packages/pipfiles/Pipfile3.10.max
  99. 16 16
      tools/packages/pipfiles/Pipfile3.11.max
  100. 16 16
      tools/packages/pipfiles/Pipfile3.12.max

+ 1 - 1
.github/actions/install/action.yml

@@ -45,7 +45,7 @@ runs:
       shell: bash
 
     - name: Setup LibMagic (MacOS)
-      if: inputs.os == 'macos-latest' && inputs.install-gui
+      if: inputs.os == 'macos-13' && inputs.install-gui
       run: brew install libmagic
       shell: bash
 

+ 33 - 0
.github/scripts/link-workflow-to-pr.js

@@ -0,0 +1,33 @@
+// This script is used to link a workflow run to a pull request.
+// The script retrieves the workflow runs for the repository and finds the workflow run for the branch targeted.
+// It then updates the pull request description with the link to the workflow run.
+
+module.exports = async ({github, context, branchTargeted, pullRequestNumber}) => {
+  console.log(`Link the workflow run to the pull request #${pullRequestNumber} for the branch '${branchTargeted}'`);
+  // Retrieve the workflow runs for the repository.
+  const runs = await github.request('GET /repos/{owner}/{repo}/actions/runs', {
+    owner: context.repo.owner,
+    repo: context.repo.repo,
+    headers: {
+      'X-GitHub-Api-Version': '2022-11-28'
+    }
+  })
+
+  // Retrieve the workflow run for the branch targeted.
+  const workflow = runs.data.workflow_runs.find(run => run.head_branch == branchTargeted)
+
+  // Handle the error case.
+  let message = `No workflow found for the branch ${branchTargeted}`;
+  if (workflow) {
+    console.warn(`Workflow run found: ${workflow.html_url}`)
+    message = `[Workflow result](${workflow.html_url})`;
+  }
+
+  // Update the pull request with the link to the workflow run.
+  github.rest.pulls.update({
+    owner: context.repo.owner,
+    repo: context.repo.repo,
+    pull_number: pullRequestNumber,
+    body: message,
+  });
+}

+ 10 - 0
.github/scripts/run-workflow.js

@@ -0,0 +1,10 @@
+// Trigger a specific workflow on a specific branch.
+module.exports = async ({github, context, branchTargeted, workflowToTrigger}) => {
+  console.log(`Run the workflow #${workflowToTrigger} on the branch '${branchTargeted}'`);
+  await github.rest.actions.createWorkflowDispatch({
+    owner: context.repo.owner,
+    repo: context.repo.repo,
+    workflow_id: workflowToTrigger,
+    ref: branchTargeted,
+  })
+}

+ 1 - 0
.github/workflows/build-and-release-single-package.yml

@@ -152,6 +152,7 @@ jobs:
         working-directory: ${{ steps.set-variables.outputs.package_dir }}
         run: |
           python setup.py build_py && python -m build
+          for file in ./dist/*; do mv "$file" "${file//_/-}"; done
 
       - name: Create tag and release
         working-directory: ${{ steps.set-variables.outputs.package_dir }}

+ 31 - 10
.github/workflows/dependencies-management.yml

@@ -1,3 +1,13 @@
+# This workflow is used to manage the dependencies of the Taipy packages.
+# - Runs each Sunday.
+# - For each Python version supported:
+#   - Call a custom script to align dependencies between Taipy packages.
+#   - Call a custom script to update dependencies (Pipfile and requirements.txt).
+#   - If a new package version is available for the Python version:
+#     - The Python version's Pull Request (PR) is created.
+#       - If the Python version is the latest supported, the PR contains an updated Pipfile and requirements.txt.
+#       - Otherwise, the PR contains the updated Pipfile.
+#     - The action triggers tests workflow to test compatibility and link the workflow to the PR in the description.
 name: Dependencies management
 
 on:
@@ -20,7 +30,6 @@ jobs:
     runs-on: ${{ matrix.os }}
     steps:
       - uses: actions/checkout@v4
-
       - uses: actions/setup-python@v5
         with:
           python-version: ${{matrix.python-version}}
@@ -42,7 +51,7 @@ jobs:
           cat pipfiles/Pipfile${{matrix.python-version}}.max
 
       - name: Create the pull request updating the dependencies (3.12 only)
-        if: steps.ensure-dependencies-are-up-to-date.outputs.diff != '' and ${{matrix.python-version}} == '3.12'
+        if: steps.ensure-dependencies-are-up-to-date.outputs.diff != '' && matrix.python-version == '3.12'
         uses: peter-evans/create-pull-request@v5
         with:
           token: ${{ secrets.GITHUB_TOKEN }}
@@ -58,7 +67,7 @@ jobs:
             tools/packages/taipy*/*requirements.txt
 
       - name: Create the pull request updating the Pipfile max
-        if: steps.ensure-dependencies-are-up-to-date.outputs.diff != '' and ${{matrix.python-version}} != '3.12'
+        if: steps.ensure-dependencies-are-up-to-date.outputs.diff != '' && matrix.python-version != '3.12'
         uses: peter-evans/create-pull-request@v5
         with:
           token: ${{ secrets.GITHUB_TOKEN }}
@@ -76,12 +85,24 @@ jobs:
       # This action triggers the overall-tests.yml workflow on the PR
       # to allow the tests to run on the new dependencies.
       - name: Run tests on PR
-        uses: actions/github-script@v6
+        uses: actions/github-script@v7
         with:
+          github-token: ${{ secrets.TRIGGER_GITHUB_PR }}
           script: |
-            github.rest.actions.createWorkflowDispatch({
-              owner: context.repo.owner,
-              repo: context.repo.repo,
-              workflow_id: 'overall-tests.yml',
-              ref: 'dependencies/update-python${{matrix.python-version}}',
-            })
+            const runTests = require('.github/scripts/run-workflow.js')
+            const linkTests = require('.github/scripts/link-workflow-to-pr.js')
+
+            // Branch to target with the workflow run.
+            const branchTargeted = "dependencies/update-python${{matrix.python-version}}";
+            // The current pull request number to link the workflow run.
+            const pullRequestNumber = process.env.PULL_REQUEST_NUMBER;
+            // The workflow file to trigger.
+            const workflowToTrigger = 'overall-tests.yml';
+            const waitForWorkflowCreation = 120000; // 2 minutes
+
+            // Run the tests.
+            await runTests({github, context, branchTargeted, workflowToTrigger});
+            // Wait for the workflow to be created.
+            await new Promise(r => setTimeout(r, waitForWorkflowCreation));
+            // Link the workflow to the PR.
+            await linkTests({github, context, branchTargeted, pullRequestNumber});

+ 1 - 1
.github/workflows/frontend.yml

@@ -15,7 +15,7 @@ jobs:
     strategy:
       matrix:
         node-version: [20.x]
-        os: [ubuntu-latest, windows-latest, macos-latest]
+        os: [ubuntu-latest, windows-latest, macos-13]
     runs-on: ${{ matrix.os }}
 
     defaults:

+ 5 - 5
.github/workflows/overall-tests.yml

@@ -12,6 +12,7 @@ jobs:
     uses: ./.github/workflows/partial-tests.yml
 
   coverage:
+    timeout-minutes: 40
     runs-on: ubuntu-latest
     if: ${{ github.event_name == 'pull_request' }}
     steps:
@@ -40,18 +41,17 @@ jobs:
           token: ${{ secrets.GITHUB_TOKEN }}
 
   overall-tests:
-    needs: [coverage, partial-tests]
+    needs: [partial-tests]
     timeout-minutes: 50
     strategy:
       fail-fast: false
       matrix:
         python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
-        os: [ubuntu-latest, windows-latest, macos-latest]
+        os: [ubuntu-latest, windows-latest, macos-13]
         pipfile-version: ['min', 'max']
     runs-on: ${{ matrix.os }}
     steps:
       - uses: actions/checkout@v4
-
       - uses: actions/setup-python@v5
         with:
           python-version: ${{matrix.python-version}}
@@ -69,13 +69,13 @@ jobs:
           pipenv run pytest -m "not orchestrator_dispatcher and not standalone and not teste2e" tests
 
   intermittent-tests:
-    needs: [coverage, partial-tests]
+    needs: [partial-tests]
     timeout-minutes: 40
     strategy:
       fail-fast: false
       matrix:
         python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
-        os: [ubuntu-latest, windows-latest, macos-latest]
+        os: [ubuntu-latest, windows-latest, macos-13]
         orchestrator: ['orchestrator_dispatcher', 'standalone']
         pipfile-version: ['min', 'max']
     runs-on: ${{ matrix.os }}

+ 1 - 1
.github/workflows/packaging.yml

@@ -18,7 +18,7 @@ jobs:
     strategy:
       matrix:
         python-versions: [ '3.8', '3.9', '3.10', '3.11', '3.12']
-        os: [ubuntu-latest, macos-latest] #, windows-latest]
+        os: [ubuntu-latest, macos-13] #, windows-latest]
 
     runs-on: ${{ matrix.os }}
 

+ 6 - 6
.github/workflows/partial-tests.yml

@@ -21,7 +21,7 @@ jobs:
       fail-fast: false
       matrix:
         python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
-        os: [ubuntu-latest, windows-latest, macos-latest]
+        os: [ubuntu-latest, windows-latest, macos-13]
     runs-on: ${{ matrix.os }}
     steps:
       - uses: actions/checkout@v4
@@ -61,7 +61,7 @@ jobs:
         run: pipenv install --dev --python=${{ matrix.python-version }}
 
       - name: Setup LibMagic (MacOS)
-        if: matrix.os == 'macos-latest'
+        if: matrix.os == 'macos-13'
         run: brew install libmagic
 
       - uses: actions/setup-node@v4
@@ -116,7 +116,7 @@ jobs:
       fail-fast: false
       matrix:
         python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
-        os: [ubuntu-latest, windows-latest, macos-latest]
+        os: [ubuntu-latest, windows-latest, macos-13]
     runs-on: ${{ matrix.os }}
     steps:
       - uses: actions/checkout@v4
@@ -141,7 +141,7 @@ jobs:
         run: pipenv install --dev --python=${{ matrix.python-version }}
 
       - name: Setup LibMagic (MacOS)
-        if: matrix.os == 'macos-latest' && steps.changes.outputs.core == 'true'
+        if: matrix.os == 'macos-13' && steps.changes.outputs.core == 'true'
         run: brew install libmagic
 
       - name: Pytest Core orchestrator_dispatcher
@@ -155,7 +155,7 @@ jobs:
       fail-fast: false
       matrix:
         python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
-        os: [ubuntu-latest, windows-latest, macos-latest]
+        os: [ubuntu-latest, windows-latest, macos-13]
     runs-on: ${{ matrix.os }}
     steps:
       - uses: actions/checkout@v4
@@ -180,7 +180,7 @@ jobs:
         run: pipenv install --dev --python=${{ matrix.python-version }}
 
       - name: Setup LibMagic (MacOS)
-        if: matrix.os == 'macos-latest' && steps.changes.outputs.core == 'true'
+        if: matrix.os == 'macos-13' && steps.changes.outputs.core == 'true'
         run: brew install libmagic
 
       - name: Pytest Core standalone

+ 1 - 1
.github/workflows/publish.yml

@@ -105,7 +105,7 @@ jobs:
     strategy:
       matrix:
         python-versions: ['3.8','3.9','3.10', '3.11', '3.12']
-        os: [ubuntu-latest,windows-latest,macos-latest]
+        os: [ubuntu-latest,windows-latest,macos-13]
     runs-on: ${{ matrix.os }}
     steps:
       - uses: actions/checkout@v3

+ 3 - 2
Pipfile

@@ -8,7 +8,7 @@ apispec = {extras = ["yaml"], version = "==6.3"}
 apispec-webframeworks = "==0.5.2"
 "backports.zoneinfo" = {version="==0.2.1", markers="python_version < '3.9'", extras=["tzdata"]}
 cookiecutter = "==2.1.1"
-deepdiff = "==6.2.2"
+deepdiff = "==6.7.1"
 flask = "==3.0.0"
 flask-cors = "==4.0.0"
 flask-socketio = "==5.3.6"
@@ -23,7 +23,7 @@ networkx = "==2.6"
 openpyxl = "==3.1.2"
 pandas = "==1.3.5"
 pyarrow = "==14.0.2"
-pymongo = {extras = ["srv"], version = "==4.2.0"}
+pymongo = {extras = ["srv"], version = "==4.6.3"}
 python-dotenv = "==1.0.0"
 python-magic = {version = "==0.4.24", markers="sys_platform != 'win32'"}
 python-magic-bin = {version = "==0.4.14", markers="sys_platform == 'win32'"}
@@ -36,6 +36,7 @@ tzlocal = "==3.0"
 boto3 = "==1.29.1"
 watchdog = "==4.0.0"
 charset-normalizer = "==3.3.2"
+numpy = "<2.0.0"
 
 [dev-packages]
 freezegun = "*"

+ 1 - 1
README.md

@@ -21,7 +21,7 @@ Taipy is an open-source Python library for easy, end-to-end application developm
     <br/><br/>
     <a href="https://discord.com/invite/SJyz2VJGxV">Discord support</a>
     ·
-    <a href="https://docs.taipy.io/en/latest/knowledge_base/demos">Demos & Examples</a>
+    <a href="https://docs.taipy.io/en/latest/gallery/">Demos & Examples</a>
   </p>
 
 &nbsp;

+ 16 - 4
frontend/taipy-gui/base/src/app.ts

@@ -3,18 +3,20 @@ import { sendWsMessage, TAIPY_CLIENT_ID } from "../../src/context/wsUtils";
 import { uploadFile } from "../../src/workers/fileupload";
 
 import { Socket, io } from "socket.io-client";
-import { DataManager } from "./dataManager";
+import { DataManager, ModuleData } from "./dataManager";
 import { initSocket } from "./utils";
 
 export type OnInitHandler = (appManager: TaipyApp) => void;
 export type OnChangeHandler = (appManager: TaipyApp, encodedName: string, value: unknown) => void;
 export type OnNotifyHandler = (appManager: TaipyApp, type: string, message: string) => void;
+export type onReloadHandler = (appManager: TaipyApp, removedChanges: ModuleData) => void;
 
 export class TaipyApp {
     socket: Socket;
     _onInit: OnInitHandler | undefined;
     _onChange: OnChangeHandler | undefined;
     _onNotify: OnNotifyHandler | undefined;
+    _onReload: onReloadHandler | undefined;
     variableData: DataManager | undefined;
     functionData: DataManager | undefined;
     appId: string;
@@ -46,7 +48,7 @@ export class TaipyApp {
         return this._onInit;
     }
     set onInit(handler: OnInitHandler | undefined) {
-        if (handler !== undefined && handler?.length !== 1) {
+        if (handler !== undefined && handler.length !== 1) {
             throw new Error("onInit() requires one parameter");
         }
         this._onInit = handler;
@@ -56,7 +58,7 @@ export class TaipyApp {
         return this._onChange;
     }
     set onChange(handler: OnChangeHandler | undefined) {
-        if (handler !== undefined && handler?.length !== 3) {
+        if (handler !== undefined && handler.length !== 3) {
             throw new Error("onChange() requires three parameters");
         }
         this._onChange = handler;
@@ -66,12 +68,22 @@ export class TaipyApp {
         return this._onNotify;
     }
     set onNotify(handler: OnNotifyHandler | undefined) {
-        if (handler !== undefined && handler?.length !== 3) {
+        if (handler !== undefined && handler.length !== 3) {
             throw new Error("onNotify() requires three parameters");
         }
         this._onNotify = handler;
     }
 
+    get onReload() {
+        return this._onReload;
+    }
+    set onReload(handler: onReloadHandler | undefined) {
+        if (handler !== undefined && handler?.length !== 2) {
+            throw new Error("_onReload() requires two parameters");
+        }
+        this._onReload = handler;
+    }
+
     // Utility methods
     init() {
         this.clientId = "";

+ 1 - 0
frontend/taipy-gui/base/src/dataManager.ts

@@ -50,6 +50,7 @@ export class DataManager {
                 this._data[vData["encoded_name"]] = vData.value;
             }
         }
+        return changes;
     }
 
     getEncodedName(varName: string, module: string): string | undefined {

+ 7 - 2
frontend/taipy-gui/base/src/utils.ts

@@ -1,3 +1,4 @@
+import merge from "lodash/merge";
 import { Socket } from "socket.io-client";
 import { IdMessage, storeClientId } from "../../src/context/utils";
 import { WsMessage, sendWsMessage } from "../../src/context/wsUtils";
@@ -74,8 +75,12 @@ const processWsMessage = (message: WsMessage, appManager: TaipyApp) => {
             const variableData = payload.variable;
             const functionData = payload.function;
             if (appManager.variableData && appManager.functionData) {
-                appManager.variableData.init(variableData);
-                appManager.functionData.init(functionData);
+                const varChanges = appManager.variableData.init(variableData);
+                const functionChanges = appManager.functionData.init(functionData);
+                const changes = merge(varChanges, functionChanges);
+                if (varChanges || functionChanges) {
+                    appManager.onReload && appManager.onReload(appManager, changes);
+                }
             } else {
                 appManager.variableData = new DataManager(variableData);
                 appManager.functionData = new DataManager(functionData);

+ 263 - 234
frontend/taipy-gui/package-lock.json

@@ -146,27 +146,27 @@
       }
     },
     "node_modules/@babel/compat-data": {
-      "version": "7.24.1",
-      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.1.tgz",
-      "integrity": "sha512-Pc65opHDliVpRHuKfzI+gSA4zcgr65O4cl64fFJIWEEh8JoHIHh0Oez1Eo8Arz8zq/JhgKodQaxEwUPRtZylVA==",
+      "version": "7.24.4",
+      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz",
+      "integrity": "sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==",
       "dev": true,
       "engines": {
         "node": ">=6.9.0"
       }
     },
     "node_modules/@babel/core": {
-      "version": "7.24.3",
-      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.3.tgz",
-      "integrity": "sha512-5FcvN1JHw2sHJChotgx8Ek0lyuh4kCKelgMTTqhYJJtloNvUfpAFMeNQUtdlIaktwrSV9LtCdqwk48wL2wBacQ==",
+      "version": "7.24.4",
+      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.4.tgz",
+      "integrity": "sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg==",
       "dev": true,
       "dependencies": {
         "@ampproject/remapping": "^2.2.0",
         "@babel/code-frame": "^7.24.2",
-        "@babel/generator": "^7.24.1",
+        "@babel/generator": "^7.24.4",
         "@babel/helper-compilation-targets": "^7.23.6",
         "@babel/helper-module-transforms": "^7.23.3",
-        "@babel/helpers": "^7.24.1",
-        "@babel/parser": "^7.24.1",
+        "@babel/helpers": "^7.24.4",
+        "@babel/parser": "^7.24.4",
         "@babel/template": "^7.24.0",
         "@babel/traverse": "^7.24.1",
         "@babel/types": "^7.24.0",
@@ -200,9 +200,9 @@
       }
     },
     "node_modules/@babel/generator": {
-      "version": "7.24.1",
-      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.1.tgz",
-      "integrity": "sha512-DfCRfZsBcrPEHUfuBMgbJ1Ut01Y/itOs+hY2nFLgqsqXd52/iSiVq5TITtUasIUgm+IIKdY2/1I7auiQOEeC9A==",
+      "version": "7.24.4",
+      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.4.tgz",
+      "integrity": "sha512-Xd6+v6SnjWVx/nus+y0l1sxMOTOMBkyL4+BIdbALyatQnAe/SRVjANeDPSCYaX+i1iJmuGSKf3Z+E+V/va1Hvw==",
       "dev": true,
       "dependencies": {
         "@babel/types": "^7.24.0",
@@ -362,9 +362,9 @@
       }
     },
     "node_modules/@babel/helpers": {
-      "version": "7.24.1",
-      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.1.tgz",
-      "integrity": "sha512-BpU09QqEe6ZCHuIHFphEFgvNSrubve1FtyMton26ekZ85gRGi6LrTF7zArARp2YvyFxloeiRmtSCq5sjh1WqIg==",
+      "version": "7.24.4",
+      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.4.tgz",
+      "integrity": "sha512-FewdlZbSiwaVGlgT1DPANDuCHaDMiOo+D/IDYRFYjHOuv66xMSJ7fQwwODwRNAPkADIO/z1EoF/l2BCWlWABDw==",
       "dev": true,
       "dependencies": {
         "@babel/template": "^7.24.0",
@@ -454,9 +454,9 @@
       }
     },
     "node_modules/@babel/parser": {
-      "version": "7.24.1",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.1.tgz",
-      "integrity": "sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==",
+      "version": "7.24.4",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz",
+      "integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==",
       "dev": true,
       "bin": {
         "parser": "bin/babel-parser.js"
@@ -643,9 +643,9 @@
       }
     },
     "node_modules/@babel/runtime": {
-      "version": "7.24.1",
-      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz",
-      "integrity": "sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==",
+      "version": "7.24.4",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.4.tgz",
+      "integrity": "sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==",
       "dependencies": {
         "regenerator-runtime": "^0.14.0"
       },
@@ -813,9 +813,9 @@
       }
     },
     "node_modules/@emotion/serialize": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.3.tgz",
-      "integrity": "sha512-iD4D6QVZFDhcbH0RAG1uVu1CwVLMWUkCvAqqlewO/rxf8+87yIBAlt4+AxMiiKPLs5hFc0owNk/sLLAOROw3cA==",
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.4.tgz",
+      "integrity": "sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ==",
       "dependencies": {
         "@emotion/hash": "^0.9.1",
         "@emotion/memoize": "^0.8.1",
@@ -830,14 +830,14 @@
       "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA=="
     },
     "node_modules/@emotion/styled": {
-      "version": "11.11.0",
-      "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.0.tgz",
-      "integrity": "sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==",
+      "version": "11.11.5",
+      "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.5.tgz",
+      "integrity": "sha512-/ZjjnaNKvuMPxcIiUkf/9SHoG4Q196DRl1w82hQ3WCsjo1IUR8uaGWrC6a87CrYAW0Kb/pK7hk8BnLgLRi9KoQ==",
       "dependencies": {
         "@babel/runtime": "^7.18.3",
         "@emotion/babel-plugin": "^11.11.0",
-        "@emotion/is-prop-valid": "^1.2.1",
-        "@emotion/serialize": "^1.1.2",
+        "@emotion/is-prop-valid": "^1.2.2",
+        "@emotion/serialize": "^1.1.4",
         "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1",
         "@emotion/utils": "^1.2.1"
       },
@@ -1036,9 +1036,9 @@
       }
     },
     "node_modules/@humanwhocodes/object-schema": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz",
-      "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==",
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
+      "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
       "dev": true
     },
     "node_modules/@istanbuljs/load-nyc-config": {
@@ -1650,18 +1650,18 @@
       }
     },
     "node_modules/@mui/core-downloads-tracker": {
-      "version": "5.15.14",
-      "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.14.tgz",
-      "integrity": "sha512-on75VMd0XqZfaQW+9pGjSNiqW+ghc5E2ZSLRBXwcXl/C4YzjfyjrLPhrEpKnR9Uym9KXBvxrhoHfPcczYHweyA==",
+      "version": "5.15.15",
+      "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.15.tgz",
+      "integrity": "sha512-aXnw29OWQ6I5A47iuWEI6qSSUfH6G/aCsW9KmW3LiFqr7uXZBK4Ks+z8G+qeIub8k0T5CMqlT2q0L+ZJTMrqpg==",
       "funding": {
         "type": "opencollective",
         "url": "https://opencollective.com/mui-org"
       }
     },
     "node_modules/@mui/icons-material": {
-      "version": "5.15.14",
-      "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.14.tgz",
-      "integrity": "sha512-vj/51k7MdFmt+XVw94sl30SCvGx6+wJLsNYjZRgxhS6y3UtnWnypMOsm3Kmg8TN+P0dqwsjy4/fX7B1HufJIhw==",
+      "version": "5.15.15",
+      "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.15.tgz",
+      "integrity": "sha512-kkeU/pe+hABcYDH6Uqy8RmIsr2S/y5bP2rp+Gat4CcRjCcVne6KudS1NrZQhUCRysrTDCAhcbcf9gt+/+pGO2g==",
       "dependencies": {
         "@babel/runtime": "^7.23.9"
       },
@@ -1684,14 +1684,14 @@
       }
     },
     "node_modules/@mui/material": {
-      "version": "5.15.14",
-      "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.14.tgz",
-      "integrity": "sha512-kEbRw6fASdQ1SQ7LVdWR5OlWV3y7Y54ZxkLzd6LV5tmz+NpO3MJKZXSfgR0LHMP7meKsPiMm4AuzV0pXDpk/BQ==",
+      "version": "5.15.15",
+      "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.15.tgz",
+      "integrity": "sha512-3zvWayJ+E1kzoIsvwyEvkTUKVKt1AjchFFns+JtluHCuvxgKcLSRJTADw37k0doaRtVAsyh8bz9Afqzv+KYrIA==",
       "dependencies": {
         "@babel/runtime": "^7.23.9",
         "@mui/base": "5.0.0-beta.40",
-        "@mui/core-downloads-tracker": "^5.15.14",
-        "@mui/system": "^5.15.14",
+        "@mui/core-downloads-tracker": "^5.15.15",
+        "@mui/system": "^5.15.15",
         "@mui/types": "^7.2.14",
         "@mui/utils": "^5.15.14",
         "@types/react-transition-group": "^4.4.10",
@@ -1785,9 +1785,9 @@
       }
     },
     "node_modules/@mui/system": {
-      "version": "5.15.14",
-      "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.14.tgz",
-      "integrity": "sha512-auXLXzUaCSSOLqJXmsAaq7P96VPRXg2Rrz6OHNV7lr+kB8lobUF+/N84Vd9C4G/wvCXYPs5TYuuGBRhcGbiBGg==",
+      "version": "5.15.15",
+      "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.15.tgz",
+      "integrity": "sha512-aulox6N1dnu5PABsfxVGOZffDVmlxPOVgj56HrUnJE8MCSh8lOvvkd47cebIVQQYAjpwieXQXiDPj5pwM40jTQ==",
       "dependencies": {
         "@babel/runtime": "^7.23.9",
         "@mui/private-theming": "^5.15.14",
@@ -1864,9 +1864,9 @@
       }
     },
     "node_modules/@mui/x-date-pickers": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.0.0.tgz",
-      "integrity": "sha512-/9mp4O2WMixHOso63DBoZVfJVYGrzOHF5voheV2tYQ4XqDdTKp2AdWS3oh8PGwrsvCzqkvb3quzTqhKoEsJUwA==",
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.2.0.tgz",
+      "integrity": "sha512-hsXugZ+n1ZnHRYzf7+PFrjZ44T+FyGZmTreBmH0M2RUaAblgK+A1V3KNLT+r4Y9gJLH+92LwePxQ9xyfR+E51A==",
       "dependencies": {
         "@babel/runtime": "^7.24.0",
         "@mui/base": "^5.0.0-beta.40",
@@ -1929,9 +1929,9 @@
       }
     },
     "node_modules/@mui/x-tree-view": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/@mui/x-tree-view/-/x-tree-view-7.0.0.tgz",
-      "integrity": "sha512-9nus8fesjBT/V/SzjZKhABG3qvGtupkccoOnz43ebLts8MSYtR8jwnOMArwrVnzrEoaS4F4dv3M8SYVQkHWfaQ==",
+      "version": "7.3.0",
+      "resolved": "https://registry.npmjs.org/@mui/x-tree-view/-/x-tree-view-7.3.0.tgz",
+      "integrity": "sha512-zPLtY4UP4UrglAdVRphE3Ow2UVUNKo+YkiF5z6VRqMenZBiMY+CkHSC3T+xzlAz2sSiiLZdiYJFqEpjPJI3Fcw==",
       "dependencies": {
         "@babel/runtime": "^7.24.0",
         "@mui/base": "^5.0.0-beta.40",
@@ -2077,9 +2077,9 @@
       }
     },
     "node_modules/@remix-run/router": {
-      "version": "1.15.3",
-      "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz",
-      "integrity": "sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==",
+      "version": "1.16.0",
+      "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.0.tgz",
+      "integrity": "sha512-Quz1KOffeEf/zwkCBM3kBtH4ZoZ+pT3xIXBG4PPW/XFtDP7EGhtTiC2+gpL9GnR7+Qdet5Oa6cYSvwKYg6kN9Q==",
       "engines": {
         "node": ">=14.0.0"
       }
@@ -2121,27 +2121,28 @@
       }
     },
     "node_modules/@socket.io/component-emitter": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
-      "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg=="
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.1.tgz",
+      "integrity": "sha512-dzJtaDAAoXx4GCOJpbB2eG/Qj8VDpdwkLsWGzGm+0L7E8/434RyMbAHmk9ubXWVAb9nXmc44jUf8GKqVDiKezg=="
     },
     "node_modules/@testing-library/dom": {
-      "version": "9.3.4",
-      "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.4.tgz",
-      "integrity": "sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==",
+      "version": "10.0.0",
+      "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.0.0.tgz",
+      "integrity": "sha512-PmJPnogldqoVFf+EwbHvbBJ98MmqASV8kLrBYgsDNxQcFMeIS7JFL48sfyXvuMtgmWO/wMhh25odr+8VhDmn4g==",
       "dev": true,
+      "peer": true,
       "dependencies": {
         "@babel/code-frame": "^7.10.4",
         "@babel/runtime": "^7.12.5",
         "@types/aria-query": "^5.0.1",
-        "aria-query": "5.1.3",
+        "aria-query": "5.3.0",
         "chalk": "^4.1.0",
         "dom-accessibility-api": "^0.5.9",
         "lz-string": "^1.5.0",
         "pretty-format": "^27.0.2"
       },
       "engines": {
-        "node": ">=14"
+        "node": ">=18"
       }
     },
     "node_modules/@testing-library/jest-dom": {
@@ -2209,9 +2210,9 @@
       "dev": true
     },
     "node_modules/@testing-library/react": {
-      "version": "14.2.2",
-      "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-14.2.2.tgz",
-      "integrity": "sha512-SOUuM2ysCvjUWBXTNfQ/ztmnKDmqaiPV3SvoIuyxMUca45rbSWWAT/qB8CUs/JQ/ux/8JFs9DNdFQ3f6jH3crA==",
+      "version": "14.3.1",
+      "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-14.3.1.tgz",
+      "integrity": "sha512-H99XjUhWQw0lTgyMN05W3xQG1Nh4lq574D8keFf1dDoNTJgp66VbJozRaczoF+wsiaPJNt/TcnfpLGufGxSrZQ==",
       "dev": true,
       "dependencies": {
         "@babel/runtime": "^7.12.5",
@@ -2226,6 +2227,34 @@
         "react-dom": "^18.0.0"
       }
     },
+    "node_modules/@testing-library/react/node_modules/@testing-library/dom": {
+      "version": "9.3.4",
+      "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.4.tgz",
+      "integrity": "sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==",
+      "dev": true,
+      "dependencies": {
+        "@babel/code-frame": "^7.10.4",
+        "@babel/runtime": "^7.12.5",
+        "@types/aria-query": "^5.0.1",
+        "aria-query": "5.1.3",
+        "chalk": "^4.1.0",
+        "dom-accessibility-api": "^0.5.9",
+        "lz-string": "^1.5.0",
+        "pretty-format": "^27.0.2"
+      },
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/@testing-library/react/node_modules/aria-query": {
+      "version": "5.1.3",
+      "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz",
+      "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==",
+      "dev": true,
+      "dependencies": {
+        "deep-equal": "^2.0.5"
+      }
+    },
     "node_modules/@testing-library/user-event": {
       "version": "14.5.2",
       "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.5.2.tgz",
@@ -2375,9 +2404,9 @@
       }
     },
     "node_modules/@types/eslint": {
-      "version": "8.56.6",
-      "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.6.tgz",
-      "integrity": "sha512-ymwc+qb1XkjT/gfoQwxIeHZ6ixH23A+tCT2ADSA/DPVKzAjwYkTXBMCQ/f6fe4wEa85Lhp26VPeUxI7wMhAi7A==",
+      "version": "8.56.10",
+      "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz",
+      "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==",
       "dev": true,
       "dependencies": {
         "@types/estree": "*",
@@ -2564,19 +2593,18 @@
       "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q=="
     },
     "node_modules/@types/react": {
-      "version": "18.2.70",
-      "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.70.tgz",
-      "integrity": "sha512-hjlM2hho2vqklPhopNkXkdkeq6Lv8WSZTpr7956zY+3WS5cfYUewtCzsJLsbW5dEv3lfSeQ4W14ZFeKC437JRQ==",
+      "version": "18.2.79",
+      "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.79.tgz",
+      "integrity": "sha512-RwGAGXPl9kSXwdNTafkOEuFrTBD5SA2B3iEB96xi8+xu5ddUa/cpvyVCSNn+asgLCTHkb5ZxN8gbuibYJi4s1w==",
       "dependencies": {
         "@types/prop-types": "*",
-        "@types/scheduler": "*",
         "csstype": "^3.0.2"
       }
     },
     "node_modules/@types/react-dom": {
-      "version": "18.2.22",
-      "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.22.tgz",
-      "integrity": "sha512-fHkBXPeNtfvri6gdsMYyW+dW7RXFo6Ad09nLFK0VQWR7yGLai/Cyvyj696gbwYvBnhGtevUG9cET0pmUbMtoPQ==",
+      "version": "18.2.25",
+      "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.25.tgz",
+      "integrity": "sha512-o/V48vf4MQh7juIKZU2QGDfli6p1+OOi5oXx36Hffpc9adsHeXjVp8rHuPkjd8VT8sOJ2Zp05HR7CdpGTIUFUA==",
       "dev": true,
       "dependencies": {
         "@types/react": "*"
@@ -2652,7 +2680,8 @@
     "node_modules/@types/scheduler": {
       "version": "0.16.8",
       "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz",
-      "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A=="
+      "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==",
+      "optional": true
     },
     "node_modules/@types/semver": {
       "version": "7.5.8",
@@ -2704,22 +2733,22 @@
       "dev": true
     },
     "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "7.3.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.3.1.tgz",
-      "integrity": "sha512-STEDMVQGww5lhCuNXVSQfbfuNII5E08QWkvAw5Qwf+bj2WT+JkG1uc+5/vXA3AOYMDHVOSpL+9rcbEUiHIm2dw==",
+      "version": "7.7.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.7.1.tgz",
+      "integrity": "sha512-KwfdWXJBOviaBVhxO3p5TJiLpNuh2iyXyjmWN0f1nU87pwyvfS0EmjC6ukQVYVFJd/K1+0NWGPDXiyEyQorn0Q==",
       "dev": true,
       "dependencies": {
-        "@eslint-community/regexpp": "^4.5.1",
-        "@typescript-eslint/scope-manager": "7.3.1",
-        "@typescript-eslint/type-utils": "7.3.1",
-        "@typescript-eslint/utils": "7.3.1",
-        "@typescript-eslint/visitor-keys": "7.3.1",
+        "@eslint-community/regexpp": "^4.10.0",
+        "@typescript-eslint/scope-manager": "7.7.1",
+        "@typescript-eslint/type-utils": "7.7.1",
+        "@typescript-eslint/utils": "7.7.1",
+        "@typescript-eslint/visitor-keys": "7.7.1",
         "debug": "^4.3.4",
         "graphemer": "^1.4.0",
-        "ignore": "^5.2.4",
+        "ignore": "^5.3.1",
         "natural-compare": "^1.4.0",
-        "semver": "^7.5.4",
-        "ts-api-utils": "^1.0.1"
+        "semver": "^7.6.0",
+        "ts-api-utils": "^1.3.0"
       },
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -2739,15 +2768,15 @@
       }
     },
     "node_modules/@typescript-eslint/parser": {
-      "version": "7.3.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.3.1.tgz",
-      "integrity": "sha512-Rq49+pq7viTRCH48XAbTA+wdLRrB/3sRq4Lpk0oGDm0VmnjBrAOVXH/Laalmwsv2VpekiEfVFwJYVk6/e8uvQw==",
+      "version": "7.7.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.7.1.tgz",
+      "integrity": "sha512-vmPzBOOtz48F6JAGVS/kZYk4EkXao6iGrD838sp1w3NQQC0W8ry/q641KU4PrG7AKNAf56NOcR8GOpH8l9FPCw==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/scope-manager": "7.3.1",
-        "@typescript-eslint/types": "7.3.1",
-        "@typescript-eslint/typescript-estree": "7.3.1",
-        "@typescript-eslint/visitor-keys": "7.3.1",
+        "@typescript-eslint/scope-manager": "7.7.1",
+        "@typescript-eslint/types": "7.7.1",
+        "@typescript-eslint/typescript-estree": "7.7.1",
+        "@typescript-eslint/visitor-keys": "7.7.1",
         "debug": "^4.3.4"
       },
       "engines": {
@@ -2767,13 +2796,13 @@
       }
     },
     "node_modules/@typescript-eslint/scope-manager": {
-      "version": "7.3.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.3.1.tgz",
-      "integrity": "sha512-fVS6fPxldsKY2nFvyT7IP78UO1/I2huG+AYu5AMjCT9wtl6JFiDnsv4uad4jQ0GTFzcUV5HShVeN96/17bTBag==",
+      "version": "7.7.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.7.1.tgz",
+      "integrity": "sha512-PytBif2SF+9SpEUKynYn5g1RHFddJUcyynGpztX3l/ik7KmZEv19WCMhUBkHXPU9es/VWGD3/zg3wg90+Dh2rA==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.3.1",
-        "@typescript-eslint/visitor-keys": "7.3.1"
+        "@typescript-eslint/types": "7.7.1",
+        "@typescript-eslint/visitor-keys": "7.7.1"
       },
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -2784,15 +2813,15 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils": {
-      "version": "7.3.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.3.1.tgz",
-      "integrity": "sha512-iFhaysxFsMDQlzJn+vr3OrxN8NmdQkHks4WaqD4QBnt5hsq234wcYdyQ9uquzJJIDAj5W4wQne3yEsYA6OmXGw==",
+      "version": "7.7.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.7.1.tgz",
+      "integrity": "sha512-ZksJLW3WF7o75zaBPScdW1Gbkwhd/lyeXGf1kQCxJaOeITscoSl0MjynVvCzuV5boUz/3fOI06Lz8La55mu29Q==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/typescript-estree": "7.3.1",
-        "@typescript-eslint/utils": "7.3.1",
+        "@typescript-eslint/typescript-estree": "7.7.1",
+        "@typescript-eslint/utils": "7.7.1",
         "debug": "^4.3.4",
-        "ts-api-utils": "^1.0.1"
+        "ts-api-utils": "^1.3.0"
       },
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -2811,9 +2840,9 @@
       }
     },
     "node_modules/@typescript-eslint/types": {
-      "version": "7.3.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.3.1.tgz",
-      "integrity": "sha512-2tUf3uWggBDl4S4183nivWQ2HqceOZh1U4hhu4p1tPiIJoRRXrab7Y+Y0p+dozYwZVvLPRI6r5wKe9kToF9FIw==",
+      "version": "7.7.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.7.1.tgz",
+      "integrity": "sha512-AmPmnGW1ZLTpWa+/2omPrPfR7BcbUU4oha5VIbSbS1a1Tv966bklvLNXxp3mrbc+P2j4MNOTfDffNsk4o0c6/w==",
       "dev": true,
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -2824,19 +2853,19 @@
       }
     },
     "node_modules/@typescript-eslint/typescript-estree": {
-      "version": "7.3.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.3.1.tgz",
-      "integrity": "sha512-tLpuqM46LVkduWP7JO7yVoWshpJuJzxDOPYIVWUUZbW+4dBpgGeUdl/fQkhuV0A8eGnphYw3pp8d2EnvPOfxmQ==",
+      "version": "7.7.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.7.1.tgz",
+      "integrity": "sha512-CXe0JHCXru8Fa36dteXqmH2YxngKJjkQLjxzoj6LYwzZ7qZvgsLSc+eqItCrqIop8Vl2UKoAi0StVWu97FQZIQ==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.3.1",
-        "@typescript-eslint/visitor-keys": "7.3.1",
+        "@typescript-eslint/types": "7.7.1",
+        "@typescript-eslint/visitor-keys": "7.7.1",
         "debug": "^4.3.4",
         "globby": "^11.1.0",
         "is-glob": "^4.0.3",
-        "minimatch": "9.0.3",
-        "semver": "^7.5.4",
-        "ts-api-utils": "^1.0.1"
+        "minimatch": "^9.0.4",
+        "semver": "^7.6.0",
+        "ts-api-utils": "^1.3.0"
       },
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -2852,18 +2881,18 @@
       }
     },
     "node_modules/@typescript-eslint/utils": {
-      "version": "7.3.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.3.1.tgz",
-      "integrity": "sha512-jIERm/6bYQ9HkynYlNZvXpzmXWZGhMbrOvq3jJzOSOlKXsVjrrolzWBjDW6/TvT5Q3WqaN4EkmcfdQwi9tDjBQ==",
+      "version": "7.7.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.7.1.tgz",
+      "integrity": "sha512-QUvBxPEaBXf41ZBbaidKICgVL8Hin0p6prQDu6bbetWo39BKbWJxRsErOzMNT1rXvTll+J7ChrbmMCXM9rsvOQ==",
       "dev": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.4.0",
-        "@types/json-schema": "^7.0.12",
-        "@types/semver": "^7.5.0",
-        "@typescript-eslint/scope-manager": "7.3.1",
-        "@typescript-eslint/types": "7.3.1",
-        "@typescript-eslint/typescript-estree": "7.3.1",
-        "semver": "^7.5.4"
+        "@types/json-schema": "^7.0.15",
+        "@types/semver": "^7.5.8",
+        "@typescript-eslint/scope-manager": "7.7.1",
+        "@typescript-eslint/types": "7.7.1",
+        "@typescript-eslint/typescript-estree": "7.7.1",
+        "semver": "^7.6.0"
       },
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -2877,13 +2906,13 @@
       }
     },
     "node_modules/@typescript-eslint/visitor-keys": {
-      "version": "7.3.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.3.1.tgz",
-      "integrity": "sha512-9RMXwQF8knsZvfv9tdi+4D/j7dMG28X/wMJ8Jj6eOHyHWwDW4ngQJcqEczSsqIKKjFiLFr40Mnr7a5ulDD3vmw==",
+      "version": "7.7.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.7.1.tgz",
+      "integrity": "sha512-gBL3Eq25uADw1LQ9kVpf3hRM+DWzs0uZknHYK3hq4jcTPqVCClHGDnB6UUUV2SFeBeA4KWHWbbLqmbGcZ4FYbw==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.3.1",
-        "eslint-visitor-keys": "^3.4.1"
+        "@typescript-eslint/types": "7.7.1",
+        "eslint-visitor-keys": "^3.4.3"
       },
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -3354,12 +3383,12 @@
       "dev": true
     },
     "node_modules/aria-query": {
-      "version": "5.1.3",
-      "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz",
-      "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==",
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
+      "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
       "dev": true,
       "dependencies": {
-        "deep-equal": "^2.0.5"
+        "dequal": "^2.0.3"
       }
     },
     "node_modules/array-back": {
@@ -3924,9 +3953,9 @@
       }
     },
     "node_modules/caniuse-lite": {
-      "version": "1.0.30001600",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001600.tgz",
-      "integrity": "sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ==",
+      "version": "1.0.30001612",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001612.tgz",
+      "integrity": "sha512-lFgnZ07UhaCcsSZgWW0K5j4e69dK1u/ltrL9lTUiFOwNHs12S3UMIEYgBV0Z6C6hRDev7iRnMzzYmKabYdXF9g==",
       "funding": [
         {
           "type": "opencollective",
@@ -4195,9 +4224,9 @@
       }
     },
     "node_modules/clsx": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz",
-      "integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==",
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+      "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
       "engines": {
         "node": ">=6"
       }
@@ -4476,9 +4505,9 @@
       }
     },
     "node_modules/core-js": {
-      "version": "3.36.1",
-      "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.36.1.tgz",
-      "integrity": "sha512-BTvUrwxVBezj5SZ3f10ImnX2oRByMxql3EimVqMysepbC9EeMUOpLwdy6Eoili2x6E4kf+ZUB5k/+Jv55alPfA==",
+      "version": "3.37.0",
+      "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.37.0.tgz",
+      "integrity": "sha512-fu5vHevQ8ZG4og+LXug8ulUtVxjOcEYvifJr7L5Bfq9GOztVqsKd9/59hUk2ZSbCrS3BqUr3EpaYGIYzq7g3Ug==",
       "hasInstallScript": true,
       "funding": {
         "type": "opencollective",
@@ -4605,16 +4634,16 @@
       "integrity": "sha512-X1xgQhkZ9n94WDwntqst5D/FKkmiU0GlJSFZSV3kLvyJ1WC5VeyoXDOuleUD+SIuH9C7W05is++0Woh0CGfKjQ=="
     },
     "node_modules/css-loader": {
-      "version": "6.10.0",
-      "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.10.0.tgz",
-      "integrity": "sha512-LTSA/jWbwdMlk+rhmElbDR2vbtQoTBPr7fkJE+mxrHj+7ru0hUmHafDRzWIjIHTwpitWVaqY2/UWGRca3yUgRw==",
+      "version": "6.11.0",
+      "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz",
+      "integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==",
       "dev": true,
       "dependencies": {
         "icss-utils": "^5.1.0",
         "postcss": "^8.4.33",
-        "postcss-modules-extract-imports": "^3.0.0",
-        "postcss-modules-local-by-default": "^4.0.4",
-        "postcss-modules-scope": "^3.1.1",
+        "postcss-modules-extract-imports": "^3.1.0",
+        "postcss-modules-local-by-default": "^4.0.5",
+        "postcss-modules-scope": "^3.2.0",
         "postcss-modules-values": "^4.0.0",
         "postcss-value-parser": "^4.2.0",
         "semver": "^7.5.4"
@@ -4987,9 +5016,9 @@
       }
     },
     "node_modules/dedent": {
-      "version": "1.5.1",
-      "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz",
-      "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==",
+      "version": "1.5.3",
+      "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz",
+      "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==",
       "dev": true,
       "peerDependencies": {
         "babel-plugin-macros": "^3.1.0"
@@ -5318,9 +5347,9 @@
       }
     },
     "node_modules/dts-bundle-generator": {
-      "version": "9.3.1",
-      "resolved": "https://registry.npmjs.org/dts-bundle-generator/-/dts-bundle-generator-9.3.1.tgz",
-      "integrity": "sha512-1/nMT7LFOkXbrL1ZvLpzrjNbfX090LZ64nLIXVmet557mshFCGP/oTiQiZenafJZ6GsmRQLTYKSlQnkxK8tsTw==",
+      "version": "9.5.1",
+      "resolved": "https://registry.npmjs.org/dts-bundle-generator/-/dts-bundle-generator-9.5.1.tgz",
+      "integrity": "sha512-DxpJOb2FNnEyOzMkG11sxO2dmxPjthoVWxfKqWYJ/bI/rT1rvTMktF5EKjAYrRZu6Z6t3NhOUZ0sZ5ZXevOfbA==",
       "dev": true,
       "dependencies": {
         "typescript": ">=5.0.2",
@@ -5363,9 +5392,9 @@
       "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ=="
     },
     "node_modules/electron-to-chromium": {
-      "version": "1.4.715",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.715.tgz",
-      "integrity": "sha512-XzWNH4ZSa9BwVUQSDorPWAUQ5WGuYz7zJUNpNif40zFCiCl20t8zgylmreNmn26h5kiyw2lg7RfTmeMBsDklqg=="
+      "version": "1.4.747",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.747.tgz",
+      "integrity": "sha512-+FnSWZIAvFHbsNVmUxhEqWiaOiPMcfum1GQzlWCg/wLigVtshOsjXHyEFfmt6cFK6+HkS3QOJBv6/3OPumbBfw=="
     },
     "node_modules/element-size": {
       "version": "1.1.1",
@@ -5472,9 +5501,9 @@
       }
     },
     "node_modules/envinfo": {
-      "version": "7.11.1",
-      "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.11.1.tgz",
-      "integrity": "sha512-8PiZgZNIB4q/Lw4AhOvAfB/ityHAd2bli3lESSWmWSzSsl5dKpy5N1d1Rfkd2teq/g9xN90lc6o98DOjMeYHpg==",
+      "version": "7.12.0",
+      "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.12.0.tgz",
+      "integrity": "sha512-Iw9rQJBGpJRd3rwXm9ft/JiGoAZmLxxJZELYDQoPRZ4USVhkKtIcNBPw6U+/K2mBpaqM25JSV6Yl4Az9vO2wJg==",
       "dev": true,
       "bin": {
         "envinfo": "dist/cli.js"
@@ -5492,9 +5521,9 @@
       }
     },
     "node_modules/es-abstract": {
-      "version": "1.23.2",
-      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.2.tgz",
-      "integrity": "sha512-60s3Xv2T2p1ICykc7c+DNDPLDMm9t4QxCOUU0K9JxiLjM3C1zB9YVdN7tjxrFd4+AkZ8CdX1ovUga4P2+1e+/w==",
+      "version": "1.23.3",
+      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz",
+      "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==",
       "dev": true,
       "dependencies": {
         "array-buffer-byte-length": "^1.0.1",
@@ -5536,11 +5565,11 @@
         "safe-regex-test": "^1.0.3",
         "string.prototype.trim": "^1.2.9",
         "string.prototype.trimend": "^1.0.8",
-        "string.prototype.trimstart": "^1.0.7",
+        "string.prototype.trimstart": "^1.0.8",
         "typed-array-buffer": "^1.0.2",
         "typed-array-byte-length": "^1.0.1",
         "typed-array-byte-offset": "^1.0.2",
-        "typed-array-length": "^1.0.5",
+        "typed-array-length": "^1.0.6",
         "unbox-primitive": "^1.0.2",
         "which-typed-array": "^1.1.15"
       },
@@ -5618,9 +5647,9 @@
       }
     },
     "node_modules/es-module-lexer": {
-      "version": "1.4.2",
-      "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.2.tgz",
-      "integrity": "sha512-7nOqkomXZEaxUDJw21XZNtRk739QvrPSoZoRtbsEfcii00vdzZUh6zh1CQwHhrib8MdEtJfv5rJiGeb4KuV/vw==",
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.0.tgz",
+      "integrity": "sha512-pqrTKmwEIgafsYZAGw9kszYzmagcE/n4dbgwGWLEXg7J4QFJVQRBld8j3Q3GNez79jzxZshq0bcT962QHOghjw==",
       "dev": true
     },
     "node_modules/es-object-atoms": {
@@ -6639,9 +6668,9 @@
       "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA=="
     },
     "node_modules/gl-text": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/gl-text/-/gl-text-1.3.1.tgz",
-      "integrity": "sha512-/f5gcEMiZd+UTBJLTl3D+CkCB/0UFGTx3nflH8ZmyWcLkZhsZ1+Xx5YYkw2rgWAzgPeE35xCqBuHSoMKQVsR+w==",
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/gl-text/-/gl-text-1.4.0.tgz",
+      "integrity": "sha512-o47+XBqLCj1efmuNyCHt7/UEJmB9l66ql7pnobD6p+sgmBUdzfMZXIF0zD2+KRfpd99DJN+QXdvTFAGCKCVSmQ==",
       "dependencies": {
         "bit-twiddle": "^1.0.2",
         "color-normalize": "^1.5.0",
@@ -7471,9 +7500,9 @@
       "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
     },
     "node_modules/inline-style-parser": {
-      "version": "0.2.2",
-      "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.2.tgz",
-      "integrity": "sha512-EcKzdTHVe8wFVOGEYXiW9WmJXPjqi1T+234YpJr98RiFYKHV3cdy1+3mkTE+KHTHxFFLH51SfaGOoUdW+v7ViQ=="
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.3.tgz",
+      "integrity": "sha512-qlD8YNDqyTKTyuITrDOffsl6Tdhv+UC4hcdAVuQsK4IMQ99nSgd1MIA/Q+jQYoh9r3hVUXhYh7urSRmXPkW04g=="
     },
     "node_modules/internal-slot": {
       "version": "1.0.7",
@@ -9883,9 +9912,9 @@
       }
     },
     "node_modules/micromark-core-commonmark": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.0.tgz",
-      "integrity": "sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA==",
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.1.tgz",
+      "integrity": "sha512-CUQyKr1e///ZODyD1U3xit6zXwy1a8q2a1S1HKtIlmgvurrEpaw/Y9y6KSIbF8P59cn/NjzHyO+Q2fAyYLQrAA==",
       "funding": [
         {
           "type": "GitHub Sponsors",
@@ -10219,9 +10248,9 @@
       }
     },
     "node_modules/micromark-util-subtokenize": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.0.tgz",
-      "integrity": "sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg==",
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.1.tgz",
+      "integrity": "sha512-jZNtiFl/1aY73yS3UGQkutD0UbhTt68qnRpw2Pifmz5wV9h8gOVsN70v+Lq/f1rKaU/W8pxRe8y8Q9FX1AOe1Q==",
       "funding": [
         {
           "type": "GitHub Sponsors",
@@ -10320,9 +10349,9 @@
       }
     },
     "node_modules/minimatch": {
-      "version": "9.0.3",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
-      "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+      "version": "9.0.4",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz",
+      "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==",
       "dev": true,
       "dependencies": {
         "brace-expansion": "^2.0.1"
@@ -10580,9 +10609,9 @@
       }
     },
     "node_modules/nwsapi": {
-      "version": "2.2.7",
-      "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz",
-      "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==",
+      "version": "2.2.9",
+      "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.9.tgz",
+      "integrity": "sha512-2f3F0SEEer8bBu0dsNCFF50N0cTThV1nWFYcEYFZttdW0lDAoybv9cQoK7X7/68Z89S7FoRrVjP1LPX4XRf9vg==",
       "dev": true
     },
     "node_modules/object-assign": {
@@ -11073,14 +11102,14 @@
       }
     },
     "node_modules/plotly.js": {
-      "version": "2.30.1",
-      "resolved": "https://registry.npmjs.org/plotly.js/-/plotly.js-2.30.1.tgz",
-      "integrity": "sha512-KE3KeM4B6qtjPU7FGOxklmwYua4nWGgr48BRMWZVysZjphlSaQLzvUAieFlUCfPBPfJIRBLxFQy1KHMIQgfwrA==",
+      "version": "2.32.0",
+      "resolved": "https://registry.npmjs.org/plotly.js/-/plotly.js-2.32.0.tgz",
+      "integrity": "sha512-QBYyfVFs1XdoXQBq/f7SoiqQD/BEyDA5WwvN1NwY4ZTrTX6GmJ5jE5ydlt1I4K8i5W6H1atgti31jcSYD6StKA==",
       "dependencies": {
         "@plotly/d3": "3.8.1",
         "@plotly/d3-sankey": "0.7.2",
         "@plotly/d3-sankey-circular": "0.33.1",
-        "@plotly/mapbox-gl": "v1.13.4",
+        "@plotly/mapbox-gl": "1.13.4",
         "@turf/area": "^6.4.0",
         "@turf/bbox": "^6.4.0",
         "@turf/centroid": "^6.0.2",
@@ -11101,7 +11130,7 @@
         "d3-time-format": "^2.2.3",
         "fast-isnumeric": "^1.1.4",
         "gl-mat4": "^1.2.0",
-        "gl-text": "^1.3.1",
+        "gl-text": "^1.4.0",
         "has-hover": "^1.0.1",
         "has-passive-events": "^1.0.0",
         "is-mobile": "^4.0.0",
@@ -11176,9 +11205,9 @@
       }
     },
     "node_modules/postcss-modules-extract-imports": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz",
-      "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==",
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz",
+      "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==",
       "dev": true,
       "engines": {
         "node": "^10 || ^12 || >= 14"
@@ -11188,9 +11217,9 @@
       }
     },
     "node_modules/postcss-modules-local-by-default": {
-      "version": "4.0.4",
-      "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.4.tgz",
-      "integrity": "sha512-L4QzMnOdVwRm1Qb8m4x8jsZzKAaPAgrUF1r/hjDR2Xj7R+8Zsf97jAlSQzWtKx5YNiNGN8QxmPFIc/sh+RQl+Q==",
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz",
+      "integrity": "sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==",
       "dev": true,
       "dependencies": {
         "icss-utils": "^5.0.0",
@@ -11205,9 +11234,9 @@
       }
     },
     "node_modules/postcss-modules-scope": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.1.1.tgz",
-      "integrity": "sha512-uZgqzdTleelWjzJY+Fhti6F3C9iF1JR/dODLs/JDefozYcKTBCdD8BIl6nNPbTbcLnGrk56hzwZC2DaGNvYjzA==",
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz",
+      "integrity": "sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==",
       "dev": true,
       "dependencies": {
         "postcss-selector-parser": "^6.0.4"
@@ -11353,9 +11382,9 @@
       "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
     },
     "node_modules/property-information": {
-      "version": "6.4.1",
-      "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.4.1.tgz",
-      "integrity": "sha512-OHYtXfu5aI2sS2LWFSN5rgJjrQ4pCy8i1jubJLe2QvMF8JJ++HXTUIVWFLfXJoaOfvYYjk2SN8J2wFUWIGXT4w==",
+      "version": "6.5.0",
+      "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz",
+      "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==",
       "funding": {
         "type": "github",
         "url": "https://github.com/sponsors/wooorm"
@@ -11540,13 +11569,13 @@
       }
     },
     "node_modules/react-jsx-parser/node_modules/@types/react": {
-      "version": "17.0.79",
-      "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.79.tgz",
-      "integrity": "sha512-gavKA8AwJAML9zWHuiQRASjrrPJHbT/zrUDHiUGUf+l5a3pkEd6atvjjq+8y2vfRHBJLQJjFpxSa9I8qe9zHAw==",
+      "version": "17.0.80",
+      "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.80.tgz",
+      "integrity": "sha512-LrgHIu2lEtIo8M7d1FcI3BdwXWoRQwMoXOZ7+dPTW0lYREjmlHl3P0U1VD0i/9tppOuv8/sam7sOjx34TxSFbA==",
       "optional": true,
       "dependencies": {
         "@types/prop-types": "*",
-        "@types/scheduler": "*",
+        "@types/scheduler": "^0.16",
         "csstype": "^3.0.2"
       }
     },
@@ -11597,11 +11626,11 @@
       }
     },
     "node_modules/react-router": {
-      "version": "6.22.3",
-      "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz",
-      "integrity": "sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==",
+      "version": "6.23.0",
+      "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.23.0.tgz",
+      "integrity": "sha512-wPMZ8S2TuPadH0sF5irFGjkNLIcRvOSaEe7v+JER8508dyJumm6XZB1u5kztlX0RVq6AzRVndzqcUh6sFIauzA==",
       "dependencies": {
-        "@remix-run/router": "1.15.3"
+        "@remix-run/router": "1.16.0"
       },
       "engines": {
         "node": ">=14.0.0"
@@ -11611,12 +11640,12 @@
       }
     },
     "node_modules/react-router-dom": {
-      "version": "6.22.3",
-      "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.3.tgz",
-      "integrity": "sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==",
+      "version": "6.23.0",
+      "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.23.0.tgz",
+      "integrity": "sha512-Q9YaSYvubwgbal2c9DJKfx6hTNoBp3iJDsl+Duva/DwxoJH+OTXkxGpql4iUK2sla/8z4RpjAm6EWx1qUDuopQ==",
       "dependencies": {
-        "@remix-run/router": "1.15.3",
-        "react-router": "6.22.3"
+        "@remix-run/router": "1.16.0",
+        "react-router": "6.23.0"
       },
       "engines": {
         "node": ">=14.0.0"
@@ -12733,9 +12762,9 @@
       }
     },
     "node_modules/stringify-entities": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.3.tgz",
-      "integrity": "sha512-BP9nNHMhhfcMbiuQKCqMjhDP5yBCAxsPu4pHFFzJ6Alo9dZgY4VLDPutXqIjpRiMoKdp7Av85Gr73Q5uH9k7+g==",
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz",
+      "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==",
       "dependencies": {
         "character-entities-html4": "^2.0.0",
         "character-entities-legacy": "^3.0.0"
@@ -12805,11 +12834,11 @@
       "integrity": "sha512-i0TFx4wPcO0FwX+4RkLJi1MxmcTv90jNZgxMu9XRnMXMeFUY1VJlIoXpZunPUvUUqbCT1pg5PEkFqqpcaElNaA=="
     },
     "node_modules/style-to-object": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.5.tgz",
-      "integrity": "sha512-rDRwHtoDD3UMMrmZ6BzOW0naTjMsVZLIjsGleSKS/0Oz+cgCfAPRspaqJuE8rDzpKha/nEvnM0IF4seEAZUTKQ==",
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.6.tgz",
+      "integrity": "sha512-khxq+Qm3xEyZfKd/y9L3oIWQimxuc4STrQKtQn8aSDRHb8mFgpukgX1hdzfrMEW6JCjyJ8p89x+IUMVnCBI1PA==",
       "dependencies": {
-        "inline-style-parser": "0.2.2"
+        "inline-style-parser": "0.2.3"
       }
     },
     "node_modules/stylis": {
@@ -12940,9 +12969,9 @@
       }
     },
     "node_modules/terser": {
-      "version": "5.29.2",
-      "resolved": "https://registry.npmjs.org/terser/-/terser-5.29.2.tgz",
-      "integrity": "sha512-ZiGkhUBIM+7LwkNjXYJq8svgkd+QK3UUr0wJqY4MieaezBSAIPgbSPZyIx0idM6XWK5CMzSWa8MJIzmRcB8Caw==",
+      "version": "5.30.4",
+      "resolved": "https://registry.npmjs.org/terser/-/terser-5.30.4.tgz",
+      "integrity": "sha512-xRdd0v64a8mFK9bnsKVdoNP9GQIKUAaJPTaqEQDL4w/J8WaW4sWXXoMZ+6SimPkfT5bElreXf8m9HnmPc3E1BQ==",
       "dev": true,
       "dependencies": {
         "@jridgewell/source-map": "^0.3.3",
@@ -13441,9 +13470,9 @@
       }
     },
     "node_modules/typedoc": {
-      "version": "0.25.12",
-      "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.12.tgz",
-      "integrity": "sha512-F+qhkK2VoTweDXd1c42GS/By2DvI2uDF4/EpG424dTexSHdtCH52C6IcAvMA6jR3DzAWZjHpUOW+E02kyPNUNw==",
+      "version": "0.25.13",
+      "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.13.tgz",
+      "integrity": "sha512-pQqiwiJ+Z4pigfOnnysObszLiU3mVLWAExSPf+Mu06G/qsc3wzbuM56SZQvONhHLncLUhYzOVkjFFpFfL5AzhQ==",
       "dev": true,
       "dependencies": {
         "lunr": "^2.3.9",
@@ -13474,9 +13503,9 @@
       }
     },
     "node_modules/typescript": {
-      "version": "5.4.3",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz",
-      "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==",
+      "version": "5.4.5",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
+      "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
       "dev": true,
       "bin": {
         "tsc": "bin/tsc",

+ 2 - 1
frontend/taipy-gui/src/components/Taipy/AutoLoadingTable.tsx

@@ -75,6 +75,7 @@ import {
 } from "../../utils/hooks";
 import TableFilter, { FilterDesc } from "./TableFilter";
 import { getSuffixedClassNames, getUpdateVar } from "./utils";
+import { emptyArray } from "../../utils";
 
 interface RowData {
     colsOrder: string[];
@@ -177,7 +178,7 @@ const AutoLoadingTable = (props: TaipyTableProps) => {
         height = "80vh",
         width = "100%",
         updateVars,
-        selected = [],
+        selected = emptyArray,
         pageSize = 100,
         defaultKey = "",
         onEdit = "",

+ 1 - 1
frontend/taipy-gui/src/components/Taipy/Chart.tsx

@@ -233,7 +233,7 @@ const TaipyPlotlyButtons: ModeBarButtonAny[] = [
             if (height) {
                 gd.attributeStyleMap.set("height", height);
             } else {
-                gd.setAttribute("data-height", getComputedStyle(gd.querySelector(".svg-container") || gd).height)
+                gd.setAttribute("data-height", getComputedStyle(gd).height)
             }
             window.dispatchEvent(new Event('resize'));
         },

+ 2 - 1
frontend/taipy-gui/src/components/Taipy/Menu.tsx

@@ -27,6 +27,7 @@ import { SingleItem } from "./lovUtils";
 import { createSendActionNameAction } from "../../context/taipyReducers";
 import { MenuProps } from "../../utils/lov";
 import { useClassNames, useDispatch, useModule } from "../../utils/hooks";
+import { emptyArray } from "../../utils";
 
 const boxDrawerStyle = { overflowX: "hidden" } as CSSProperties;
 const headerSx = { padding: 0 };
@@ -34,7 +35,7 @@ const avatarSx = { bgcolor: (theme: Theme) => theme.palette.text.primary };
 const baseTitleProps = { noWrap: true, variant: "h6" } as const;
 
 const Menu = (props: MenuProps) => {
-    const { label, onAction = "", lov, width, inactiveIds = [], active = true } = props;
+    const { label, onAction = "", lov, width, inactiveIds = emptyArray, active = true } = props;
     const [selectedValue, setSelectedValue] = useState("");
     const [opened, setOpened] = useState(false);
     const dispatch = useDispatch();

+ 29 - 7
frontend/taipy-gui/src/components/Taipy/PaginatedTable.tsx

@@ -81,6 +81,7 @@ import {
 } from "../../utils/hooks";
 import TableFilter, { FilterDesc } from "./TableFilter";
 import { getSuffixedClassNames, getUpdateVar } from "./utils";
+import { emptyArray } from "../../utils";
 
 const loadingStyle: CSSProperties = { width: "100%", height: "3em", textAlign: "right", verticalAlign: "center" };
 const skelSx = { width: "100%", height: "3em" };
@@ -95,7 +96,7 @@ const PaginatedTable = (props: TaipyPaginatedTableProps) => {
         allowAllRows = false,
         showAll = false,
         height,
-        selected = [],
+        selected = emptyArray,
         updateVars,
         onEdit = "",
         onDelete = "",
@@ -147,7 +148,12 @@ const PaginatedTable = (props: TaipyPaginatedTableProps) => {
                         col.tooltip = props.tooltip;
                     }
                 });
-                addDeleteColumn((active && (onAdd || onDelete) ? 1 : 0) + (active && filter ? 1 : 0) + (active && downloadable ? 1 : 0), baseColumns);
+                addDeleteColumn(
+                    (active && (onAdd || onDelete) ? 1 : 0) +
+                        (active && filter ? 1 : 0) +
+                        (active && downloadable ? 1 : 0),
+                    baseColumns
+                );
                 const colsOrder = Object.keys(baseColumns).sort(getsortByIndex(baseColumns));
                 const styTt = colsOrder.reduce<Record<string, Record<string, string>>>((pv, col) => {
                     if (baseColumns[col].style) {
@@ -178,7 +184,18 @@ const PaginatedTable = (props: TaipyPaginatedTableProps) => {
             hNan,
             false,
         ];
-    }, [active, editable, onAdd, onDelete, baseColumns, props.lineStyle, props.tooltip, props.nanValue, props.filter, downloadable]);
+    }, [
+        active,
+        editable,
+        onAdd,
+        onDelete,
+        baseColumns,
+        props.lineStyle,
+        props.tooltip,
+        props.nanValue,
+        props.filter,
+        downloadable,
+    ]);
 
     useDispatchRequestUpdateOnFirstRender(dispatch, id, module, updateVars);
 
@@ -267,7 +284,7 @@ const PaginatedTable = (props: TaipyPaginatedTableProps) => {
         dispatch,
         module,
         compare,
-        onCompare
+        onCompare,
     ]);
 
     const onSort = useCallback(
@@ -359,7 +376,12 @@ const PaginatedTable = (props: TaipyPaginatedTableProps) => {
     }, [pageSizeOptions, allowAllRows, pageSize]);
 
     const { rows, rowCount, filteredCount, compRows } = useMemo(() => {
-        const ret = { rows: [], rowCount: 0, filteredCount: 0, compRows: [] } as { rows: RowType[]; rowCount: number; filteredCount: number; compRows: RowType[] };
+        const ret = { rows: [], rowCount: 0, filteredCount: 0, compRows: [] } as {
+            rows: RowType[];
+            rowCount: number;
+            filteredCount: number;
+            compRows: RowType[];
+        };
         if (value) {
             if (value.data) {
                 ret.rows = value.data as RowType[];
@@ -367,7 +389,7 @@ const PaginatedTable = (props: TaipyPaginatedTableProps) => {
             if (value.rowcount) {
                 ret.rowCount = value.rowcount as unknown as number;
                 if (value.fullrowcount && value.rowcount != value.fullrowcount) {
-                    ret.filteredCount = (value.fullrowcount as unknown as number - ret.rowCount);
+                    ret.filteredCount = (value.fullrowcount as unknown as number) - ret.rowCount;
                 }
             }
             if (value.comp) {
@@ -413,7 +435,7 @@ const PaginatedTable = (props: TaipyPaginatedTableProps) => {
                     index: getRowIndex(rows[rowIndex], rowIndex, startIndex),
                     col: colName === undefined ? null : colName,
                     value,
-                    reason: value === undefined ? "click": "button",
+                    reason: value === undefined ? "click" : "button",
                     user_data: userData,
                 })
             ),

+ 1 - 3
frontend/taipy-gui/src/components/pages/TaipyRendered.tsx

@@ -23,7 +23,7 @@ import { getRegisteredComponents } from "../Taipy";
 import { unregisteredRender, renderError } from "../Taipy/Unregistered";
 import { createPartialAction } from "../../context/taipyReducers";
 import ErrorFallback from "../../utils/ErrorBoundary";
-import { getBaseURL } from "../../utils";
+import { emptyArray, getBaseURL } from "../../utils";
 
 interface TaipyRenderedProps {
     path?: string;
@@ -61,8 +61,6 @@ const setStyle = (id: string, styleString: string): void => {
     }
 };
 
-const emptyArray: string[] = [];
-
 interface PageState {
     jsx?: string;
     module?: string;

+ 2 - 0
frontend/taipy-gui/src/utils/index.ts

@@ -206,3 +206,5 @@ export const TIMEZONE_CLIENT = Intl.DateTimeFormat().resolvedOptions().timeZone;
 export const getBaseURL = (): string => {
     return window.taipyConfig?.baseURL || "/";
 };
+
+export const emptyArray = [];

+ 123 - 130
frontend/taipy/package-lock.json

@@ -41,7 +41,6 @@
       }
     },
     "../../taipy/gui/webapp": {
-      "name": "taipy-gui",
       "version": "3.2.0"
     },
     "node_modules/@aashutoshrathi/word-wrap": {
@@ -171,9 +170,9 @@
       }
     },
     "node_modules/@babel/runtime": {
-      "version": "7.24.1",
-      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz",
-      "integrity": "sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==",
+      "version": "7.24.4",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.4.tgz",
+      "integrity": "sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==",
       "dependencies": {
         "regenerator-runtime": "^0.14.0"
       },
@@ -275,9 +274,9 @@
       }
     },
     "node_modules/@emotion/serialize": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.3.tgz",
-      "integrity": "sha512-iD4D6QVZFDhcbH0RAG1uVu1CwVLMWUkCvAqqlewO/rxf8+87yIBAlt4+AxMiiKPLs5hFc0owNk/sLLAOROw3cA==",
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.4.tgz",
+      "integrity": "sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ==",
       "dependencies": {
         "@emotion/hash": "^0.9.1",
         "@emotion/memoize": "^0.8.1",
@@ -292,14 +291,14 @@
       "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA=="
     },
     "node_modules/@emotion/styled": {
-      "version": "11.11.0",
-      "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.0.tgz",
-      "integrity": "sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==",
+      "version": "11.11.5",
+      "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.5.tgz",
+      "integrity": "sha512-/ZjjnaNKvuMPxcIiUkf/9SHoG4Q196DRl1w82hQ3WCsjo1IUR8uaGWrC6a87CrYAW0Kb/pK7hk8BnLgLRi9KoQ==",
       "dependencies": {
         "@babel/runtime": "^7.18.3",
         "@emotion/babel-plugin": "^11.11.0",
-        "@emotion/is-prop-valid": "^1.2.1",
-        "@emotion/serialize": "^1.1.2",
+        "@emotion/is-prop-valid": "^1.2.2",
+        "@emotion/serialize": "^1.1.4",
         "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1",
         "@emotion/utils": "^1.2.1"
       },
@@ -498,9 +497,9 @@
       }
     },
     "node_modules/@humanwhocodes/object-schema": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz",
-      "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==",
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
+      "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
       "dev": true
     },
     "node_modules/@jest/schemas": {
@@ -653,18 +652,18 @@
       }
     },
     "node_modules/@mui/core-downloads-tracker": {
-      "version": "5.15.14",
-      "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.14.tgz",
-      "integrity": "sha512-on75VMd0XqZfaQW+9pGjSNiqW+ghc5E2ZSLRBXwcXl/C4YzjfyjrLPhrEpKnR9Uym9KXBvxrhoHfPcczYHweyA==",
+      "version": "5.15.15",
+      "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.15.tgz",
+      "integrity": "sha512-aXnw29OWQ6I5A47iuWEI6qSSUfH6G/aCsW9KmW3LiFqr7uXZBK4Ks+z8G+qeIub8k0T5CMqlT2q0L+ZJTMrqpg==",
       "funding": {
         "type": "opencollective",
         "url": "https://opencollective.com/mui-org"
       }
     },
     "node_modules/@mui/icons-material": {
-      "version": "5.15.14",
-      "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.14.tgz",
-      "integrity": "sha512-vj/51k7MdFmt+XVw94sl30SCvGx6+wJLsNYjZRgxhS6y3UtnWnypMOsm3Kmg8TN+P0dqwsjy4/fX7B1HufJIhw==",
+      "version": "5.15.15",
+      "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.15.tgz",
+      "integrity": "sha512-kkeU/pe+hABcYDH6Uqy8RmIsr2S/y5bP2rp+Gat4CcRjCcVne6KudS1NrZQhUCRysrTDCAhcbcf9gt+/+pGO2g==",
       "dependencies": {
         "@babel/runtime": "^7.23.9"
       },
@@ -687,14 +686,14 @@
       }
     },
     "node_modules/@mui/material": {
-      "version": "5.15.14",
-      "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.14.tgz",
-      "integrity": "sha512-kEbRw6fASdQ1SQ7LVdWR5OlWV3y7Y54ZxkLzd6LV5tmz+NpO3MJKZXSfgR0LHMP7meKsPiMm4AuzV0pXDpk/BQ==",
+      "version": "5.15.15",
+      "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.15.tgz",
+      "integrity": "sha512-3zvWayJ+E1kzoIsvwyEvkTUKVKt1AjchFFns+JtluHCuvxgKcLSRJTADw37k0doaRtVAsyh8bz9Afqzv+KYrIA==",
       "dependencies": {
         "@babel/runtime": "^7.23.9",
         "@mui/base": "5.0.0-beta.40",
-        "@mui/core-downloads-tracker": "^5.15.14",
-        "@mui/system": "^5.15.14",
+        "@mui/core-downloads-tracker": "^5.15.15",
+        "@mui/system": "^5.15.15",
         "@mui/types": "^7.2.14",
         "@mui/utils": "^5.15.14",
         "@types/react-transition-group": "^4.4.10",
@@ -788,9 +787,9 @@
       }
     },
     "node_modules/@mui/system": {
-      "version": "5.15.14",
-      "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.14.tgz",
-      "integrity": "sha512-auXLXzUaCSSOLqJXmsAaq7P96VPRXg2Rrz6OHNV7lr+kB8lobUF+/N84Vd9C4G/wvCXYPs5TYuuGBRhcGbiBGg==",
+      "version": "5.15.15",
+      "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.15.tgz",
+      "integrity": "sha512-aulox6N1dnu5PABsfxVGOZffDVmlxPOVgj56HrUnJE8MCSh8lOvvkd47cebIVQQYAjpwieXQXiDPj5pwM40jTQ==",
       "dependencies": {
         "@babel/runtime": "^7.23.9",
         "@mui/private-theming": "^5.15.14",
@@ -867,9 +866,9 @@
       }
     },
     "node_modules/@mui/x-date-pickers": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.0.0.tgz",
-      "integrity": "sha512-/9mp4O2WMixHOso63DBoZVfJVYGrzOHF5voheV2tYQ4XqDdTKp2AdWS3oh8PGwrsvCzqkvb3quzTqhKoEsJUwA==",
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.2.0.tgz",
+      "integrity": "sha512-hsXugZ+n1ZnHRYzf7+PFrjZ44T+FyGZmTreBmH0M2RUaAblgK+A1V3KNLT+r4Y9gJLH+92LwePxQ9xyfR+E51A==",
       "dependencies": {
         "@babel/runtime": "^7.24.0",
         "@mui/base": "^5.0.0-beta.40",
@@ -932,9 +931,9 @@
       }
     },
     "node_modules/@mui/x-tree-view": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/@mui/x-tree-view/-/x-tree-view-7.0.0.tgz",
-      "integrity": "sha512-9nus8fesjBT/V/SzjZKhABG3qvGtupkccoOnz43ebLts8MSYtR8jwnOMArwrVnzrEoaS4F4dv3M8SYVQkHWfaQ==",
+      "version": "7.3.0",
+      "resolved": "https://registry.npmjs.org/@mui/x-tree-view/-/x-tree-view-7.3.0.tgz",
+      "integrity": "sha512-zPLtY4UP4UrglAdVRphE3Ow2UVUNKo+YkiF5z6VRqMenZBiMY+CkHSC3T+xzlAz2sSiiLZdiYJFqEpjPJI3Fcw==",
       "dependencies": {
         "@babel/runtime": "^7.24.0",
         "@mui/base": "^5.0.0-beta.40",
@@ -1085,9 +1084,9 @@
       "dev": true
     },
     "node_modules/@types/eslint": {
-      "version": "8.56.6",
-      "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.6.tgz",
-      "integrity": "sha512-ymwc+qb1XkjT/gfoQwxIeHZ6ixH23A+tCT2ADSA/DPVKzAjwYkTXBMCQ/f6fe4wEa85Lhp26VPeUxI7wMhAi7A==",
+      "version": "8.56.10",
+      "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz",
+      "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==",
       "dev": true,
       "dependencies": {
         "@types/estree": "*",
@@ -1150,9 +1149,9 @@
       "dev": true
     },
     "node_modules/@types/node": {
-      "version": "20.11.30",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz",
-      "integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==",
+      "version": "20.12.7",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz",
+      "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==",
       "dev": true,
       "dependencies": {
         "undici-types": "~5.26.4"
@@ -1169,12 +1168,11 @@
       "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q=="
     },
     "node_modules/@types/react": {
-      "version": "18.2.70",
-      "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.70.tgz",
-      "integrity": "sha512-hjlM2hho2vqklPhopNkXkdkeq6Lv8WSZTpr7956zY+3WS5cfYUewtCzsJLsbW5dEv3lfSeQ4W14ZFeKC437JRQ==",
+      "version": "18.2.79",
+      "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.79.tgz",
+      "integrity": "sha512-RwGAGXPl9kSXwdNTafkOEuFrTBD5SA2B3iEB96xi8+xu5ddUa/cpvyVCSNn+asgLCTHkb5ZxN8gbuibYJi4s1w==",
       "dependencies": {
         "@types/prop-types": "*",
-        "@types/scheduler": "*",
         "csstype": "^3.0.2"
       }
     },
@@ -1186,11 +1184,6 @@
         "@types/react": "*"
       }
     },
-    "node_modules/@types/scheduler": {
-      "version": "0.16.8",
-      "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz",
-      "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A=="
-    },
     "node_modules/@types/semver": {
       "version": "7.5.8",
       "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz",
@@ -1213,22 +1206,22 @@
       "dev": true
     },
     "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "7.3.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.3.1.tgz",
-      "integrity": "sha512-STEDMVQGww5lhCuNXVSQfbfuNII5E08QWkvAw5Qwf+bj2WT+JkG1uc+5/vXA3AOYMDHVOSpL+9rcbEUiHIm2dw==",
+      "version": "7.7.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.7.0.tgz",
+      "integrity": "sha512-GJWR0YnfrKnsRoluVO3PRb9r5aMZriiMMM/RHj5nnTrBy1/wIgk76XCtCKcnXGjpZQJQRFtGV9/0JJ6n30uwpQ==",
       "dev": true,
       "dependencies": {
-        "@eslint-community/regexpp": "^4.5.1",
-        "@typescript-eslint/scope-manager": "7.3.1",
-        "@typescript-eslint/type-utils": "7.3.1",
-        "@typescript-eslint/utils": "7.3.1",
-        "@typescript-eslint/visitor-keys": "7.3.1",
+        "@eslint-community/regexpp": "^4.10.0",
+        "@typescript-eslint/scope-manager": "7.7.0",
+        "@typescript-eslint/type-utils": "7.7.0",
+        "@typescript-eslint/utils": "7.7.0",
+        "@typescript-eslint/visitor-keys": "7.7.0",
         "debug": "^4.3.4",
         "graphemer": "^1.4.0",
-        "ignore": "^5.2.4",
+        "ignore": "^5.3.1",
         "natural-compare": "^1.4.0",
-        "semver": "^7.5.4",
-        "ts-api-utils": "^1.0.1"
+        "semver": "^7.6.0",
+        "ts-api-utils": "^1.3.0"
       },
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -1248,15 +1241,15 @@
       }
     },
     "node_modules/@typescript-eslint/parser": {
-      "version": "7.3.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.3.1.tgz",
-      "integrity": "sha512-Rq49+pq7viTRCH48XAbTA+wdLRrB/3sRq4Lpk0oGDm0VmnjBrAOVXH/Laalmwsv2VpekiEfVFwJYVk6/e8uvQw==",
+      "version": "7.7.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.7.0.tgz",
+      "integrity": "sha512-fNcDm3wSwVM8QYL4HKVBggdIPAy9Q41vcvC/GtDobw3c4ndVT3K6cqudUmjHPw8EAp4ufax0o58/xvWaP2FmTg==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/scope-manager": "7.3.1",
-        "@typescript-eslint/types": "7.3.1",
-        "@typescript-eslint/typescript-estree": "7.3.1",
-        "@typescript-eslint/visitor-keys": "7.3.1",
+        "@typescript-eslint/scope-manager": "7.7.0",
+        "@typescript-eslint/types": "7.7.0",
+        "@typescript-eslint/typescript-estree": "7.7.0",
+        "@typescript-eslint/visitor-keys": "7.7.0",
         "debug": "^4.3.4"
       },
       "engines": {
@@ -1276,13 +1269,13 @@
       }
     },
     "node_modules/@typescript-eslint/scope-manager": {
-      "version": "7.3.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.3.1.tgz",
-      "integrity": "sha512-fVS6fPxldsKY2nFvyT7IP78UO1/I2huG+AYu5AMjCT9wtl6JFiDnsv4uad4jQ0GTFzcUV5HShVeN96/17bTBag==",
+      "version": "7.7.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.7.0.tgz",
+      "integrity": "sha512-/8INDn0YLInbe9Wt7dK4cXLDYp0fNHP5xKLHvZl3mOT5X17rK/YShXaiNmorl+/U4VKCVIjJnx4Ri5b0y+HClw==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.3.1",
-        "@typescript-eslint/visitor-keys": "7.3.1"
+        "@typescript-eslint/types": "7.7.0",
+        "@typescript-eslint/visitor-keys": "7.7.0"
       },
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -1293,15 +1286,15 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils": {
-      "version": "7.3.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.3.1.tgz",
-      "integrity": "sha512-iFhaysxFsMDQlzJn+vr3OrxN8NmdQkHks4WaqD4QBnt5hsq234wcYdyQ9uquzJJIDAj5W4wQne3yEsYA6OmXGw==",
+      "version": "7.7.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.7.0.tgz",
+      "integrity": "sha512-bOp3ejoRYrhAlnT/bozNQi3nio9tIgv3U5C0mVDdZC7cpcQEDZXvq8inrHYghLVwuNABRqrMW5tzAv88Vy77Sg==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/typescript-estree": "7.3.1",
-        "@typescript-eslint/utils": "7.3.1",
+        "@typescript-eslint/typescript-estree": "7.7.0",
+        "@typescript-eslint/utils": "7.7.0",
         "debug": "^4.3.4",
-        "ts-api-utils": "^1.0.1"
+        "ts-api-utils": "^1.3.0"
       },
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -1320,9 +1313,9 @@
       }
     },
     "node_modules/@typescript-eslint/types": {
-      "version": "7.3.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.3.1.tgz",
-      "integrity": "sha512-2tUf3uWggBDl4S4183nivWQ2HqceOZh1U4hhu4p1tPiIJoRRXrab7Y+Y0p+dozYwZVvLPRI6r5wKe9kToF9FIw==",
+      "version": "7.7.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.7.0.tgz",
+      "integrity": "sha512-G01YPZ1Bd2hn+KPpIbrAhEWOn5lQBrjxkzHkWvP6NucMXFtfXoevK82hzQdpfuQYuhkvFDeQYbzXCjR1z9Z03w==",
       "dev": true,
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -1333,19 +1326,19 @@
       }
     },
     "node_modules/@typescript-eslint/typescript-estree": {
-      "version": "7.3.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.3.1.tgz",
-      "integrity": "sha512-tLpuqM46LVkduWP7JO7yVoWshpJuJzxDOPYIVWUUZbW+4dBpgGeUdl/fQkhuV0A8eGnphYw3pp8d2EnvPOfxmQ==",
+      "version": "7.7.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.7.0.tgz",
+      "integrity": "sha512-8p71HQPE6CbxIBy2kWHqM1KGrC07pk6RJn40n0DSc6bMOBBREZxSDJ+BmRzc8B5OdaMh1ty3mkuWRg4sCFiDQQ==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.3.1",
-        "@typescript-eslint/visitor-keys": "7.3.1",
+        "@typescript-eslint/types": "7.7.0",
+        "@typescript-eslint/visitor-keys": "7.7.0",
         "debug": "^4.3.4",
         "globby": "^11.1.0",
         "is-glob": "^4.0.3",
-        "minimatch": "9.0.3",
-        "semver": "^7.5.4",
-        "ts-api-utils": "^1.0.1"
+        "minimatch": "^9.0.4",
+        "semver": "^7.6.0",
+        "ts-api-utils": "^1.3.0"
       },
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -1361,18 +1354,18 @@
       }
     },
     "node_modules/@typescript-eslint/utils": {
-      "version": "7.3.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.3.1.tgz",
-      "integrity": "sha512-jIERm/6bYQ9HkynYlNZvXpzmXWZGhMbrOvq3jJzOSOlKXsVjrrolzWBjDW6/TvT5Q3WqaN4EkmcfdQwi9tDjBQ==",
+      "version": "7.7.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.7.0.tgz",
+      "integrity": "sha512-LKGAXMPQs8U/zMRFXDZOzmMKgFv3COlxUQ+2NMPhbqgVm6R1w+nU1i4836Pmxu9jZAuIeyySNrN/6Rc657ggig==",
       "dev": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.4.0",
-        "@types/json-schema": "^7.0.12",
-        "@types/semver": "^7.5.0",
-        "@typescript-eslint/scope-manager": "7.3.1",
-        "@typescript-eslint/types": "7.3.1",
-        "@typescript-eslint/typescript-estree": "7.3.1",
-        "semver": "^7.5.4"
+        "@types/json-schema": "^7.0.15",
+        "@types/semver": "^7.5.8",
+        "@typescript-eslint/scope-manager": "7.7.0",
+        "@typescript-eslint/types": "7.7.0",
+        "@typescript-eslint/typescript-estree": "7.7.0",
+        "semver": "^7.6.0"
       },
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -1386,13 +1379,13 @@
       }
     },
     "node_modules/@typescript-eslint/visitor-keys": {
-      "version": "7.3.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.3.1.tgz",
-      "integrity": "sha512-9RMXwQF8knsZvfv9tdi+4D/j7dMG28X/wMJ8Jj6eOHyHWwDW4ngQJcqEczSsqIKKjFiLFr40Mnr7a5ulDD3vmw==",
+      "version": "7.7.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.7.0.tgz",
+      "integrity": "sha512-h0WHOj8MhdhY8YWkzIF30R379y0NqyOHExI9N9KCzvmu05EgG4FumeYa3ccfKUSphyWkWQE1ybVrgz/Pbam6YA==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.3.1",
-        "eslint-visitor-keys": "^3.4.1"
+        "@typescript-eslint/types": "7.7.0",
+        "eslint-visitor-keys": "^3.4.3"
       },
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -2004,9 +1997,9 @@
       }
     },
     "node_modules/caniuse-lite": {
-      "version": "1.0.30001600",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001600.tgz",
-      "integrity": "sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ==",
+      "version": "1.0.30001612",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001612.tgz",
+      "integrity": "sha512-lFgnZ07UhaCcsSZgWW0K5j4e69dK1u/ltrL9lTUiFOwNHs12S3UMIEYgBV0Z6C6hRDev7iRnMzzYmKabYdXF9g==",
       "dev": true,
       "funding": [
         {
@@ -2352,9 +2345,9 @@
       }
     },
     "node_modules/electron-to-chromium": {
-      "version": "1.4.715",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.715.tgz",
-      "integrity": "sha512-XzWNH4ZSa9BwVUQSDorPWAUQ5WGuYz7zJUNpNif40zFCiCl20t8zgylmreNmn26h5kiyw2lg7RfTmeMBsDklqg==",
+      "version": "1.4.745",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.745.tgz",
+      "integrity": "sha512-tRbzkaRI5gbUn5DEvF0dV4TQbMZ5CLkWeTAXmpC9IrYT+GE+x76i9p+o3RJ5l9XmdQlI1pPhVtE9uNcJJ0G0EA==",
       "dev": true
     },
     "node_modules/enhanced-resolve": {
@@ -2371,9 +2364,9 @@
       }
     },
     "node_modules/envinfo": {
-      "version": "7.11.1",
-      "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.11.1.tgz",
-      "integrity": "sha512-8PiZgZNIB4q/Lw4AhOvAfB/ityHAd2bli3lESSWmWSzSsl5dKpy5N1d1Rfkd2teq/g9xN90lc6o98DOjMeYHpg==",
+      "version": "7.12.0",
+      "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.12.0.tgz",
+      "integrity": "sha512-Iw9rQJBGpJRd3rwXm9ft/JiGoAZmLxxJZELYDQoPRZ4USVhkKtIcNBPw6U+/K2mBpaqM25JSV6Yl4Az9vO2wJg==",
       "dev": true,
       "bin": {
         "envinfo": "dist/cli.js"
@@ -2391,9 +2384,9 @@
       }
     },
     "node_modules/es-abstract": {
-      "version": "1.23.2",
-      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.2.tgz",
-      "integrity": "sha512-60s3Xv2T2p1ICykc7c+DNDPLDMm9t4QxCOUU0K9JxiLjM3C1zB9YVdN7tjxrFd4+AkZ8CdX1ovUga4P2+1e+/w==",
+      "version": "1.23.3",
+      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz",
+      "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==",
       "dev": true,
       "dependencies": {
         "array-buffer-byte-length": "^1.0.1",
@@ -2435,11 +2428,11 @@
         "safe-regex-test": "^1.0.3",
         "string.prototype.trim": "^1.2.9",
         "string.prototype.trimend": "^1.0.8",
-        "string.prototype.trimstart": "^1.0.7",
+        "string.prototype.trimstart": "^1.0.8",
         "typed-array-buffer": "^1.0.2",
         "typed-array-byte-length": "^1.0.1",
         "typed-array-byte-offset": "^1.0.2",
-        "typed-array-length": "^1.0.5",
+        "typed-array-length": "^1.0.6",
         "unbox-primitive": "^1.0.2",
         "which-typed-array": "^1.1.15"
       },
@@ -2497,9 +2490,9 @@
       }
     },
     "node_modules/es-module-lexer": {
-      "version": "1.4.2",
-      "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.2.tgz",
-      "integrity": "sha512-7nOqkomXZEaxUDJw21XZNtRk739QvrPSoZoRtbsEfcii00vdzZUh6zh1CQwHhrib8MdEtJfv5rJiGeb4KuV/vw==",
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.0.tgz",
+      "integrity": "sha512-pqrTKmwEIgafsYZAGw9kszYzmagcE/n4dbgwGWLEXg7J4QFJVQRBld8j3Q3GNez79jzxZshq0bcT962QHOghjw==",
       "dev": true
     },
     "node_modules/es-object-atoms": {
@@ -4116,9 +4109,9 @@
       }
     },
     "node_modules/minimatch": {
-      "version": "9.0.3",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
-      "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+      "version": "9.0.4",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz",
+      "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==",
       "dev": true,
       "dependencies": {
         "brace-expansion": "^2.0.1"
@@ -5185,9 +5178,9 @@
       }
     },
     "node_modules/terser": {
-      "version": "5.29.2",
-      "resolved": "https://registry.npmjs.org/terser/-/terser-5.29.2.tgz",
-      "integrity": "sha512-ZiGkhUBIM+7LwkNjXYJq8svgkd+QK3UUr0wJqY4MieaezBSAIPgbSPZyIx0idM6XWK5CMzSWa8MJIzmRcB8Caw==",
+      "version": "5.30.4",
+      "resolved": "https://registry.npmjs.org/terser/-/terser-5.30.4.tgz",
+      "integrity": "sha512-xRdd0v64a8mFK9bnsKVdoNP9GQIKUAaJPTaqEQDL4w/J8WaW4sWXXoMZ+6SimPkfT5bElreXf8m9HnmPc3E1BQ==",
       "dev": true,
       "dependencies": {
         "@jridgewell/source-map": "^0.3.3",
@@ -5458,9 +5451,9 @@
       }
     },
     "node_modules/typescript": {
-      "version": "5.4.3",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz",
-      "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==",
+      "version": "5.4.5",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
+      "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
       "dev": true,
       "bin": {
         "tsc": "bin/tsc",

+ 23 - 5
frontend/taipy/src/CoreSelector.tsx

@@ -44,6 +44,7 @@ import {
     BadgePos,
     BadgeSx,
     BaseTreeViewSx,
+    EmptyArray,
     FlagSx,
     ParentItemSx,
     iconLabelSx,
@@ -55,7 +56,7 @@ export interface EditProps {
     id: string;
 }
 
-const treeSlots = {expandIcon: ChevronRight};
+const treeSlots = { expandIcon: ChevronRight };
 
 type Entities = Cycles | Scenarios | DataNodes;
 type Entity = Cycle | Scenario | Sequence | DataNode;
@@ -109,7 +110,7 @@ const CoreItem = (props: {
     onPin?: (e: MouseEvent<HTMLElement>) => void;
     hideNonPinned: boolean;
 }) => {
-    const [id, label, items = [], nodeType, primary] = props.item;
+    const [id, label, items = EmptyArray, nodeType, primary] = props.item;
     const isPinned = props.pins[0][id];
     const isShown = props.hideNonPinned ? props.pins[1][id] : true;
 
@@ -226,6 +227,15 @@ const getChildrenIds = (entity: Entity): string[] => {
     return res;
 };
 
+const getExpandedIds = (nodeId: string, exp?: string[], entities?: Entities) => {
+    const ret = entities && findEntityAndParents(nodeId, entities);
+    if (ret && ret[1]) {
+        const res = ret[1].map((r) => r[0]);
+        return exp ? [...exp, ...res] : res;
+    }
+    return exp;
+};
+
 const CoreSelector = (props: CoreSelectorProps) => {
     const {
         id = "",
@@ -249,6 +259,7 @@ const CoreSelector = (props: CoreSelectorProps) => {
     const [selected, setSelected] = useState("");
     const [pins, setPins] = useState<[Pinned, Pinned]>([{}, {}]);
     const [hideNonPinned, setShowPinned] = useState(false);
+    const [expandedItems, setExpandedItems] = useState<string[]>();
 
     const dispatch = useDispatch();
     const module = useModule();
@@ -292,21 +303,27 @@ const CoreSelector = (props: CoreSelectorProps) => {
     useEffect(() => {
         if (value !== undefined && value !== null) {
             setSelected(value);
+            setExpandedItems((exp) => getExpandedIds(value, exp, props.entities));
         } else if (defaultValue) {
             try {
                 const parsedValue = JSON.parse(defaultValue);
                 if (Array.isArray(parsedValue)) {
-                    parsedValue.length && setSelected(parsedValue[0]);
+                    if (parsedValue.length) {
+                        setSelected(parsedValue[0]);
+                        setExpandedItems((exp) => getExpandedIds(parsedValue[0], exp, props.entities));
+                    }
                 } else {
                     setSelected(parsedValue);
+                    setExpandedItems((exp) => getExpandedIds(parsedValue, exp, props.entities));
                 }
             } catch {
                 setSelected(defaultValue);
+                setExpandedItems((exp) => getExpandedIds(defaultValue, exp, props.entities));
             }
         } else if (value === null) {
             setSelected("");
         }
-    }, [defaultValue, value]);
+    }, [defaultValue, value, props.entities]);
 
     useEffect(() => {
         if (entities && !entities.length) {
@@ -402,11 +419,12 @@ const CoreSelector = (props: CoreSelectorProps) => {
                 onItemSelectionToggle={onNodeSelect}
                 selectedItems={selected}
                 multiSelect={multiple && !multiple}
+                expandedItems={expandedItems}
             >
                 {entities
                     ? entities.map((item) => (
                           <CoreItem
-                              key={item[0]}
+                              key={item ? item[0] : ""}
                               item={item}
                               displayCycles={displayCycles}
                               showPrimaryFlag={showPrimaryFlag}

+ 1 - 1
frontend/taipy/src/ScenarioSelector.tsx

@@ -422,7 +422,7 @@ const ScenarioSelector = (props: ScenarioSelectorProps) => {
 
     const onSubmit = useCallback(
         (...values: unknown[]) => {
-            dispatch(createSendActionNameAction(props.id, module, props.onScenarioCrud, props.onCreation, ...values));
+            dispatch(createSendActionNameAction(props.id, module, props.onScenarioCrud, props.onCreation, props.updateVarName, ...values));
             if (values.length > 1 && values[1]) {
                 // delete requested => unselect current node
                 const lovVar = getUpdateVar(props.updateVars, "scenarios");

+ 3 - 0
frontend/taipy/src/utils.ts

@@ -215,3 +215,6 @@ export const MenuProps = {
 export const selectSx = { m: 1, width: 300 };
 
 export const DeleteIconSx = { height: 50, width: 50, p: 0 };
+
+
+export const EmptyArray = [];

+ 6 - 6
taipy/config/_config.py

@@ -20,12 +20,12 @@ from .unique_section import UniqueSection
 class _Config:
     DEFAULT_KEY = "default"
 
-    def __init__(self):
+    def __init__(self) -> None:
         self._sections: Dict[str, Dict[str, Section]] = {}
         self._unique_sections: Dict[str, UniqueSection] = {}
         self._global_config: GlobalAppConfig = GlobalAppConfig()
 
-    def _clean(self):
+    def _clean(self) -> None:
         self._global_config._clean()
         for unique_section in self._unique_sections.values():
             unique_section._clean()
@@ -39,7 +39,7 @@ class _Config:
         config._global_config = GlobalAppConfig.default_config()
         return config
 
-    def _update(self, other_config):
+    def _update(self, other_config) -> None:
         self._global_config._update(other_config._global_config._to_dict())
         if other_config._unique_sections:
             for section_name, other_section in other_config._unique_sections.items():
@@ -55,12 +55,12 @@ class _Config:
                     self._sections[section_name] = {}
                     self.__add_sections(self._sections[section_name], other_non_unique_sections)
 
-    def __add_sections(self, entity_config, other_entity_configs):
+    def __add_sections(self, entity_config, other_entity_configs) -> None:
         for cfg_id, sub_config in other_entity_configs.items():
             entity_config[cfg_id] = copy(sub_config)
             self.__point_nested_section_to_self(sub_config)
 
-    def __update_sections(self, entity_config, other_entity_configs):
+    def __update_sections(self, entity_config, other_entity_configs) -> None:
         if self.DEFAULT_KEY in other_entity_configs:
             if self.DEFAULT_KEY in entity_config:
                 entity_config[self.DEFAULT_KEY]._update(other_entity_configs[self.DEFAULT_KEY]._to_dict())
@@ -73,7 +73,7 @@ class _Config:
                 entity_config[cfg_id]._update(sub_config._to_dict(), entity_config.get(self.DEFAULT_KEY))
             self.__point_nested_section_to_self(sub_config)
 
-    def __point_nested_section_to_self(self, section):
+    def __point_nested_section_to_self(self, section) -> None:
         """Loop through attributes of a Section to find if any attribute has a list of Section as value.
         If there is, update each nested Section by the corresponding instance in self.
 

+ 2 - 2
taipy/config/_config_comparator/_config_comparator.py

@@ -22,11 +22,11 @@ from ._comparator_result import _ComparatorResult
 
 
 class _ConfigComparator:
-    def __init__(self):
+    def __init__(self) -> None:
         self._unconflicted_sections: Set[str] = set()
         self.__logger = _TaipyLogger._get_logger()
 
-    def _add_unconflicted_section(self, section_name: Union[str, Set[str]]):
+    def _add_unconflicted_section(self, section_name: Union[str, Set[str]]) -> None:
         if isinstance(section_name, str):
             section_name = {section_name}
 

+ 4 - 4
taipy/config/checker/issue_collector.py

@@ -29,7 +29,7 @@ class IssueCollector:
     _WARNING_LEVEL = "WARNING"
     _INFO_LEVEL = "INFO"
 
-    def __init__(self):
+    def __init__(self) -> None:
         self._errors: List[Issue] = []
         self._warnings: List[Issue] = []
         self._infos: List[Issue] = []
@@ -50,11 +50,11 @@ class IssueCollector:
     def errors(self) -> List[Issue]:
         return self._errors
 
-    def _add_error(self, field: str, value: Any, message: str, checker_name: str):
+    def _add_error(self, field: str, value: Any, message: str, checker_name: str) -> None:
         self._errors.append(Issue(self._ERROR_LEVEL, field, value, message, checker_name))
 
-    def _add_warning(self, field: str, value: Any, message: str, checker_name: str):
+    def _add_warning(self, field: str, value: Any, message: str, checker_name: str) -> None:
         self._warnings.append(Issue(self._WARNING_LEVEL, field, value, message, checker_name))
 
-    def _add_info(self, field: str, value: Any, message: str, checker_name: str):
+    def _add_info(self, field: str, value: Any, message: str, checker_name: str) -> None:
         self._infos.append(Issue(self._INFO_LEVEL, field, value, message, checker_name))

+ 4 - 4
taipy/config/config.pyi

@@ -787,10 +787,10 @@ class Config:
     @staticmethod
     def configure_task(
         id: str,
-        function,
+        function: Optional[Callable],
         input: Optional[Union[DataNodeConfig, List[DataNodeConfig]]] = None,
         output: Optional[Union[DataNodeConfig, List[DataNodeConfig]]] = None,
-        skippable: Optional[bool] = False,
+        skippable: bool = False,
         **properties,
     ) -> "TaskConfig":
         """Configure a new task configuration.
@@ -815,10 +815,10 @@ class Config:
 
     @staticmethod
     def set_default_task_configuration(
-        function,
+        function: Optional[Callable],
         input: Optional[Union[DataNodeConfig, List[DataNodeConfig]]] = None,
         output: Optional[Union[DataNodeConfig, List[DataNodeConfig]]] = None,
-        skippable: Optional[bool] = False,
+        skippable: bool = False,
         **properties,
     ) -> "TaskConfig":
         """Set the default values for task configurations.

+ 1 - 1
taipy/config/setup.py

@@ -28,7 +28,7 @@ with open(version_path) as version_file:
     if vext := version.get("ext"):
         version_string = f"{version_string}.{vext}"
 
-requirements = ["toml>=0.10,<0.11", "deepdiff>=6.2,<6.3"]
+requirements = ["toml>=0.10,<0.11", "deepdiff>=6.7,<6.8"]
 
 test_requirements = ["pytest>=3.8"]
 

+ 1 - 1
taipy/core/_core.py

@@ -40,7 +40,7 @@ class Core:
     _orchestrator: Optional[_Orchestrator] = None
     _dispatcher: Optional[_JobDispatcher] = None
 
-    def __init__(self):
+    def __init__(self) -> None:
         """
         Initialize a Core service.
         """

+ 13 - 8
taipy/core/_entity/_entity_ids.py

@@ -11,16 +11,21 @@
 
 from __future__ import annotations
 
+from typing import TYPE_CHECKING, Set
+
+if TYPE_CHECKING:
+    from taipy import CycleId, DataNodeId, JobId, ScenarioId, SequenceId, SubmissionId, TaskId
+
 
 class _EntityIds:
-    def __init__(self):
-        self.data_node_ids = set()
-        self.task_ids = set()
-        self.scenario_ids = set()
-        self.sequence_ids = set()
-        self.job_ids = set()
-        self.cycle_ids = set()
-        self.submission_ids = set()
+    def __init__(self) -> None:
+        self.data_node_ids: Set[DataNodeId] = set()
+        self.task_ids: Set[TaskId] = set()
+        self.scenario_ids: Set[ScenarioId] = set()
+        self.sequence_ids: Set[SequenceId] = set()
+        self.job_ids: Set[JobId] = set()
+        self.cycle_ids: Set[CycleId] = set()
+        self.submission_ids: Set[SubmissionId] = set()
 
     def __add__(self, other: _EntityIds):
         self.data_node_ids.update(other.data_node_ids)

+ 1 - 1
taipy/core/_entity/_reload.py

@@ -48,7 +48,7 @@ class _Reloader:
         self._no_reload_context = False
 
 
-def _self_reload(manager):
+def _self_reload(manager: str):
     def __reload(fct):
         @functools.wraps(fct)
         def _do_reload(self, *args, **kwargs):

+ 0 - 3
taipy/core/_manager/_manager.py

@@ -155,9 +155,6 @@ class _Manager(Generic[EntityType]):
 
     @classmethod
     def _export(cls, id: str, folder_path: Union[str, pathlib.Path], **kwargs):
-        """
-        Export an entity.
-        """
         return cls._repository._export(id, folder_path)
 
     @classmethod

+ 1 - 1
taipy/core/_version/_version_fs_repository.py

@@ -22,7 +22,7 @@ from ._version_repository_interface import _VersionRepositoryInterface
 
 
 class _VersionFSRepository(_FileSystemRepository, _VersionRepositoryInterface):
-    def __init__(self):
+    def __init__(self) -> None:
         super().__init__(model_type=_VersionModel, converter=_VersionConverter, dir_name="version")
 
     @property

+ 1 - 1
taipy/core/_version/_version_sql_repository.py

@@ -19,7 +19,7 @@ from ._version_repository_interface import _VersionRepositoryInterface
 
 
 class _VersionSQLRepository(_SQLRepository, _VersionRepositoryInterface):
-    def __init__(self):
+    def __init__(self) -> None:
         super().__init__(model_type=_VersionModel, converter=_VersionConverter)
 
     def _set_latest_version(self, version_number):

+ 12 - 12
taipy/core/config/task_config.py

@@ -10,7 +10,7 @@
 # specific language governing permissions and limitations under the License.
 
 from copy import copy
-from typing import Any, Dict, List, Optional, Union, cast
+from typing import Any, Callable, Dict, List, Optional, Union, cast
 
 from taipy.config._config import _Config
 from taipy.config.common._template_handler import _TemplateHandler as _tpl
@@ -53,12 +53,12 @@ class TaskConfig(Section):
     def __init__(
         self,
         id: str,
-        function,
+        function: Optional[Callable],
         inputs: Optional[Union[DataNodeConfig, List[DataNodeConfig]]] = None,
         outputs: Optional[Union[DataNodeConfig, List[DataNodeConfig]]] = None,
-        skippable: Optional[bool] = False,
+        skippable: bool = False,
         **properties,
-    ):
+    ) -> None:
         if inputs:
             self._inputs = [inputs] if isinstance(inputs, DataNodeConfig) else copy(inputs)
         else:
@@ -71,8 +71,8 @@ class TaskConfig(Section):
                 skippable = True
         else:
             self._outputs = []
-        self._skippable = skippable
-        self.function = function
+        self._skippable: bool = skippable
+        self.function: Optional[Callable] = function
         super().__init__(id, **properties)
 
     def __copy__(self):
@@ -100,14 +100,14 @@ class TaskConfig(Section):
         return list(self._outputs)
 
     @property
-    def skippable(self):
+    def skippable(self) -> bool:
         return _tpl._replace_templates(self._skippable)
 
     @classmethod
     def default_config(cls):
         return TaskConfig(cls._DEFAULT_KEY, None, [], [], False)
 
-    def _clean(self):
+    def _clean(self) -> None:
         self.function = None
         self._inputs = []
         self._outputs = []
@@ -158,10 +158,10 @@ class TaskConfig(Section):
     @staticmethod
     def _configure(
         id: str,
-        function,
+        function: Optional[Callable],
         input: Optional[Union[DataNodeConfig, List[DataNodeConfig]]] = None,
         output: Optional[Union[DataNodeConfig, List[DataNodeConfig]]] = None,
-        skippable: Optional[bool] = False,
+        skippable: bool = False,
         **properties,
     ) -> "TaskConfig":
         """Configure a new task configuration.
@@ -189,10 +189,10 @@ class TaskConfig(Section):
 
     @staticmethod
     def _set_default_configuration(
-        function,
+        function: Optional[Callable],
         input: Optional[Union[DataNodeConfig, List[DataNodeConfig]]] = None,
         output: Optional[Union[DataNodeConfig, List[DataNodeConfig]]] = None,
-        skippable: Optional[bool] = False,
+        skippable: bool = False,
         **properties,
     ) -> "TaskConfig":
         """Set the default values for task configurations.

+ 1 - 1
taipy/core/cycle/_cycle_fs_repository.py

@@ -14,5 +14,5 @@ from ._cycle_model import _CycleModel
 
 
 class _CycleFSRepository(_FileSystemRepository):
-    def __init__(self):
+    def __init__(self) -> None:
         super().__init__(model_type=_CycleModel, converter=_CycleConverter, dir_name="cycles")

+ 1 - 1
taipy/core/cycle/_cycle_sql_repository.py

@@ -14,5 +14,5 @@ from ._cycle_model import _CycleModel
 
 
 class _CycleSQLRepository(_SQLRepository):
-    def __init__(self):
+    def __init__(self) -> None:
         super().__init__(model_type=_CycleModel, converter=_CycleConverter)

+ 1 - 1
taipy/core/data/_data_fs_repository.py

@@ -14,5 +14,5 @@ from ._data_model import _DataNodeModel
 
 
 class _DataFSRepository(_FileSystemRepository):
-    def __init__(self):
+    def __init__(self) -> None:
         super().__init__(model_type=_DataNodeModel, converter=_DataNodeConverter, dir_name="data_nodes")

+ 1 - 1
taipy/core/data/_data_manager.py

@@ -195,7 +195,7 @@ class _DataManager(_Manager[DataNode], _VersionMixin):
         imported_data_node._version = version
         cls._set(imported_data_node)
 
-        if not isinstance(imported_data_node, _FileDataNodeMixin):
+        if not (isinstance(imported_data_node, _FileDataNodeMixin) and isinstance(imported_data_node, DataNode)):
             return imported_data_node
 
         data_folder: pathlib.Path = pathlib.Path(str(kwargs.get("data_folder")))

+ 1 - 1
taipy/core/data/_data_model.py

@@ -69,7 +69,7 @@ class _DataNodeModel(_BaseModel):
             scope=Scope._from_repr(data["scope"]),
             storage_type=data["storage_type"],
             owner_id=data.get("owner_id"),
-            parent_ids=data.get("parent_ids", []),
+            parent_ids=_BaseModel._deserialize_attribute(data.get("parent_ids", [])),
             last_edit_date=data.get("last_edit_date"),
             edits=_BaseModel._deserialize_attribute(data["edits"]),
             version=data["version"],

+ 1 - 1
taipy/core/data/_data_sql_repository.py

@@ -14,5 +14,5 @@ from ._data_model import _DataNodeModel
 
 
 class _DataSQLRepository(_SQLRepository):
-    def __init__(self):
+    def __init__(self) -> None:
         super().__init__(model_type=_DataNodeModel, converter=_DataNodeConverter)

+ 4 - 4
taipy/core/data/_file_datanode_mixin.py

@@ -34,9 +34,9 @@ class _FileDataNodeMixin(object):
     _DEFAULT_PATH_KEY = "default_path"
     _IS_GENERATED_KEY = "is_generated"
 
-    def __init__(self, properties: Dict):
-        self._path = properties.get(self._PATH_KEY, properties.get(self._DEFAULT_PATH_KEY))
-        self._is_generated = properties.get(self._IS_GENERATED_KEY, self._path is None)
+    def __init__(self, properties: Dict) -> None:
+        self._path: str = properties.get(self._PATH_KEY, properties.get(self._DEFAULT_PATH_KEY))
+        self._is_generated: bool = properties.get(self._IS_GENERATED_KEY, self._path is None)
         self._last_edit_date: Optional[datetime] = None
 
         if self._path and ".data" in self._path:
@@ -50,7 +50,7 @@ class _FileDataNodeMixin(object):
     def _write_default_data(self, default_value: Any):
         if default_value is not None and not os.path.exists(self._path):
             self._write(default_value)  # type: ignore[attr-defined]
-            self._last_edit_date = datetime.now()
+            self._last_edit_date = DataNode._get_last_modified_datetime(self._path) or datetime.now()
             self._edits.append(  # type: ignore[attr-defined]
                 Edit(
                     {

+ 9 - 7
taipy/core/data/data_node.py

@@ -82,7 +82,7 @@ class DataNode(_Entity, _Labeled):
     __ID_SEPARATOR = "_"
     __logger = _TaipyLogger._get_logger()
     _REQUIRED_PROPERTIES: List[str] = []
-    _MANAGER_NAME = "data"
+    _MANAGER_NAME: str = "data"
     _PATH_KEY = "path"
     __EDIT_TIMEOUT = 30
 
@@ -103,9 +103,9 @@ class DataNode(_Entity, _Labeled):
         editor_id: Optional[str] = None,
         editor_expiration_date: Optional[datetime] = None,
         **kwargs,
-    ):
+    ) -> None:
         self._config_id = _validate_id(config_id)
-        self.id = id or self._new_id(self.config_id)
+        self.id = id or self._new_id(self._config_id)
         self._owner_id = owner_id
         self._parent_ids = parent_ids or set()
         self._scope = scope
@@ -165,7 +165,7 @@ class DataNode(_Entity, _Labeled):
     @property  # type: ignore
     @_self_reload(_MANAGER_NAME)
     def last_edit_date(self):
-        last_modified_datetime = self.__get_last_modified_datetime()
+        last_modified_datetime = self._get_last_modified_datetime(self._properties.get(self._PATH_KEY, None))
         if last_modified_datetime and last_modified_datetime > self._last_edit_date:
             return last_modified_datetime
         else:
@@ -297,8 +297,8 @@ class DataNode(_Entity, _Labeled):
             return self._properties[protected_attribute_name]
         raise AttributeError(f"{attribute_name} is not an attribute of data node {self.id}")
 
-    def __get_last_modified_datetime(self) -> Optional[datetime]:
-        path = self._properties.get(self._PATH_KEY, None)
+    @classmethod
+    def _get_last_modified_datetime(cls, path: Optional[str] = None) -> Optional[datetime]:
         if path and os.path.isfile(path):
             return datetime.fromtimestamp(os.path.getmtime(path))
 
@@ -387,7 +387,9 @@ class DataNode(_Entity, _Labeled):
         """
         edit = {k: v for k, v in options.items() if v is not None}
         if "timestamp" not in edit:
-            edit["timestamp"] = datetime.now()
+            edit["timestamp"] = (
+                self._get_last_modified_datetime(self._properties.get(self._PATH_KEY, None)) or datetime.now()
+            )
         self.last_edit_date = edit.get("timestamp")
         self._edits.append(edit)
 

+ 1 - 1
taipy/core/job/_job_fs_repository.py

@@ -14,5 +14,5 @@ from ._job_model import _JobModel
 
 
 class _JobFSRepository(_FileSystemRepository):
-    def __init__(self):
+    def __init__(self) -> None:
         super().__init__(model_type=_JobModel, converter=_JobConverter, dir_name="jobs")

+ 1 - 1
taipy/core/job/_job_sql_repository.py

@@ -14,5 +14,5 @@ from ._job_model import _JobModel
 
 
 class _JobSQLRepository(_SQLRepository):
-    def __init__(self):
+    def __init__(self) -> None:
         super().__init__(model_type=_JobModel, converter=_JobConverter)

+ 5 - 5
taipy/core/notification/core_event_consumer.py

@@ -46,7 +46,7 @@ class CoreEventConsumerBase(threading.Thread):
 
     """
 
-    def __init__(self, registration_id: str, queue: SimpleQueue):
+    def __init__(self, registration_id: str, queue: SimpleQueue) -> None:
         """Initialize a CoreEventConsumerBase instance.
 
         Parameters:
@@ -61,16 +61,16 @@ class CoreEventConsumerBase(threading.Thread):
         self.__STOP_FLAG = False
         self._TIMEOUT = 0.1
 
-    def start(self):
+    def start(self) -> None:
         """Start the event consumer thread."""
         self.__STOP_FLAG = False
         threading.Thread.start(self)
 
-    def stop(self):
+    def stop(self) -> None:
         """Stop the event consumer thread."""
         self.__STOP_FLAG = True
 
-    def run(self):
+    def run(self) -> None:
         while not self.__STOP_FLAG:
             try:
                 event: Event = self.queue.get(block=True, timeout=self._TIMEOUT)
@@ -79,6 +79,6 @@ class CoreEventConsumerBase(threading.Thread):
                 pass
 
     @abc.abstractmethod
-    def process_event(self, event: Event):
+    def process_event(self, event: Event) -> None:
         """This method should be overridden in subclasses to define how events are processed."""
         raise NotImplementedError

+ 1 - 1
taipy/core/scenario/_scenario_fs_repository.py

@@ -14,5 +14,5 @@ from ._scenario_model import _ScenarioModel
 
 
 class _ScenarioFSRepository(_FileSystemRepository):
-    def __init__(self):
+    def __init__(self) -> None:
         super().__init__(model_type=_ScenarioModel, converter=_ScenarioConverter, dir_name="scenarios")

+ 1 - 1
taipy/core/scenario/_scenario_sql_repository.py

@@ -14,5 +14,5 @@ from ._scenario_model import _ScenarioModel
 
 
 class _ScenarioSQLRepository(_SQLRepository):
-    def __init__(self):
+    def __init__(self) -> None:
         super().__init__(model_type=_ScenarioModel, converter=_ScenarioConverter)

+ 1 - 1
taipy/core/submission/_submission_fs_repository.py

@@ -14,5 +14,5 @@ from ._submission_model import _SubmissionModel
 
 
 class _SubmissionFSRepository(_FileSystemRepository):
-    def __init__(self):
+    def __init__(self) -> None:
         super().__init__(model_type=_SubmissionModel, converter=_SubmissionConverter, dir_name="submission")

+ 1 - 1
taipy/core/submission/_submission_sql_repository.py

@@ -14,5 +14,5 @@ from ._submission_model import _SubmissionModel
 
 
 class _SubmissionSQLRepository(_SQLRepository):
-    def __init__(self):
+    def __init__(self) -> None:
         super().__init__(model_type=_SubmissionModel, converter=_SubmissionConverter)

+ 2 - 2
taipy/core/submission/submission.py

@@ -52,7 +52,7 @@ class Submission(_Entity, _Labeled):
         entity_id: str,
         entity_type: str,
         entity_config_id: Optional[str] = None,
-        id: Optional[str] = None,
+        id: Optional[SubmissionId] = None,
         jobs: Optional[Union[List[Job], List[JobId]]] = None,
         properties: Optional[Dict[str, Any]] = None,
         creation_date: Optional[datetime] = None,
@@ -80,7 +80,7 @@ class Submission(_Entity, _Labeled):
         self._pending_jobs: Set = set()
 
     @staticmethod
-    def __new_id() -> str:
+    def __new_id() -> SubmissionId:
         """Generate a unique Submission identifier."""
         return SubmissionId(Submission.__SEPARATOR.join([Submission._ID_PREFIX, str(uuid.uuid4())]))
 

+ 1 - 1
taipy/core/task/_task_fs_repository.py

@@ -14,5 +14,5 @@ from ._task_model import _TaskModel
 
 
 class _TaskFSRepository(_FileSystemRepository):
-    def __init__(self):
+    def __init__(self) -> None:
         super().__init__(model_type=_TaskModel, converter=_TaskConverter, dir_name="tasks")

+ 3 - 2
taipy/core/task/_task_manager.py

@@ -9,7 +9,7 @@
 # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 # specific language governing permissions and limitations under the License.
 
-from typing import Callable, List, Optional, Type, Union
+from typing import Callable, List, Optional, Type, Union, cast
 
 from taipy.config import Config
 from taipy.config.common.scope import Scope
@@ -101,10 +101,11 @@ class _TaskManager(_Manager[Task], _VersionMixin):
                     for output_config in [Config.data_nodes[dnc.id] for dnc in task_config.output_configs]
                 ]
                 skippable = task_config.skippable
+
                 task = Task(
                     str(task_config.id),
                     dict(**task_config._properties),
-                    task_config.function,
+                    cast(Callable, task_config.function),
                     inputs,
                     outputs,
                     owner_id=owner_id,

+ 1 - 1
taipy/core/task/_task_sql_repository.py

@@ -14,5 +14,5 @@ from ._task_model import _TaskModel
 
 
 class _TaskSQLRepository(_SQLRepository):
-    def __init__(self):
+    def __init__(self) -> None:
         super().__init__(model_type=_TaskModel, converter=_TaskConverter)

+ 7 - 7
taipy/core/task/task.py

@@ -58,7 +58,7 @@ class Task(_Entity, _Labeled):
         self,
         config_id: str,
         properties: Dict[str, Any],
-        function,
+        function: Callable,
         input: Optional[Iterable[DataNode]] = None,
         output: Optional[Iterable[DataNode]] = None,
         id: Optional[TaskId] = None,
@@ -66,13 +66,13 @@ class Task(_Entity, _Labeled):
         parent_ids: Optional[Set[str]] = None,
         version: Optional[str] = None,
         skippable: bool = False,
-    ):
+    ) -> None:
         self._config_id = _validate_id(config_id)
         self.id = id or TaskId(self.__ID_SEPARATOR.join([self._ID_PREFIX, self.config_id, str(uuid.uuid4())]))
         self._owner_id = owner_id
         self._parent_ids = parent_ids or set()
-        self.__input = {dn.config_id: dn for dn in input or []}
-        self.__output = {dn.config_id: dn for dn in output or []}
+        self._input = {dn.config_id: dn for dn in input or []}
+        self._output = {dn.config_id: dn for dn in output or []}
         self._function = function
         self._version = version or _VersionManagerFactory._build_manager()._get_latest_version()
         self._skippable = skippable
@@ -126,11 +126,11 @@ class Task(_Entity, _Labeled):
 
     @property
     def input(self) -> Dict[str, DataNode]:
-        return self.__input
+        return self._input
 
     @property
     def output(self) -> Dict[str, DataNode]:
-        return self.__output
+        return self._output
 
     @property
     def data_nodes(self) -> Dict[str, DataNode]:
@@ -164,7 +164,7 @@ class Task(_Entity, _Labeled):
             The lowest scope present in input and output data nodes or GLOBAL if there are
                 either no input or no output.
         """
-        data_nodes = list(self.__input.values()) + list(self.__output.values())
+        data_nodes = list(self._input.values()) + list(self._output.values())
         return Scope(min(dn.scope for dn in data_nodes)) if len(data_nodes) != 0 else Scope.GLOBAL
 
     @property

+ 1 - 1
taipy/gui/_page.py

@@ -22,7 +22,7 @@ if t.TYPE_CHECKING:
 
 
 class _Page(object):
-    def __init__(self):
+    def __init__(self) -> None:
         self._rendered_jsx: t.Optional[str] = None
         self._renderer: t.Optional[Page] = None
         self._style: t.Optional[str] = None

+ 32 - 29
taipy/gui/_renderers/builder.py

@@ -272,14 +272,6 @@ class _Builder:
     def __set_json_attribute(self, name, value):
         return self.set_attribute(name, json.dumps(value, cls=_TaipyJsonEncoder))
 
-    def __set_list_of_(self, name: str):
-        lof = self.__get_list_of_(name)
-        if not isinstance(lof, (list, tuple)):
-            if lof is not None:
-                _warn(f"{self.__element_name}: {name} should be a list.")
-            return self
-        return self.__set_json_attribute(_to_camel_case(name), lof)
-
     def set_number_attribute(self, name: str, default_value: t.Optional[str] = None, optional: t.Optional[bool] = True):
         """
         TODO-undocumented
@@ -369,15 +361,17 @@ class _Builder:
     def __set_react_attribute(self, name: str, value: t.Any):
         return self.set_attribute(name, "{!" + (str(value).lower() if isinstance(value, bool) else str(value)) + "!}")
 
-    def _get_adapter(self, var_name: str, property_name: t.Optional[str] = None, multi_selection=True):  # noqa: C901
+    def _get_lov_adapter(self, var_name: str, property_name: t.Optional[str] = None, multi_selection=True):  # noqa: C901
         property_name = var_name if property_name is None else property_name
+        lov_name = self.__hashes.get(var_name)
         lov = self.__get_list_of_(var_name)
+        default_lov = []
         if isinstance(lov, list):
             adapter = self.__attributes.get("adapter")
             if adapter and isinstance(adapter, str):
                 adapter = self.__gui._get_user_function(adapter)
             if adapter and not callable(adapter):
-                _warn("'adapter' property value is invalid.")
+                _warn(f"{self.__element_name}: adapter property value is invalid.")
                 adapter = None
             var_type = self.__attributes.get("type")
             if isclass(var_type):
@@ -402,7 +396,7 @@ class _Builder:
                     if adapter.__name__ == "<lambda>"
                     else _get_expr_var_name(adapter.__name__)
                 )
-            if lov_name := self.__hashes.get(var_name):
+            if lov_name:
                 if adapter is None:
                     adapter = self.__gui._get_adapter_for_type(lov_name)
                 else:
@@ -415,15 +409,13 @@ class _Builder:
             if adapter is not None:
                 self.__gui._add_adapter_for_type(var_type, adapter)  # type: ignore
 
-            ret_list = []
             if len(lov) > 0:
                 for elt in lov:
                     ret = self.__gui._run_adapter(
                         t.cast(t.Callable, adapter), elt, adapter.__name__ if callable(adapter) else "adapter"
                     )  # type: ignore
                     if ret is not None:
-                        ret_list.append(ret)
-            self.__attributes[f"default_{property_name}"] = ret_list
+                        default_lov.append(ret)
 
             ret_list = []
             value = self.__attributes.get("value")
@@ -441,6 +433,26 @@ class _Builder:
                 if ret_val == "-1" and self.__attributes.get("unselected_value") is not None:
                     ret_val = str(self.__attributes.get("unselected_value", ""))
                 self.__set_default_value("value", ret_val)
+
+        # LoV default value
+        self.__set_json_attribute(_to_camel_case(f"default_{property_name}"), default_lov)
+
+        # LoV expression binding
+        if lov_name:
+            typed_lov_hash = (
+                self.__gui._evaluate_expr(
+                    "{"
+                    + f"{self.__gui._get_call_method_name('_get_adapted_lov')}"
+                    + f"({self.__gui._get_real_var_name(lov_name)[0]},'{var_type}')"
+                    + "}"
+                )
+                if var_type
+                else lov_name
+            )
+            hash_name = self.__get_typed_hash_name(typed_lov_hash, PropertyType.lov)
+            self.__update_vars.append(f"{property_name}={hash_name}")
+            self.__set_react_attribute(property_name, hash_name)
+
         return self
 
     def __filter_attribute_names(self, names: t.Iterable[str]):
@@ -495,7 +507,7 @@ class _Builder:
             if cmp_datas:
                 cmp_hash = self.__gui._evaluate_expr(
                     "{"
-                    + f'{self.__gui._get_rebuild_fn_name("_compare_data")}'
+                    + f"{self.__gui._get_call_method_name('_compare_data')}"
                     + f'({self.__gui._get_real_var_name(data_hash)[0]},{",".join(cmp_datas)})'
                     + "}"
                 )
@@ -506,7 +518,7 @@ class _Builder:
         )
 
         rebuild_fn_hash = self.__build_rebuild_fn(
-            self.__gui._get_rebuild_fn_name("_tbl_cols"), _Builder.__TABLE_COLUMNS_DEPS
+            self.__gui._get_call_method_name("_tbl_cols"), _Builder.__TABLE_COLUMNS_DEPS
         )
         if rebuild_fn_hash:
             self.__set_react_attribute("columns", rebuild_fn_hash)
@@ -551,7 +563,8 @@ class _Builder:
         self.__attributes["_default_type"] = default_type
         self.__attributes["_default_mode"] = default_mode
         rebuild_fn_hash = self.__build_rebuild_fn(
-            self.__gui._get_rebuild_fn_name("_chart_conf"), _CHART_NAMES + ("_default_type", "_default_mode", "data")
+            self.__gui._get_call_method_name("_chart_conf"),
+            _CHART_NAMES + ("_default_type", "_default_mode", "data"),
         )
         if rebuild_fn_hash:
             self.__set_react_attribute("config", rebuild_fn_hash)
@@ -671,15 +684,6 @@ class _Builder:
             )
         return self.set_attribute(_to_camel_case(f"default_{var_name}"), value)
 
-    def _set_lov(self, var_name="lov", property_name: t.Optional[str] = None):
-        property_name = var_name if property_name is None else property_name
-        self.__set_list_of_(f"default_{property_name}")
-        if hash_name := self.__hashes.get(var_name):
-            hash_name = self.__get_typed_hash_name(hash_name, PropertyType.lov)
-            self.__update_vars.append(f"{property_name}={hash_name}")
-            self.__set_react_attribute(property_name, hash_name)
-        return self
-
     def __set_dynamic_string_list(self, var_name: str, default_value: t.Any):
         hash_name = self.__hashes.get(var_name)
         loi = self.__attributes.get(var_name)
@@ -989,9 +993,8 @@ class _Builder:
                     self.__set_dynamic_string_list(attr[0], _get_tuple_val(attr, 2, None))
             elif var_type == PropertyType.data:
                 self.__set_dynamic_property_without_default(attr[0], var_type)
-            elif var_type == PropertyType.lov:
-                self._get_adapter(attr[0])  # need to be called before set_lov
-                self._set_lov(attr[0])
+            elif var_type == PropertyType.lov or var_type == PropertyType.single_lov:
+                self._get_lov_adapter(attr[0], multi_selection=var_type != PropertyType.single_lov)
             elif var_type == PropertyType.lov_value:
                 self.__set_dynamic_property_without_default(
                     attr[0], var_type, _get_tuple_val(attr, 2, None) == "optional"

+ 8 - 12
taipy/gui/_renderers/factory.py

@@ -317,31 +317,29 @@ class _Factory:
             element_name="MenuCtl",
             attributes=attrs,
         )
-        ._get_adapter("lov")  # need to be called before set_lov
-        ._set_lov()
         .set_attributes(
             [
                 ("id",),
                 ("active", PropertyType.dynamic_boolean, True),
-                ("label"),
-                ("width"),
+                ("label",),
+                ("width",),
                 ("width[mobile]",),
                 ("on_action", PropertyType.function),
                 ("inactive_ids", PropertyType.dynamic_list),
                 ("hover_text", PropertyType.dynamic_string),
+                ("lov", PropertyType.lov)
             ]
         )
         ._set_propagate(),
         "navbar": lambda gui, control_type, attrs: _Builder(
             gui=gui, control_type=control_type, element_name="NavBar", attributes=attrs, default_value=None
         )
-        ._get_adapter("lov", multi_selection=False)  # need to be called before set_lov
-        ._set_lov()
         .set_attributes(
             [
                 ("id",),
                 ("active", PropertyType.dynamic_boolean, True),
                 ("hover_text", PropertyType.dynamic_string),
+                ("lov", PropertyType.single_lov)
             ]
         ),
         "number": lambda gui, control_type, attrs: _Builder(
@@ -402,8 +400,6 @@ class _Factory:
             gui=gui, control_type=control_type, element_name="Selector", attributes=attrs, default_value=None
         )
         .set_value_and_default(with_default=False, var_type=PropertyType.lov_value)
-        ._get_adapter("lov")  # need to be called before set_lov
-        ._set_lov()
         .set_attributes(
             [
                 ("active", PropertyType.dynamic_boolean, True),
@@ -418,6 +414,7 @@ class _Factory:
                 ("on_change", PropertyType.function),
                 ("label",),
                 ("mode",),
+                ("lov", PropertyType.lov)
             ]
         )
         ._set_propagate(),
@@ -432,14 +429,14 @@ class _Factory:
         .set_attributes(
             [
                 ("active", PropertyType.dynamic_boolean, True),
-                ("height"),
+                ("height",),
                 ("hover_text", PropertyType.dynamic_string),
                 ("id",),
                 ("value_by_id", PropertyType.boolean),
                 ("max", PropertyType.number, 100),
                 ("min", PropertyType.number, 0),
                 ("step", PropertyType.number, 1),
-                ("orientation"),
+                ("orientation",),
                 ("width", PropertyType.string, "300px"),
                 ("on_change", PropertyType.function),
                 ("continuous", PropertyType.boolean, True),
@@ -518,8 +515,6 @@ class _Factory:
             gui=gui, control_type=control_type, element_name="Toggle", attributes=attrs, default_value=None
         )
         .set_value_and_default(with_default=False, var_type=PropertyType.toggle_value)
-        ._get_adapter("lov", multi_selection=False)  # need to be called before set_lov
-        ._set_lov()
         .set_attributes(
             [
                 ("active", PropertyType.dynamic_boolean, True),
@@ -531,6 +526,7 @@ class _Factory:
                 ("allow_unselect", PropertyType.boolean),
                 ("on_change", PropertyType.function),
                 ("mode",),
+                ("lov", PropertyType.single_lov)
             ]
         )
         ._set_kind()

+ 2 - 2
taipy/gui/_renderers/json.py

@@ -54,7 +54,7 @@ class _DefaultJsonAdapter(JsonAdapter):
 
 
 class _TaipyJsonAdapter(object, metaclass=_Singleton):
-    def __init__(self):
+    def __init__(self) -> None:
         self._adapters: t.List[JsonAdapter] = []
         self.register(_DefaultJsonAdapter())
 
@@ -66,7 +66,7 @@ class _TaipyJsonAdapter(object, metaclass=_Singleton):
             for adapter in reversed(self._adapters):
                 if (output := adapter.parse(o)) is not None:
                     return output
-            raise TypeError(f"Object of type {type(o).__name__} is not JSON serializable")
+            raise TypeError(f"Object of type {type(o).__name__} is not JSON serializable (value: {o}).")
         except Exception as e:
             _warn("Exception while resolving JSON", e)
             return None

+ 1 - 1
taipy/gui/builder/_api_generator.py

@@ -27,7 +27,7 @@ if t.TYPE_CHECKING:
 
 
 class _ElementApiGenerator(object, metaclass=_Singleton):
-    def __init__(self):
+    def __init__(self) -> None:
         self.__module: t.Optional[types.ModuleType] = None
 
     @staticmethod

+ 1 - 1
taipy/gui/builder/_context_manager.py

@@ -18,7 +18,7 @@ if t.TYPE_CHECKING:
 
 
 class _BuilderContextManager(object, metaclass=_Singleton):
-    def __init__(self):
+    def __init__(self) -> None:
         self.__blocks: t.List["_Block"] = []
 
     def push(self, element: "_Block") -> None:

+ 2 - 2
taipy/gui/builder/_element.py

@@ -38,7 +38,7 @@ class _Element(ABC):
             parent.add(obj)
         return obj
 
-    def __init__(self, *args, **kwargs):
+    def __init__(self, *args, **kwargs) -> None:
         self._properties: t.Dict[str, t.Any] = {}
         if args and self._DEFAULT_PROPERTY != "":
             self._properties = {self._DEFAULT_PROPERTY: args[0]}
@@ -81,7 +81,7 @@ class _Element(ABC):
 class _Block(_Element):
     """NOT DOCUMENTED"""
 
-    def __init__(self, *args, **kwargs):
+    def __init__(self, *args, **kwargs) -> None:
         super().__init__(*args, **kwargs)
         self._children: t.List[_Element] = []
 

+ 1 - 1
taipy/gui/config.py

@@ -144,7 +144,7 @@ class _Config(object):
         r"^([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$"
     )
 
-    def __init__(self):
+    def __init__(self) -> None:
         self.pages: t.List[_Page] = []
         self.root_page: t.Optional[_Page] = None
         self.routes: t.List[str] = []

+ 8 - 11
taipy/gui/gui.py

@@ -1006,16 +1006,10 @@ class Gui:
                     newvalue = self._get_user_content_url(
                         None, {"variable_name": str(_var), Gui._HTML_CONTENT_KEY: str(time.time())}
                     )
-                elif isinstance(newvalue, _TaipyLov):
-                    newvalue = [self.__adapter._run_for_var(newvalue.get_name(), elt) for elt in newvalue.get()]
-                elif isinstance(newvalue, _TaipyLovValue):
-                    if isinstance(newvalue.get(), list):
-                        newvalue = [
-                            self.__adapter._run_for_var(newvalue.get_name(), elt, id_only=True)
-                            for elt in newvalue.get()
-                        ]
-                    else:
-                        newvalue = self.__adapter._run_for_var(newvalue.get_name(), newvalue.get(), id_only=True)
+                elif isinstance(newvalue, (_TaipyLov, _TaipyLovValue)):
+                    newvalue = self.__adapter.run(
+                        newvalue.get_name(), newvalue.get(), id_only=isinstance(newvalue, _TaipyLovValue)
+                    )
                 elif isinstance(newvalue, _TaipyToJson):
                     newvalue = newvalue.get()
                 if isinstance(newvalue, (dict, _MapDict)):
@@ -1493,7 +1487,7 @@ class Gui:
     def __is_building(self):
         return hasattr(self, "_building") and self._building
 
-    def _get_rebuild_fn_name(self, name: str):
+    def _get_call_method_name(self, name: str):
         return f"{Gui.__SELF_VAR}.{name}"
 
     def __get_attributes(self, attr_json: str, hash_json: str, args_dict: t.Dict[str, t.Any]):
@@ -1505,6 +1499,9 @@ class Gui:
     def _compare_data(self, *data):
         return data[0]
 
+    def _get_adapted_lov(self, lov: list, var_type: str):
+        return self.__adapter._get_adapted_lov(lov, var_type)
+
     def _tbl_cols(
         self, rebuild: bool, rebuild_val: t.Optional[bool], attr_json: str, hash_json: str, **kwargs
     ) -> t.Union[str, _DoNotUpdate]:

+ 1 - 0
taipy/gui/types.py

@@ -107,6 +107,7 @@ class PropertyType(Enum):
     """
     image = _TaipyContentImage
     json = "json"
+    single_lov = "singlelov"
     lov = _TaipyLov
     """
     The property holds a LoV (list of values).

+ 46 - 8
taipy/gui/utils/_adapter.py

@@ -18,12 +18,29 @@ from ..icon import Icon
 from . import _MapDict
 
 
+class _AdaptedLov:
+    def __init__(self, lov: t.Any, var_type: str) -> None:
+        self._lov = lov
+        self._type = var_type
+
+    @staticmethod
+    def get_lov(lov: t.Any):
+        return lov._lov if isinstance(lov, _AdaptedLov) else lov
+
+    @staticmethod
+    def get_type(lov: t.Any):
+        return lov._type if isinstance(lov, _AdaptedLov) else None
+
+
 class _Adapter:
-    def __init__(self):
+    def __init__(self) -> None:
         self.__adapter_for_type: t.Dict[str, t.Callable] = {}
         self.__type_for_variable: t.Dict[str, str] = {}
         self.__warning_by_type: t.Set[str] = set()
 
+    def _get_adapted_lov(self, lov: t.Any, var_type: str) -> _AdaptedLov:
+        return _AdaptedLov(lov, var_type)
+
     def _add_for_type(self, type_name: str, adapter: t.Callable) -> None:
         self.__adapter_for_type[type_name] = adapter
 
@@ -40,23 +57,44 @@ class _Adapter:
             index += 1
         return type_name
 
-    def _run_for_var(self, var_name: str, value: t.Any, id_only=False) -> t.Any:
-        ret = self._run(self.__get_for_var(var_name, value), value, var_name, id_only)
-        return ret if ret is not None else value
+    def run(self, var_name: str, value: t.Any, id_only=False) -> t.Any:
+        lov = _AdaptedLov.get_lov(value)
+        adapter = self.__get_for_var(var_name, value)
+        if isinstance(lov, (list, tuple)):
+            res = []
+            for elt in lov:
+                v = self._run(adapter, elt, var_name, id_only)
+                res.append(v if v is not None else elt)
+            return res
+        return self._run(adapter, lov, var_name, id_only)
 
     def __get_for_var(self, var_name: str, value: t.Any) -> t.Optional[t.Callable]:
         adapter = None
+        type_name = _AdaptedLov.get_type(value)
+        if type_name:
+            adapter = self.__adapter_for_type.get(type_name)
+        if callable(adapter):
+            return adapter
         type_name = self.__type_for_variable.get(var_name)
         if not isinstance(type_name, str):
             adapter = self.__adapter_for_type.get(var_name)
-            type_name = var_name if callable(adapter) else type(value).__name__
+            lov = _AdaptedLov.get_lov(value)
+            elt = lov[0] if isinstance(lov, (list, tuple)) and len(lov) else None
+            type_name = var_name if callable(adapter) else type(elt).__name__
         if adapter is None:
             adapter = self.__adapter_for_type.get(type_name)
         return adapter if callable(adapter) else None
 
-    def _get_elt_per_ids(self, var_name: str, lov: t.List[t.Any]) -> t.Dict[str, t.Any]:
+    def _get_elt_per_ids(
+        self, var_name: str, lov: t.List[t.Any], adapter: t.Optional[t.Callable] = None
+    ) -> t.Dict[str, t.Any]:
         dict_res = {}
-        adapter = self.__get_for_var(var_name, lov[0] if lov else None)
+        type_name = _AdaptedLov.get_type(lov)
+        lov = _AdaptedLov.get_lov(lov)
+        if not adapter and type_name:
+            adapter = self.__adapter_for_type.get(type_name)
+        if not adapter:
+            adapter = self.__get_for_var(var_name, lov[0] if lov else None)
         for value in lov:
             try:
                 result = adapter(value._dict if isinstance(value, _MapDict) else value) if adapter else value
@@ -64,7 +102,7 @@ class _Adapter:
                     dict_res[self.__get_id(result)] = value
                     children = self.__get_children(result)
                     if children is not None:
-                        dict_res.update(self._get_elt_per_ids(var_name, children))
+                        dict_res.update(self._get_elt_per_ids(var_name, children, adapter))
             except Exception as e:
                 _warn(f"Cannot run adapter for {var_name}", e)
         return dict_res

+ 1 - 1
taipy/gui/utils/_runtime_manager.py

@@ -18,7 +18,7 @@ if t.TYPE_CHECKING:
 
 
 class _RuntimeManager(object, metaclass=_Singleton):
-    def __init__(self):
+    def __init__(self) -> None:
         self.__port_gui: t.Dict[int, "Gui"] = {}
 
     def add_gui(self, gui: "Gui", port: int):

+ 17 - 10
taipy/gui_core/_context.py

@@ -277,19 +277,20 @@ class _GuiCoreContext(CoreEventConsumerBase):
 
     def crud_scenario(self, state: State, id: str, payload: t.Dict[str, str]):  # noqa: C901
         args = payload.get("args")
+        start_idx = 2
         if (
             args is None
             or not isinstance(args, list)
-            or len(args) < 4
-            or not isinstance(args[1], bool)
-            or not isinstance(args[2], bool)
-            or not isinstance(args[3], dict)
+            or len(args) < start_idx + 3
+            or not isinstance(args[start_idx], bool)
+            or not isinstance(args[start_idx + 1], bool)
+            or not isinstance(args[start_idx + 2], dict)
         ):
             return
-        update = args[1]
-        delete = args[2]
-        data = args[3]
-        with_dialog = True if len(args) < 5 else bool(args[4])
+        update = args[start_idx]
+        delete = args[start_idx + 1]
+        data = args[start_idx + 2]
+        with_dialog = True if len(args) < start_idx + 4 else bool(args[start_idx + 3])
         scenario = None
 
         name = data.get(_GuiCoreContext.__PROP_ENTITY_NAME)
@@ -331,7 +332,7 @@ class _GuiCoreContext(CoreEventConsumerBase):
                 date = None
             scenario_id = None
             try:
-                gui: Gui = state._gui
+                gui = state.get_gui()
                 on_creation = args[0] if isinstance(args[0], str) else None
                 on_creation_function = gui._get_user_function(on_creation) if on_creation else None
                 if callable(on_creation_function):
@@ -370,7 +371,7 @@ class _GuiCoreContext(CoreEventConsumerBase):
                     _warn(f"on_creation(): '{on_creation}' is not a function.")
                 elif not with_dialog:
                     if len(Config.scenarios) == 2:
-                        scenario_config = [sc for k, sc in Config.scenarios.items() if k != "default"][0]
+                        scenario_config = next(sc for k, sc in Config.scenarios.items() if k != "default")
                     else:
                         state.assign(
                             _GuiCoreContext._SCENARIO_SELECTOR_ERROR_VAR,
@@ -385,6 +386,12 @@ class _GuiCoreContext(CoreEventConsumerBase):
                 state.assign(_GuiCoreContext._SCENARIO_SELECTOR_ERROR_VAR, f"Error creating Scenario. {e}")
             finally:
                 self.scenario_refresh(scenario_id)
+                if scenario and (sel_scenario_var := args[1] if isinstance(args[1], str) else None):
+                    try:
+                        var_name, _ = gui._get_real_var_name(sel_scenario_var)
+                        state.assign(var_name, scenario)
+                    except Exception as e:  # pragma: no cover
+                        _warn("Can't find value variable name in context", e)
         if scenario:
             if not is_editable(scenario):
                 state.assign(

+ 3 - 3
taipy/rest/api/exceptions/exceptions.py

@@ -11,15 +11,15 @@
 
 
 class ConfigIdMissingException(Exception):
-    def __init__(self):
+    def __init__(self) -> None:
         self.message = "Config id is missing."
 
 
 class ScenarioIdMissingException(Exception):
-    def __init__(self):
+    def __init__(self) -> None:
         self.message = "Scenario id is missing."
 
 
 class SequenceNameMissingException(Exception):
-    def __init__(self):
+    def __init__(self) -> None:
         self.message = "Sequence name is missing."

+ 1 - 1
taipy/rest/app.py

@@ -18,7 +18,7 @@ from .commons.encoder import _CustomEncoder
 from .extensions import apispec
 
 
-def create_app(testing=False, flask_env=None, secret_key=None):
+def create_app(testing=False, flask_env=None, secret_key=None) -> Flask:
     """Application factory, used to create application"""
     app = Flask(__name__)
     app.config.update(

+ 4 - 2
taipy/rest/rest.py

@@ -8,6 +8,8 @@
 # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 # specific language governing permissions and limitations under the License.
+from flask import Flask
+
 from taipy.config import Config
 
 from .app import create_app as _create_app
@@ -18,7 +20,7 @@ class Rest:
     Runnable Rest application serving REST APIs on top of Taipy Core functionalities.
     """
 
-    def __init__(self):
+    def __init__(self) -> None:
         """
         Initialize a REST API server.
 
@@ -31,7 +33,7 @@ class Rest:
         However, editing these parameters is only recommended for advanced users. Indeed, the default behavior of the
         REST server without any required configuration satisfies all the standard and basic needs.
         """
-        self._app = _create_app(
+        self._app: Flask = _create_app(
             Config.global_config.testing or False, Config.global_config.env, Config.global_config.secret_key
         )
 

+ 2 - 3
tests/core/_orchestrator/_dispatcher/test_dispatcher__needs_to_run.py

@@ -77,9 +77,8 @@ def test_need_to_run_skippable_task_with_validity_period_on_output():
 
     assert dispatcher._needs_to_run(task)  # output data is not edited
 
-    output_edit_time = datetime.now()  # edit time
-    with freezegun.freeze_time(output_edit_time):
-        task.output["output"].write("Hello world !")  # output data is edited
+    task.output["output"].write("Hello world !")  # output data is edited
+    output_edit_time = task.output["output"].last_edit_date
 
     with freezegun.freeze_time(output_edit_time + timedelta(minutes=30)):  # 30 min after edit time
         assert not dispatcher._needs_to_run(task)  # output data is written and validity period not expired

+ 6 - 4
tests/core/_orchestrator/test_orchestrator__submit.py

@@ -63,9 +63,9 @@ def test_submit_scenario_development_mode():
 
     # data nodes should have been written (except the input dn_0)
     assert scenario.dn_0.last_edit_date < submit_time
-    assert scenario.dn_1.last_edit_date == submit_time
-    assert scenario.dn_2.last_edit_date == submit_time
-    assert scenario.dn_3.last_edit_date == submit_time
+    assert scenario.dn_1.last_edit_date is not None
+    assert scenario.dn_2.last_edit_date is not None
+    assert scenario.dn_3.last_edit_date is not None
 
     # jobs are created in a specific order and are correct
     assert len(jobs) == 4
@@ -340,7 +340,9 @@ def test_submit_sequence_development_mode():
 
     # data nodes should have been written (except the input dn_0)
     assert sce.dn_0.last_edit_date < submit_time
-    assert sce.dn_1.last_edit_date == submit_time == sce.dn_2.last_edit_date == sce.dn_3.last_edit_date
+    assert sce.dn_1.last_edit_date is not None
+    assert sce.dn_2.last_edit_date is not None
+    assert sce.dn_3.last_edit_date is not None
 
     # jobs are created in a specific order and are correct
     assert len(jobs) == 3

+ 1 - 1
tests/core/_orchestrator/test_orchestrator__submit_task.py

@@ -55,7 +55,7 @@ def test_submit_task_development_mode():
         job = submission.jobs[0]
 
     # task output should have been written
-    assert scenario.dn_1.last_edit_date == submit_time
+    assert scenario.dn_1.last_edit_date is not None
 
     # job exists and is correct
     assert job.task == scenario.t1

+ 10 - 3
tests/core/cycle/test_cycle_repositories.py

@@ -21,12 +21,19 @@ from taipy.core.exceptions import ModelNotFound
 
 class TestCycleRepositories:
     @pytest.mark.parametrize("repo", [_CycleFSRepository, _CycleSQLRepository])
-    def test_save_and_load(self, cycle, repo, init_sql_repo):
+    def test_save_and_load(self, cycle: Cycle, repo, init_sql_repo):
         repository = repo()
         repository._save(cycle)
 
-        obj = repository._load(cycle.id)
-        assert isinstance(obj, Cycle)
+        loaded_cycle = repository._load(cycle.id)
+        assert isinstance(loaded_cycle, Cycle)
+        assert cycle._frequency == loaded_cycle._frequency
+        assert cycle._creation_date == loaded_cycle._creation_date
+        assert cycle._start_date == loaded_cycle._start_date
+        assert cycle._end_date == loaded_cycle._end_date
+        assert cycle._name == loaded_cycle._name
+        assert cycle.id == loaded_cycle.id
+        assert cycle._properties == loaded_cycle._properties
 
     @pytest.mark.parametrize("repo", [_CycleFSRepository, _CycleSQLRepository])
     def test_exists(self, cycle, repo, init_sql_repo):

+ 16 - 3
tests/core/data/test_data_repositories.py

@@ -21,12 +21,25 @@ from taipy.core.exceptions import ModelNotFound
 
 class TestDataNodeRepository:
     @pytest.mark.parametrize("repo", [_DataFSRepository, _DataSQLRepository])
-    def test_save_and_load(self, data_node, repo, init_sql_repo):
+    def test_save_and_load(self, data_node: DataNode, repo, init_sql_repo):
         repository = repo()
         repository._save(data_node)
 
-        obj = repository._load(data_node.id)
-        assert isinstance(obj, DataNode)
+        loaded_data_node = repository._load(data_node.id)
+        assert isinstance(loaded_data_node, DataNode)
+        assert data_node.id == loaded_data_node.id
+        assert data_node._config_id == loaded_data_node._config_id
+        assert data_node._owner_id == loaded_data_node._owner_id
+        assert data_node._parent_ids == loaded_data_node._parent_ids
+        assert data_node._scope == loaded_data_node._scope
+        assert data_node._last_edit_date == loaded_data_node._last_edit_date
+        assert data_node._edit_in_progress == loaded_data_node._edit_in_progress
+        assert data_node._version == loaded_data_node._version
+        assert data_node._validity_period == loaded_data_node._validity_period
+        assert data_node._editor_id == loaded_data_node._editor_id
+        assert data_node._editor_expiration_date == loaded_data_node._editor_expiration_date
+        assert data_node._edits == loaded_data_node._edits
+        assert data_node._properties == loaded_data_node._properties
 
     @pytest.mark.parametrize("repo", [_DataFSRepository, _DataSQLRepository])
     def test_exists(self, data_node, repo, init_sql_repo):

+ 6 - 5
tests/core/notification/test_events_published.py

@@ -10,6 +10,7 @@
 # specific language governing permissions and limitations under the License.
 
 from queue import SimpleQueue
+from typing import Dict, List
 
 from taipy.config import Config, Frequency
 from taipy.core import taipy as tp
@@ -25,11 +26,11 @@ class Snapshot:
     A captured snapshot of the recording core events consumer.
     """
 
-    def __init__(self):
-        self.collected_events = []
-        self.entity_type_collected = {}
-        self.operation_collected = {}
-        self.attr_name_collected = {}
+    def __init__(self) -> None:
+        self.collected_events: List[Event] = []
+        self.entity_type_collected: Dict[EventEntityType, int] = {}
+        self.operation_collected: Dict[EventEntityType, int] = {}
+        self.attr_name_collected: Dict[EventEntityType, int] = {}
 
     def capture_event(self, event):
         self.collected_events.append(event)

+ 14 - 3
tests/core/scenario/test_scenario_repositories.py

@@ -21,12 +21,23 @@ from taipy.core.scenario.scenario import Scenario, ScenarioId
 
 class TestScenarioFSRepository:
     @pytest.mark.parametrize("repo", [_ScenarioFSRepository, _ScenarioSQLRepository])
-    def test_save_and_load(self, scenario, repo, init_sql_repo):
+    def test_save_and_load(self, scenario: Scenario, repo, init_sql_repo):
         repository = repo()
         repository._save(scenario)
 
-        obj = repository._load(scenario.id)
-        assert isinstance(obj, Scenario)
+        loaded_scenario = repository._load(scenario.id)
+        assert isinstance(loaded_scenario, Scenario)
+        assert scenario._config_id == loaded_scenario._config_id
+        assert scenario.id == loaded_scenario.id
+        assert scenario._tasks == loaded_scenario._tasks
+        assert scenario._additional_data_nodes == loaded_scenario._additional_data_nodes
+        assert scenario._creation_date == loaded_scenario._creation_date
+        assert scenario._cycle == loaded_scenario._cycle
+        assert scenario._primary_scenario == loaded_scenario._primary_scenario
+        assert scenario._tags == loaded_scenario._tags
+        assert scenario._properties == loaded_scenario._properties
+        assert scenario._sequences == loaded_scenario._sequences
+        assert scenario._version == loaded_scenario._version
 
     @pytest.mark.parametrize("repo", [_ScenarioFSRepository, _ScenarioSQLRepository])
     def test_exists(self, scenario, repo, init_sql_repo):

+ 108 - 76
tests/core/task/test_task_repositories.py

@@ -13,6 +13,8 @@ import os
 
 import pytest
 
+from taipy.config.config import Config
+from taipy.core.data._data_fs_repository import _DataFSRepository
 from taipy.core.data._data_sql_repository import _DataSQLRepository
 from taipy.core.exceptions import ModelNotFound
 from taipy.core.task._task_fs_repository import _TaskFSRepository
@@ -21,149 +23,179 @@ from taipy.core.task.task import Task, TaskId
 
 
 class TestTaskFSRepository:
-    @pytest.mark.parametrize("repo", [_TaskFSRepository, _TaskSQLRepository])
-    def test_save_and_load(self, data_node, repo, init_sql_repo):
-        repository = repo()
-        _DataSQLRepository()._save(data_node)
+    @pytest.mark.parametrize("repo", [(_TaskFSRepository, _DataFSRepository), (_TaskSQLRepository, _DataSQLRepository)])
+    def test_save_and_load(self, data_node, repo, tmp_sqlite):
+        if repo[1] == _DataSQLRepository:
+            Config.configure_core(repository_type="sql", repository_properties={"db_location": tmp_sqlite})
+        task_repository, data_repository = repo[0](), repo[1]()
+        data_repository._save(data_node)
         task = Task("task_config_id", {}, print, [data_node], [data_node])
 
-        repository._save(task)
-
-        obj = repository._load(task.id)
-        assert isinstance(obj, Task)
-
-    @pytest.mark.parametrize("repo", [_TaskFSRepository, _TaskSQLRepository])
-    def test_exists(self, data_node, repo, init_sql_repo):
-        repository = repo()
-        _DataSQLRepository()._save(data_node)
+        task_repository._save(task)
+
+        loaded_task = task_repository._load(task.id)
+        assert isinstance(loaded_task, Task)
+        assert task._config_id == loaded_task._config_id
+        assert task.id == loaded_task.id
+        assert task._owner_id == loaded_task._owner_id
+        assert task._parent_ids == loaded_task._parent_ids
+        assert task._input == loaded_task._input
+        assert task._output == loaded_task._output
+        assert task._function == loaded_task._function
+        assert task._version == loaded_task._version
+        assert task._skippable == loaded_task._skippable
+        assert task._properties == loaded_task._properties
+
+    @pytest.mark.parametrize("repo", [(_TaskFSRepository, _DataFSRepository), (_TaskSQLRepository, _DataSQLRepository)])
+    def test_exists(self, data_node, repo, tmp_sqlite):
+        if repo[1] == _DataSQLRepository:
+            Config.configure_core(repository_type="sql", repository_properties={"db_location": tmp_sqlite})
+        task_repository, data_repository = repo[0](), repo[1]()
+        data_repository._save(data_node)
         task = Task("task_config_id", {}, print, [data_node], [data_node])
 
-        repository._save(task)
+        task_repository._save(task)
 
-        assert repository._exists(task.id)
-        assert not repository._exists("not-existed-task")
+        assert task_repository._exists(task.id)
+        assert not task_repository._exists("not-existed-task")
 
-    @pytest.mark.parametrize("repo", [_TaskFSRepository, _TaskSQLRepository])
-    def test_load_all(self, data_node, repo, init_sql_repo):
-        repository = repo()
-        _DataSQLRepository()._save(data_node)
+    @pytest.mark.parametrize("repo", [(_TaskFSRepository, _DataFSRepository), (_TaskSQLRepository, _DataSQLRepository)])
+    def test_load_all(self, data_node, repo, tmp_sqlite):
+        if repo[1] == _DataSQLRepository:
+            Config.configure_core(repository_type="sql", repository_properties={"db_location": tmp_sqlite})
+        task_repository, data_repository = repo[0](), repo[1]()
+        data_repository._save(data_node)
         task = Task("task_config_id", {}, print, [data_node], [data_node])
 
         for i in range(10):
             task.id = TaskId(f"task-{i}")
-            repository._save(task)
-        data_nodes = repository._load_all()
+            task_repository._save(task)
+        data_nodes = task_repository._load_all()
 
         assert len(data_nodes) == 10
 
-    @pytest.mark.parametrize("repo", [_TaskFSRepository, _TaskSQLRepository])
-    def test_load_all_with_filters(self, data_node, repo, init_sql_repo):
-        repository = repo()
-        _DataSQLRepository()._save(data_node)
+    @pytest.mark.parametrize("repo", [(_TaskFSRepository, _DataFSRepository), (_TaskSQLRepository, _DataSQLRepository)])
+    def test_load_all_with_filters(self, data_node, repo, tmp_sqlite):
+        if repo[1] == _DataSQLRepository:
+            Config.configure_core(repository_type="sql", repository_properties={"db_location": tmp_sqlite})
+        task_repository, data_repository = repo[0](), repo[1]()
+        data_repository._save(data_node)
         task = Task("task_config_id", {}, print, [data_node], [data_node])
 
         for i in range(10):
             task.id = TaskId(f"task-{i}")
             task._owner_id = f"owner-{i}"
-            repository._save(task)
-        objs = repository._load_all(filters=[{"owner_id": "owner-2"}])
+            task_repository._save(task)
+        objs = task_repository._load_all(filters=[{"owner_id": "owner-2"}])
 
         assert len(objs) == 1
 
-    @pytest.mark.parametrize("repo", [_TaskFSRepository, _TaskSQLRepository])
-    def test_delete(self, data_node, repo, init_sql_repo):
-        repository = repo()
-        _DataSQLRepository()._save(data_node)
+    @pytest.mark.parametrize("repo", [(_TaskFSRepository, _DataFSRepository), (_TaskSQLRepository, _DataSQLRepository)])
+    def test_delete(self, data_node, repo, tmp_sqlite):
+        if repo[1] == _DataSQLRepository:
+            Config.configure_core(repository_type="sql", repository_properties={"db_location": tmp_sqlite})
+        task_repository, data_repository = repo[0](), repo[1]()
+        data_repository._save(data_node)
         task = Task("task_config_id", {}, print, [data_node], [data_node])
-        repository._save(task)
+        task_repository._save(task)
 
-        repository._delete(task.id)
+        task_repository._delete(task.id)
 
         with pytest.raises(ModelNotFound):
-            repository._load(task.id)
-
-    @pytest.mark.parametrize("repo", [_TaskFSRepository, _TaskSQLRepository])
-    def test_delete_all(self, data_node, repo, init_sql_repo):
-        repository = repo()
-        _DataSQLRepository()._save(data_node)
+            task_repository._load(task.id)
+
+    @pytest.mark.parametrize("repo", [(_TaskFSRepository, _DataFSRepository), (_TaskSQLRepository, _DataSQLRepository)])
+    def test_delete_all(self, data_node, repo, tmp_sqlite):
+        if repo[1] == _DataSQLRepository:
+            Config.configure_core(repository_type="sql", repository_properties={"db_location": tmp_sqlite})
+        task_repository, data_repository = repo[0](), repo[1]()
+        data_repository._save(data_node)
         task = Task("task_config_id", {}, print, [data_node], [data_node])
 
         for i in range(10):
             task.id = TaskId(f"task-{i}")
-            repository._save(task)
+            task_repository._save(task)
 
-        assert len(repository._load_all()) == 10
+        assert len(task_repository._load_all()) == 10
 
-        repository._delete_all()
+        task_repository._delete_all()
 
-        assert len(repository._load_all()) == 0
+        assert len(task_repository._load_all()) == 0
 
-    @pytest.mark.parametrize("repo", [_TaskFSRepository, _TaskSQLRepository])
-    def test_delete_many(self, data_node, repo, init_sql_repo):
-        repository = repo()
-        _DataSQLRepository()._save(data_node)
+    @pytest.mark.parametrize("repo", [(_TaskFSRepository, _DataFSRepository), (_TaskSQLRepository, _DataSQLRepository)])
+    def test_delete_many(self, data_node, repo, tmp_sqlite):
+        if repo[1] == _DataSQLRepository:
+            Config.configure_core(repository_type="sql", repository_properties={"db_location": tmp_sqlite})
+        task_repository, data_repository = repo[0](), repo[1]()
+        data_repository._save(data_node)
         task = Task("task_config_id", {}, print, [data_node], [data_node])
 
         for i in range(10):
             task.id = TaskId(f"task-{i}")
-            repository._save(task)
+            task_repository._save(task)
 
-        objs = repository._load_all()
+        objs = task_repository._load_all()
         assert len(objs) == 10
         ids = [x.id for x in objs[:3]]
-        repository._delete_many(ids)
+        task_repository._delete_many(ids)
 
-        assert len(repository._load_all()) == 7
+        assert len(task_repository._load_all()) == 7
 
-    @pytest.mark.parametrize("repo", [_TaskFSRepository, _TaskSQLRepository])
-    def test_delete_by(self, data_node, repo, init_sql_repo):
-        repository = repo()
-        _DataSQLRepository()._save(data_node)
+    @pytest.mark.parametrize("repo", [(_TaskFSRepository, _DataFSRepository), (_TaskSQLRepository, _DataSQLRepository)])
+    def test_delete_by(self, data_node, repo, tmp_sqlite):
+        if repo[1] == _DataSQLRepository:
+            Config.configure_core(repository_type="sql", repository_properties={"db_location": tmp_sqlite})
+        task_repository, data_repository = repo[0](), repo[1]()
+        data_repository._save(data_node)
         task = Task("task_config_id", {}, print, [data_node], [data_node])
 
         # Create 5 entities with version 1.0 and 5 entities with version 2.0
         for i in range(10):
             task.id = TaskId(f"task-{i}")
             task._version = f"{(i+1) // 5}.0"
-            repository._save(task)
+            task_repository._save(task)
 
-        objs = repository._load_all()
+        objs = task_repository._load_all()
         assert len(objs) == 10
-        repository._delete_by("version", "1.0")
+        task_repository._delete_by("version", "1.0")
 
-        assert len(repository._load_all()) == 5
+        assert len(task_repository._load_all()) == 5
 
-    @pytest.mark.parametrize("repo", [_TaskFSRepository, _TaskSQLRepository])
-    def test_search(self, data_node, repo, init_sql_repo):
-        repository = repo()
-        _DataSQLRepository()._save(data_node)
+    @pytest.mark.parametrize("repo", [(_TaskFSRepository, _DataFSRepository), (_TaskSQLRepository, _DataSQLRepository)])
+    def test_search(self, data_node, repo, tmp_sqlite):
+        if repo[1] == _DataSQLRepository:
+            Config.configure_core(repository_type="sql", repository_properties={"db_location": tmp_sqlite})
+        task_repository, data_repository = repo[0](), repo[1]()
+        data_repository._save(data_node)
         task = Task("task_config_id", {}, print, [data_node], [data_node], version="random_version_number")
 
         for i in range(10):
             task.id = TaskId(f"task-{i}")
             task._owner_id = f"owner-{i}"
-            repository._save(task)
+            task_repository._save(task)
 
-        assert len(repository._load_all()) == 10
+        assert len(task_repository._load_all()) == 10
 
-        objs = repository._search("owner_id", "owner-2")
+        objs = task_repository._search("owner_id", "owner-2")
         assert len(objs) == 1
         assert isinstance(objs[0], Task)
 
-        objs = repository._search("owner_id", "owner-2", filters=[{"version": "random_version_number"}])
+        objs = task_repository._search("owner_id", "owner-2", filters=[{"version": "random_version_number"}])
         assert len(objs) == 1
         assert isinstance(objs[0], Task)
 
-        assert repository._search("owner_id", "owner-2", filters=[{"version": "non_existed_version"}]) == []
+        assert task_repository._search("owner_id", "owner-2", filters=[{"version": "non_existed_version"}]) == []
 
-    @pytest.mark.parametrize("repo", [_TaskFSRepository, _TaskSQLRepository])
-    def test_export(self, tmpdir, data_node, repo, init_sql_repo):
-        repository = repo()
-        _DataSQLRepository()._save(data_node)
+    @pytest.mark.parametrize("repo", [(_TaskFSRepository, _DataFSRepository), (_TaskSQLRepository, _DataSQLRepository)])
+    def test_export(self, tmpdir, data_node, repo, tmp_sqlite):
+        if repo[1] == _DataSQLRepository:
+            Config.configure_core(repository_type="sql", repository_properties={"db_location": tmp_sqlite})
+        task_repository, data_repository = repo[0](), repo[1]()
+        data_repository._save(data_node)
         task = Task("task_config_id", {}, print, [data_node], [data_node])
-        repository._save(task)
+        task_repository._save(task)
 
-        repository._export(task.id, tmpdir.strpath)
-        dir_path = repository.dir_path if repo == _TaskFSRepository else os.path.join(tmpdir.strpath, "task")
+        task_repository._export(task.id, tmpdir.strpath)
+        dir_path = task_repository.dir_path if repo[0] == _TaskFSRepository else os.path.join(tmpdir.strpath, "task")
 
         assert os.path.exists(os.path.join(dir_path, f"{task.id}.json"))

+ 193 - 0
tests/core/test_taipy/test_export_with_sql_repo.py

@@ -0,0 +1,193 @@
+# Copyright 2021-2024 Avaiga Private Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+
+import os
+import shutil
+
+import pandas as pd
+import pytest
+
+import taipy.core.taipy as tp
+from taipy import Config, Frequency, Scope
+from taipy.core.exceptions import ExportFolderAlreadyExists, InvalidExportPath
+
+
+@pytest.fixture(scope="function", autouse=True)
+def clean_tmp_folder():
+    shutil.rmtree("./tmp", ignore_errors=True)
+    yield
+    shutil.rmtree("./tmp", ignore_errors=True)
+
+
+def plus_1(x):
+    return x + 1
+
+
+def plus_1_dataframe(x):
+    return pd.DataFrame({"output": [x + 1]})
+
+
+def configure_test_scenario(input_data, frequency=None):
+    input_cfg = Config.configure_data_node(
+        id=f"i_{input_data}", storage_type="pickle", scope=Scope.SCENARIO, default_data=input_data
+    )
+    csv_output_cfg = Config.configure_data_node(id=f"o_{input_data}_csv", storage_type="csv")
+    excel_output_cfg = Config.configure_data_node(id=f"o_{input_data}_excel", storage_type="excel")
+    parquet_output_cfg = Config.configure_data_node(id=f"o_{input_data}_parquet", storage_type="parquet")
+    json_output_cfg = Config.configure_data_node(id=f"o_{input_data}_json", storage_type="json")
+
+    csv_task_cfg = Config.configure_task(f"t_{input_data}_csv", plus_1_dataframe, input_cfg, csv_output_cfg)
+    excel_task_cfg = Config.configure_task(f"t_{input_data}_excel", plus_1_dataframe, input_cfg, excel_output_cfg)
+    parquet_task_cfg = Config.configure_task(f"t_{input_data}_parquet", plus_1_dataframe, input_cfg, parquet_output_cfg)
+    json_task_cfg = Config.configure_task(f"t_{input_data}_json", plus_1, input_cfg, json_output_cfg)
+    scenario_cfg = Config.configure_scenario(
+        id=f"s_{input_data}",
+        task_configs=[csv_task_cfg, excel_task_cfg, parquet_task_cfg, json_task_cfg],
+        frequency=frequency,
+    )
+
+    return scenario_cfg
+
+
+def test_export_scenario_to_the_storage_folder(init_sql_repo):
+    scenario_cfg = configure_test_scenario(1, frequency=Frequency.DAILY)
+    scenario = tp.create_scenario(scenario_cfg)
+
+    with pytest.raises(InvalidExportPath):
+        tp.export_scenario(scenario.id, Config.core.taipy_storage_folder)
+
+
+def test_export_scenario_with_cycle(init_sql_repo):
+    scenario_cfg = configure_test_scenario(1, frequency=Frequency.DAILY)
+
+    scenario = tp.create_scenario(scenario_cfg)
+    submission = tp.submit(scenario)
+    jobs = submission.jobs
+
+    # Export the submitted scenario
+    tp.export_scenario(scenario.id, "./tmp/exp_scenario")
+
+    assert sorted(os.listdir("./tmp/exp_scenario/data_node")) == sorted(
+        [
+            f"{scenario.i_1.id}.json",
+            f"{scenario.o_1_csv.id}.json",
+            f"{scenario.o_1_excel.id}.json",
+            f"{scenario.o_1_parquet.id}.json",
+            f"{scenario.o_1_json.id}.json",
+        ]
+    )
+    assert sorted(os.listdir("./tmp/exp_scenario/task")) == sorted(
+        [
+            f"{scenario.t_1_csv.id}.json",
+            f"{scenario.t_1_excel.id}.json",
+            f"{scenario.t_1_parquet.id}.json",
+            f"{scenario.t_1_json.id}.json",
+        ]
+    )
+    assert sorted(os.listdir("./tmp/exp_scenario/scenario")) == sorted([f"{scenario.id}.json"])
+    assert sorted(os.listdir("./tmp/exp_scenario/job")) == sorted(
+        [f"{jobs[0].id}.json", f"{jobs[1].id}.json", f"{jobs[2].id}.json", f"{jobs[3].id}.json"]
+    )
+    assert os.listdir("./tmp/exp_scenario/submission") == [f"{submission.id}.json"]
+    assert sorted(os.listdir("./tmp/exp_scenario/cycle")) == sorted([f"{scenario.cycle.id}.json"])
+
+
+def test_export_scenario_without_cycle(init_sql_repo):
+    scenario_cfg = configure_test_scenario(1)
+
+    scenario = tp.create_scenario(scenario_cfg)
+    tp.submit(scenario)
+
+    # Export the submitted scenario
+    tp.export_scenario(scenario.id, "./tmp/exp_scenario")
+
+    assert os.path.exists("./tmp/exp_scenario/data_node")
+    assert os.path.exists("./tmp/exp_scenario/task")
+    assert os.path.exists("./tmp/exp_scenario/scenario")
+    assert os.path.exists("./tmp/exp_scenario/job")
+    assert os.path.exists("./tmp/exp_scenario/submission")
+    assert not os.path.exists("./tmp/exp_scenario/cycle")  # No cycle
+
+
+def test_export_scenario_override_existing_files(init_sql_repo):
+    scenario_1_cfg = configure_test_scenario(1, frequency=Frequency.DAILY)
+    scenario_2_cfg = configure_test_scenario(2)
+
+    scenario_1 = tp.create_scenario(scenario_1_cfg)
+    tp.submit(scenario_1)
+
+    # Export the submitted scenario_1
+    tp.export_scenario(scenario_1.id, "./tmp/exp_scenario")
+    assert os.path.exists("./tmp/exp_scenario/data_node")
+    assert os.path.exists("./tmp/exp_scenario/task")
+    assert os.path.exists("./tmp/exp_scenario/scenario")
+    assert os.path.exists("./tmp/exp_scenario/job")
+    assert os.path.exists("./tmp/exp_scenario/submission")
+    assert os.path.exists("./tmp/exp_scenario/cycle")
+
+    scenario_2 = tp.create_scenario(scenario_2_cfg)
+    tp.submit(scenario_2)
+
+    # Export the submitted scenario_2 to the same folder should raise an error
+    with pytest.raises(ExportFolderAlreadyExists):
+        tp.export_scenario(scenario_2.id, "./tmp/exp_scenario")
+
+    # Export the submitted scenario_2 without a cycle and override the existing files
+    tp.export_scenario(scenario_2.id, "./tmp/exp_scenario", override=True)
+    assert os.path.exists("./tmp/exp_scenario/data_node")
+    assert os.path.exists("./tmp/exp_scenario/task")
+    assert os.path.exists("./tmp/exp_scenario/scenario")
+    assert os.path.exists("./tmp/exp_scenario/job")
+    assert os.path.exists("./tmp/exp_scenario/submission")
+    # The cycles folder should be removed when overriding
+    assert not os.path.exists("./tmp/exp_scenario/cycle")
+
+
+def test_export_scenario_filesystem_with_data(init_sql_repo):
+    scenario_cfg = configure_test_scenario(1)
+    scenario = tp.create_scenario(scenario_cfg)
+    tp.submit(scenario)
+
+    # Export scenario without data
+    tp.export_scenario(scenario.id, "./tmp/exp_scenario")
+    assert not os.path.exists("./tmp/exp_scenario/user_data")
+
+    # Export scenario with data
+    tp.export_scenario(scenario.id, "./tmp/exp_scenario", include_data=True, override=True)
+    assert os.path.exists("./tmp/exp_scenario/user_data")
+    data_files = [f for _, _, files in os.walk("./tmp/exp_scenario/user_data") for f in files]
+    assert sorted(data_files) == sorted(
+        [
+            f"{scenario.i_1.id}.p",
+            f"{scenario.o_1_csv.id}.csv",
+            f"{scenario.o_1_excel.id}.xlsx",
+            f"{scenario.o_1_parquet.id}.parquet",
+            f"{scenario.o_1_json.id}.json",
+        ]
+    )
+
+
+def test_export_non_file_based_data_node_raise_warning(init_sql_repo, caplog):
+    input_cfg = Config.configure_data_node(id="i", storage_type="pickle", scope=Scope.SCENARIO, default_data=1)
+    csv_output_cfg = Config.configure_data_node(id="o_csv", storage_type="csv")
+    in_mem_output_cfg = Config.configure_data_node(id="o_mem", storage_type="in_memory")
+
+    csv_task_cfg = Config.configure_task("t_csv", plus_1_dataframe, input_cfg, csv_output_cfg)
+    in_mem_task_cfg = Config.configure_task("t_mem", plus_1, input_cfg, in_mem_output_cfg)
+    scenario_cfg = Config.configure_scenario(id="s", task_configs=[csv_task_cfg, in_mem_task_cfg])
+
+    scenario = tp.create_scenario(scenario_cfg)
+    tp.submit(scenario)
+
+    # Export scenario with in-memory data node
+    tp.export_scenario(scenario.id, "./tmp/exp_scenario", include_data=True)
+    expected_warning = f"Data node {scenario.o_mem.id} is not a file-based data node and the data will not be exported"
+    assert expected_warning in caplog.text

+ 2 - 2
tests/gui/builder/control/test_menu.py

@@ -21,8 +21,8 @@ def test_menu_builder(gui: Gui, test_client, helpers):
         "<MenuCtl",
         'libClassName="taipy-menu"',
         'defaultLov="[&quot;Item 1&quot;, &quot;Item 2&quot;, &quot;Item 3&quot;, &quot;Item 4&quot;]"',
-        "lov={_TpL_tpec_TpExPr_lov_TPMDL_0}",
+        "lov={_TpL_tp_TpExPr_gui_get_adapted_lov_lov_str_TPMDL_0_0}",
         'onAction="on_menu_action"',
-        'updateVars="lov=_TpL_tpec_TpExPr_lov_TPMDL_0"',
+        'updateVars="lov=_TpL_tp_TpExPr_gui_get_adapted_lov_lov_str_TPMDL_0_0"',
     ]
     helpers.test_control_builder(gui, page, expected_list)

+ 1 - 1
tests/gui/builder/control/test_navbar.py

@@ -28,6 +28,6 @@ def test_navbar_builder(gui: Gui, test_client, helpers):
     expected_list = [
         "<NavBar",
         'defaultLov="[[&quot;/page1&quot;, &quot;Page 1&quot;], [&quot;/page2&quot;, &quot;Page 2&quot;], [&quot;/page3&quot;, &quot;Page 3&quot;], [&quot;/page4&quot;, &quot;Page 4&quot;]]"',  # noqa: E501
-        "lov={_TpL_tpec_TpExPr_navlov_TPMDL_0}",
+        "lov={_TpL_tp_TpExPr_gui_get_adapted_lov_navlov_tuple_TPMDL_0_0}",
     ]
     helpers.test_control_builder(gui, page, expected_list)

+ 2 - 2
tests/gui/builder/control/test_selector.py

@@ -62,9 +62,9 @@ def test_selector_builder_3(gui: Gui, test_client, helpers):
         "<Selector",
         'defaultLov="[[&quot;1&quot;, &quot;scenario 1&quot;], [&quot;3&quot;, &quot;scenario 3&quot;], [&quot;2&quot;, &quot;scenario 2&quot;]]"',  # noqa: E501
         'defaultValue="[&quot;1&quot;]"',
-        "lov={_TpL_tpec_TpExPr_scenario_list_TPMDL_0}",
+        "lov={_TpL_tp_TpExPr_gui_get_adapted_lov_scenario_list_dict_TPMDL_0_0}",
         "propagate={false}",
-        'updateVars="lov=_TpL_tpec_TpExPr_scenario_list_TPMDL_0"',
+        'updateVars="lov=_TpL_tp_TpExPr_gui_get_adapted_lov_scenario_list_dict_TPMDL_0_0"',
         'updateVarName="_TpLv_tpec_TpExPr_selected_obj_TPMDL_0"',
         "value={_TpLv_tpec_TpExPr_selected_obj_TPMDL_0}",
     ]

+ 2 - 2
tests/gui/builder/control/test_toggle.py

@@ -37,8 +37,8 @@ def test_toggle_lov_builder(gui: Gui, test_client, helpers):
         'defaultLov="[[&quot;l1&quot;, &quot;v1&quot;], [&quot;l2&quot;, &quot;v2&quot;]]"',
         'defaultValue="l1"',
         'label="Label"',
-        "lov={_TpL_tpec_TpExPr_lov_TPMDL_0}",
-        'updateVars="lov=_TpL_tpec_TpExPr_lov_TPMDL_0"',
+        "lov={_TpL_tp_TpExPr_gui_get_adapted_lov_lov_tuple_TPMDL_0_0}",
+        'updateVars="lov=_TpL_tp_TpExPr_gui_get_adapted_lov_lov_tuple_TPMDL_0_0"',
         'updateVarName="_TpLv_tpec_TpExPr_x_TPMDL_0"',
         'unselectedValue=""',
         "value={_TpLv_tpec_TpExPr_x_TPMDL_0}",

+ 4 - 4
tests/gui/control/test_menu.py

@@ -19,9 +19,9 @@ def test_menu_md(gui: Gui, test_client, helpers):
         "<MenuCtl",
         'libClassName="taipy-menu"',
         'defaultLov="[&quot;Item 1&quot;, &quot;Item 2&quot;, &quot;Item 3&quot;, &quot;Item 4&quot;]"',
-        "lov={_TpL_tpec_TpExPr_lov_TPMDL_0}",
+        "lov={_TpL_tp_TpExPr_gui_get_adapted_lov_lov_str_TPMDL_0_0}",
         'onAction="on_menu_action"',
-        'updateVars="lov=_TpL_tpec_TpExPr_lov_TPMDL_0"',
+        'updateVars="lov=_TpL_tp_TpExPr_gui_get_adapted_lov_lov_str_TPMDL_0_0"',
     ]
     helpers.test_control_md(gui, md_string, expected_list)
 
@@ -33,7 +33,7 @@ def test_menu_html(gui: Gui, test_client, helpers):
         "<MenuCtl",
         'libClassName="taipy-menu"',
         'defaultLov="[&quot;Item 1&quot;, &quot;Item 2&quot;, &quot;Item 3&quot;, &quot;Item 4&quot;]"',
-        "lov={_TpL_tpec_TpExPr_lov_TPMDL_0}",
-        'updateVars="lov=_TpL_tpec_TpExPr_lov_TPMDL_0"',
+        "lov={_TpL_tp_TpExPr_gui_get_adapted_lov_lov_str_TPMDL_0_0}",
+        'updateVars="lov=_TpL_tp_TpExPr_gui_get_adapted_lov_lov_str_TPMDL_0_0"',
     ]
     helpers.test_control_html(gui, html_string, expected_list)

+ 2 - 2
tests/gui/control/test_navbar.py

@@ -26,7 +26,7 @@ def test_navbar_md(gui: Gui, test_client, helpers):
     expected_list = [
         "<NavBar",
         'defaultLov="[[&quot;/page1&quot;, &quot;Page 1&quot;], [&quot;/page2&quot;, &quot;Page 2&quot;], [&quot;/page3&quot;, &quot;Page 3&quot;], [&quot;/page4&quot;, &quot;Page 4&quot;]]"',  # noqa: E501
-        "lov={_TpL_tpec_TpExPr_navlov_TPMDL_0}",
+        "lov={_TpL_tp_TpExPr_gui_get_adapted_lov_navlov_tuple_TPMDL_0_0}",
     ]
     helpers.test_control_md(gui, md_string, expected_list)
 
@@ -45,6 +45,6 @@ def test_navbar_html(gui: Gui, test_client, helpers):
     expected_list = [
         "<NavBar",
         'defaultLov="[[&quot;/page1&quot;, &quot;Page 1&quot;], [&quot;/page2&quot;, &quot;Page 2&quot;], [&quot;/page3&quot;, &quot;Page 3&quot;], [&quot;/page4&quot;, &quot;Page 4&quot;]]"',  # noqa: E501
-        "lov={_TpL_tpec_TpExPr_navlov_TPMDL_0}",
+        "lov={_TpL_tp_TpExPr_gui_get_adapted_lov_navlov_tuple_TPMDL_0_0}",
     ]
     helpers.test_control_html(gui, html_string, expected_list)

+ 2 - 2
tests/gui/control/test_selector.py

@@ -53,9 +53,9 @@ def test_selector_md_3(gui: Gui, test_client, helpers):
         "<Selector",
         'defaultLov="[[&quot;1&quot;, &quot;scenario 1&quot;], [&quot;3&quot;, &quot;scenario 3&quot;], [&quot;2&quot;, &quot;scenario 2&quot;]]"',  # noqa: E501
         'defaultValue="[&quot;1&quot;]"',
-        "lov={_TpL_tpec_TpExPr_scenario_list_TPMDL_0}",
+        "lov={_TpL_tp_TpExPr_gui_get_adapted_lov_scenario_list_Scenario_TPMDL_0_0}",
         "propagate={false}",
-        'updateVars="lov=_TpL_tpec_TpExPr_scenario_list_TPMDL_0"',
+        'updateVars="lov=_TpL_tp_TpExPr_gui_get_adapted_lov_scenario_list_Scenario_TPMDL_0_0"',
         'updateVarName="_TpLv_tpec_TpExPr_selected_obj_TPMDL_0"',
         "value={_TpLv_tpec_TpExPr_selected_obj_TPMDL_0}",
     ]

+ 4 - 4
tests/gui/control/test_toggle.py

@@ -33,8 +33,8 @@ def test_toggle_lov_md(gui: Gui, test_client, helpers):
         'defaultLov="[[&quot;l1&quot;, &quot;v1&quot;], [&quot;l2&quot;, &quot;v2&quot;]]"',
         'defaultValue="l1"',
         'label="Label"',
-        "lov={_TpL_tpec_TpExPr_lov_TPMDL_0}",
-        'updateVars="lov=_TpL_tpec_TpExPr_lov_TPMDL_0"',
+        "lov={_TpL_tp_TpExPr_gui_get_adapted_lov_lov_tuple_TPMDL_0_0}",
+        'updateVars="lov=_TpL_tp_TpExPr_gui_get_adapted_lov_lov_tuple_TPMDL_0_0"',
         'updateVarName="_TpLv_tpec_TpExPr_x_TPMDL_0"',
         'unselectedValue=""',
         "value={_TpLv_tpec_TpExPr_x_TPMDL_0}",
@@ -57,8 +57,8 @@ def test_toggle_html_2(gui: Gui, test_client, helpers):
         'defaultLov="[[&quot;l1&quot;, &quot;v1&quot;], [&quot;l2&quot;, &quot;v2&quot;]]"',
         'defaultValue="l1"',
         'label="Label"',
-        "lov={_TpL_tpec_TpExPr_lov_TPMDL_0}",
-        'updateVars="lov=_TpL_tpec_TpExPr_lov_TPMDL_0"',
+        "lov={_TpL_tp_TpExPr_gui_get_adapted_lov_lov_tuple_TPMDL_0_0}",
+        'updateVars="lov=_TpL_tp_TpExPr_gui_get_adapted_lov_lov_tuple_TPMDL_0_0"',
         'updateVarName="_TpLv_tpec_TpExPr_x_TPMDL_0"',
         'unselectedValue=""',
         "value={_TpLv_tpec_TpExPr_x_TPMDL_0}",

+ 1 - 1
tests/gui/e2e/page_scopes/assets2_class_scopes/page1.py

@@ -13,7 +13,7 @@ from taipy.gui import Markdown, Page
 
 
 class Page1(Page):
-    def __init__(self):
+    def __init__(self) -> None:
         self.operand_2 = 0
         super().__init__()
 

+ 1 - 1
tests/gui/e2e/page_scopes/assets2_class_scopes/page2.py

@@ -13,7 +13,7 @@ from taipy.gui import Markdown, Page
 
 
 class Page2(Page):
-    def __init__(self):
+    def __init__(self) -> None:
         self.operand_2 = 0
         super().__init__()
 

+ 2 - 2
tests/gui/gui_specific/test_expression.py

@@ -123,8 +123,8 @@ def test_lambda_expression_selector(gui: Gui, test_client, helpers):
         "<Selector",
         'defaultLov="[[&quot;1&quot;, &quot;scenario 1&quot;], [&quot;3&quot;, &quot;scenario 3&quot;], [&quot;2&quot;, &quot;scenario 2&quot;]]"',  # noqa: E501
         'defaultValue="[&quot;1&quot;]"',
-        'updateVars="lov=_TpL_tpec_TpExPr_lov_TPMDL_0"',
-        "lov={_TpL_tpec_TpExPr_lov_TPMDL_0}",
+        'updateVars="lov=_TpL_tp_TpExPr_gui_get_adapted_lov_lov_test_TPMDL_0_0"',
+        "lov={_TpL_tp_TpExPr_gui_get_adapted_lov_lov_test_TPMDL_0_0}",
         'updateVarName="_TpLv_tpec_TpExPr_sel_TPMDL_0"',
         "value={_TpLv_tpec_TpExPr_sel_TPMDL_0}",
     ]

+ 2 - 0
tests/gui_core/test_context_is_deletable.py

@@ -58,6 +58,7 @@ class TestGuiCoreContext_is_deletable:
                 "",
                 {
                     "args": [
+                        "",
                         "",
                         True,
                         True,
@@ -76,6 +77,7 @@ class TestGuiCoreContext_is_deletable:
                     "",
                     {
                         "args": [
+                            "",
                             "",
                             True,
                             True,

+ 2 - 0
tests/gui_core/test_context_is_editable.py

@@ -57,6 +57,7 @@ class TestGuiCoreContext_is_editable:
                 "",
                 {
                     "args": [
+                        "",
                         "",
                         True,
                         False,
@@ -73,6 +74,7 @@ class TestGuiCoreContext_is_editable:
                     "",
                     {
                         "args": [
+                            "",
                             "",
                             True,
                             False,

+ 2 - 0
tests/gui_core/test_context_is_readable.py

@@ -89,6 +89,7 @@ class TestGuiCoreContext_is_readable:
                 "",
                 {
                     "args": [
+                        "",
                         "",
                         True,
                         False,
@@ -105,6 +106,7 @@ class TestGuiCoreContext_is_readable:
                     "",
                     {
                         "args": [
+                            "",
                             "",
                             True,
                             False,

+ 16 - 16
tools/packages/pipfiles/Pipfile3.10.max

@@ -50,34 +50,34 @@ version = "==4.2.13"
 
 
 [packages]
-"pyarrow" = {version="==15.0.0"}
-"networkx" = {version="==3.2.1", markers="python_version>'3.8'"}
+"pyarrow" = {version="==16.0.0"}
+"networkx" = {version="==3.3", markers="python_version>'3.8'"}
 "openpyxl" = {version="==3.1.2"}
-"pandas" = {version="==2.2.0", markers="python_version>'3.8'"}
-"pymongo" = {version="==4.6.1", extras=["srv"]}
-"sqlalchemy" = {version="==2.0.25"}
+"pandas" = {version="==2.2.2", markers="python_version>'3.8'"}
+"pymongo" = {version="==4.6.3", extras=["srv"]}
+"sqlalchemy" = {version="==2.0.29"}
 "toml" = {version="==0.10.2"}
-"boto3" = {version="==1.34.25"}
+"boto3" = {version="==1.34.88"}
 "backports.zoneinfo" = {version="==0.2.1", markers="python_version<'3.9'"}
-"cookiecutter" = {version="==2.5.0"}
-"flask" = {version="==3.0.1"}
+"cookiecutter" = {version="==2.6.0"}
+"flask" = {version="==3.0.3"}
 "flask-cors" = {version="==4.0.0"}
 "flask-socketio" = {version="==5.3.6"}
-"markdown" = {version="==3.5.2"}
+"markdown" = {version="==3.6"}
 "python-dotenv" = {version="==1.0.1"}
-"pytz" = {version="==2023.3.post1"}
+"pytz" = {version="==2024.1"}
 "tzlocal" = {version="==5.2"}
-"gevent" = {version="==23.9.1"}
+"gevent" = {version="==24.2.1"}
 "gevent-websocket" = {version="==0.10.1"}
 "kthread" = {version="==0.2.3"}
 "gitignore-parser" = {version="==0.1.11"}
 "simple-websocket" = {version="==1.0.0"}
-"twisted" = {version="==23.10.0"}
-"deepdiff" = {version="==6.7.1"}
+"twisted" = {version="==24.3.0"}
+"deepdiff" = {version="==7.0.1"}
 "flask-restful" = {version="==0.3.10"}
 "passlib" = {version="==1.7.4"}
-"marshmallow" = {version="==3.20.2"}
-"apispec" = {version="==6.4.0", extras=["yaml"]}
-"apispec-webframeworks" = {version="==1.0.0"}
+"marshmallow" = {version="==3.21.1"}
+"apispec" = {version="==6.6.0", extras=["yaml"]}
+"apispec-webframeworks" = {version="==1.1.0"}
 "watchdog" = {version="==4.0.0"}
 "charset-normalizer" = {version="==3.3.2"}

+ 16 - 16
tools/packages/pipfiles/Pipfile3.11.max

@@ -50,34 +50,34 @@ version = "==4.2.13"
 
 
 [packages]
-"pyarrow" = {version="==15.0.0"}
-"networkx" = {version="==3.2.1", markers="python_version>'3.8'"}
+"pyarrow" = {version="==16.0.0"}
+"networkx" = {version="==3.3", markers="python_version>'3.8'"}
 "openpyxl" = {version="==3.1.2"}
-"pandas" = {version="==2.2.0", markers="python_version>'3.8'"}
-"pymongo" = {version="==4.6.1", extras=["srv"]}
-"sqlalchemy" = {version="==2.0.25"}
+"pandas" = {version="==2.2.2", markers="python_version>'3.8'"}
+"pymongo" = {version="==4.6.3", extras=["srv"]}
+"sqlalchemy" = {version="==2.0.29"}
 "toml" = {version="==0.10.2"}
-"boto3" = {version="==1.34.25"}
+"boto3" = {version="==1.34.88"}
 "backports.zoneinfo" = {version="==0.2.1", markers="python_version<'3.9'"}
-"cookiecutter" = {version="==2.5.0"}
-"flask" = {version="==3.0.1"}
+"cookiecutter" = {version="==2.6.0"}
+"flask" = {version="==3.0.3"}
 "flask-cors" = {version="==4.0.0"}
 "flask-socketio" = {version="==5.3.6"}
-"markdown" = {version="==3.5.2"}
+"markdown" = {version="==3.6"}
 "python-dotenv" = {version="==1.0.1"}
-"pytz" = {version="==2023.3.post1"}
+"pytz" = {version="==2024.1"}
 "tzlocal" = {version="==5.2"}
-"gevent" = {version="==23.9.1"}
+"gevent" = {version="==24.2.1"}
 "gevent-websocket" = {version="==0.10.1"}
 "kthread" = {version="==0.2.3"}
 "gitignore-parser" = {version="==0.1.11"}
 "simple-websocket" = {version="==1.0.0"}
-"twisted" = {version="==23.10.0"}
-"deepdiff" = {version="==6.7.1"}
+"twisted" = {version="==24.3.0"}
+"deepdiff" = {version="==7.0.1"}
 "flask-restful" = {version="==0.3.10"}
 "passlib" = {version="==1.7.4"}
-"marshmallow" = {version="==3.20.2"}
-"apispec" = {version="==6.4.0", extras=["yaml"]}
-"apispec-webframeworks" = {version="==1.0.0"}
+"marshmallow" = {version="==3.21.1"}
+"apispec" = {version="==6.6.0", extras=["yaml"]}
+"apispec-webframeworks" = {version="==1.1.0"}
 "watchdog" = {version="==4.0.0"}
 "charset-normalizer" = {version="==3.3.2"}

+ 16 - 16
tools/packages/pipfiles/Pipfile3.12.max

@@ -50,34 +50,34 @@ version = "==4.2.13"
 
 
 [packages]
-"pyarrow" = {version="==15.0.0"}
-"networkx" = {version="==3.2.1", markers="python_version>'3.8'"}
+"pyarrow" = {version="==16.0.0"}
+"networkx" = {version="==3.3", markers="python_version>'3.8'"}
 "openpyxl" = {version="==3.1.2"}
-"pandas" = {version="==2.2.0", markers="python_version>'3.8'"}
-"pymongo" = {version="==4.6.1", extras=["srv"]}
-"sqlalchemy" = {version="==2.0.25"}
+"pandas" = {version="==2.2.2", markers="python_version>'3.8'"}
+"pymongo" = {version="==4.6.3", extras=["srv"]}
+"sqlalchemy" = {version="==2.0.29"}
 "toml" = {version="==0.10.2"}
-"boto3" = {version="==1.34.34"}
+"boto3" = {version="==1.34.88"}
 "backports.zoneinfo" = {version="==0.2.1", markers="python_version<'3.9'"}
-"cookiecutter" = {version="==2.5.0"}
-"flask" = {version="==3.0.2"}
+"cookiecutter" = {version="==2.6.0"}
+"flask" = {version="==3.0.3"}
 "flask-cors" = {version="==4.0.0"}
 "flask-socketio" = {version="==5.3.6"}
-"markdown" = {version="==3.5.2"}
+"markdown" = {version="==3.6"}
 "python-dotenv" = {version="==1.0.1"}
-"pytz" = {version="==2023.3.post1"}
+"pytz" = {version="==2024.1"}
 "tzlocal" = {version="==5.2"}
-"gevent" = {version="==23.9.1"}
+"gevent" = {version="==24.2.1"}
 "gevent-websocket" = {version="==0.10.1"}
 "kthread" = {version="==0.2.3"}
 "gitignore-parser" = {version="==0.1.11"}
 "simple-websocket" = {version="==1.0.0"}
-"twisted" = {version="==23.10.0"}
-"deepdiff" = {version="==6.7.1"}
+"twisted" = {version="==24.3.0"}
+"deepdiff" = {version="==7.0.1"}
 "flask-restful" = {version="==0.3.10"}
 "passlib" = {version="==1.7.4"}
-"marshmallow" = {version="==3.20.2"}
-"apispec" = {version="==6.4.0", extras=["yaml"]}
-"apispec-webframeworks" = {version="==1.0.0"}
+"marshmallow" = {version="==3.21.1"}
+"apispec" = {version="==6.6.0", extras=["yaml"]}
+"apispec-webframeworks" = {version="==1.1.0"}
 "watchdog" = {version="==4.0.0"}
 "charset-normalizer" = {version="==3.3.2"}

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác