From 33e6b03bc7db04f5f65500bd99e92f5d2bb6cf75 Mon Sep 17 00:00:00 2001 From: Taras Hlukhovetskyi Date: Wed, 25 Mar 2026 15:17:51 +0200 Subject: [PATCH 1/2] =?UTF-8?q?[FEAT]=20IGZ4=20Module=20Federation=20integ?= =?UTF-8?q?ration=20=E2=80=94=20merge=20feature/ig4=20into=20development?= =?UTF-8?q?=20baseline?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Squashed all commits from feature/ig4 branch ahead of development. Includes: - Module Federation build setup (vite, Dockerfile, nginx) for IGZ4 remote entry - IGZ4 auth flow: Bearer token via window.__mlrunHostServices, attachHostAuth interceptors - IGZ4 Iguazio platform API (v1/authorization, v1/profile, v1/authentication endpoints) - New project membership API in ProjectSettings/MembersPopUp/ChangeOwnerPopUp - API Token field replacing Access Key in batch run (IGZ4) - Default artifact path fix for IGZ4 (no v3io fallback) - RemoteNuclio route wrapper and real-time-functions navigation for IGZ4 - loadRemoteConfig.js for host-injected vs file-based config loading - Various bug fixes (log streaming, scrolling, breadcrumbs, empty fields) - IS_FEDERATION_MODE utility constant in src/utils/helper.js --- .env.production | 1 + .gitignore | 2 + Dockerfile | 58 +- README.md | 15 + config.json.tmpl | 3 +- config/loadDevProxyConfig.js | 57 + diff_to_dev.md | 1031 +++++++++++++ eslint.config.mjs | 16 +- eslint.mlrun-globals.mjs | 9 + nginx/nginx.conf.tmpl | 73 +- nginx/run_nginx | 24 +- package-lock.json | 1311 +++++++++++++++-- package.json | 7 +- public/config.json | 3 +- public/landing.html | 59 + scripts/previewLocalBuildMF.mjs | 9 + src/App.jsx | 8 +- src/api/jobs-api.js | 17 +- src/api/projects-iguazio-api.js | 59 +- src/common/ActionsMenu/ActionsMenu.jsx | 0 src/common/Breadcrumbs/breadcrumbs.util.js | 13 +- src/common/Download/Download.jsx | 4 +- src/components/Datasets/datasets.util.jsx | 2 +- src/components/Details/details.util.js | 5 +- src/components/JobWizard/JobWizard.jsx | 5 - src/components/JobWizard/JobWizard.util.js | 27 +- .../JobWizardAdvanced/JobWizardAdvanced.jsx | 30 +- .../JobWizardAdvanced/jobWizardAdvanced.scss | 5 +- src/components/Jobs/jobs.util.js | 6 +- src/components/LLMPrompts/llmPrompts.util.jsx | 2 +- .../monitoringApplications.util.js | 4 +- .../ProjectOverview/ProjectOverview.util.jsx | 4 +- src/components/Project/project.utils.jsx | 4 +- .../ProjectSettings/ProjectSettings.jsx | 214 ++- .../ProjectSettings/projectSettings.util.jsx | 96 +- src/components/ProjectsPage/projects.util.jsx | 2 +- .../RemoteNuclio/NuclioRemoteError.jsx | 36 + src/components/RemoteNuclio/RemoteNuclio.scss | 58 + .../RemoteNuclio/RemoteNuclioRouteWrapper.jsx | 107 ++ src/components/Workflow/workflow.util.js | 28 +- src/constants.js | 2 + .../breadcrumbsDropdown.scss | 2 +- .../ChangeOwnerPopUp/ChangeOwnerPopUp.jsx | 48 +- src/elements/MembersPopUp/MembersPopUp.jsx | 191 +-- src/elements/MembersPopUp/membersReducer.js | 19 +- .../ProjectFunctions/ProjectFunctions.jsx | 8 +- src/hooks/nuclioMode.hook.js | 6 +- src/httpClient.js | 60 +- src/index.jsx | 41 +- src/layout/Navbar/navbar.util.jsx | 2 +- src/loadRemoteConfig.js | 60 + src/main.jsx | 16 + src/reducers/jobReducer.js | 5 - src/utils/createApplicationContent.jsx | 2 +- src/utils/createConsumerGroupsContent.js | 2 +- src/utils/createRealTimePipelinesContent.js | 2 +- src/utils/getArtifactPreview.jsx | 2 +- src/utils/getJobLogs.util.js | 2 +- src/utils/nuclio.remotes.utils.js | 74 + src/utils/parseUri.js | 8 +- src/utils/projectAuth.util.js | 41 + vite.config.mjs | 59 +- 62 files changed, 3244 insertions(+), 822 deletions(-) create mode 100644 config/loadDevProxyConfig.js create mode 100644 diff_to_dev.md create mode 100644 eslint.mlrun-globals.mjs create mode 100644 public/landing.html create mode 100644 scripts/previewLocalBuildMF.mjs create mode 100644 src/common/ActionsMenu/ActionsMenu.jsx create mode 100644 src/components/RemoteNuclio/NuclioRemoteError.jsx create mode 100644 src/components/RemoteNuclio/RemoteNuclio.scss create mode 100644 src/components/RemoteNuclio/RemoteNuclioRouteWrapper.jsx create mode 100644 src/loadRemoteConfig.js create mode 100644 src/main.jsx create mode 100644 src/utils/nuclio.remotes.utils.js create mode 100644 src/utils/projectAuth.util.js diff --git a/.env.production b/.env.production index 3552083afc..97e12cb9b1 100644 --- a/.env.production +++ b/.env.production @@ -1,4 +1,5 @@ VITE_PUBLIC_URL=/mlrun +VITE_FEDERATION=false VITE_MLRUN_API_URL=${MLRUN_API_PROXY_URL} VITE_MLRUN_V3IO_ACCESS_KEY=${MLRUN_V3IO_ACCESS_KEY} VITE_IGUAZIO_API_URL= diff --git a/.gitignore b/.gitignore index a12895fef4..48a178568b 100755 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,8 @@ # production /build +.__mf__temp/ +*.tar # misc .DS_Store diff --git a/Dockerfile b/Dockerfile index 428b7a5b7d..2d20731ac8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,12 +13,7 @@ # limitations under the License. # # build stage -# node:20.18.2-alpine used as 20-alpine -FROM quay.io/mlrun/node:20-alpine as build-stage - -RUN apk update && \ - apk upgrade && \ - rm -rf /var/cache/apk/* +FROM quay.io/mlrun/node:20.19.2-slim AS build-stage WORKDIR /app @@ -26,47 +21,54 @@ COPY package*.json ./ RUN npm install COPY . . + +# build arg +ARG IS_MF=false + +RUN echo ">>> IS_MF ARG = $IS_MF" && \ + sed -i "/^VITE_FEDERATION=/d" .env.production && \ + echo "VITE_FEDERATION=$IS_MF" >> .env.production && \ + sed -i "s|^VITE_PUBLIC_URL=/mlrun|VITE_PUBLIC_URL=|" .env.production && \ + echo ">>> Final .env.production:" && grep '^VITE_' .env.production + RUN npm run build ARG COMMIT_HASH ARG DATE -RUN echo ${COMMIT_HASH} > ./build/COMMIT_HASH && \ - echo ${DATE} > ./build/BUILD_DATE +RUN echo "${COMMIT_HASH}" > ./build/COMMIT_HASH && \ + echo "${DATE}" > ./build/BUILD_DATE # production stage -FROM gcr.io/iguazio/nginx-unprivileged:1.21-alpine as production-stage +FROM gcr.io/iguazio/nginx-unprivileged:1.21-alpine AS production-stage -# align UID & GID with nginx-unprivileged image UID & GID ARG UID=101 ARG GID=101 +ARG IS_MF=false USER root -# escalate permissions to update packages -RUN apk update --no-cache && apk upgrade --no-cache -# we are inheriting $UID and $GID from the base image, you can find more information here: -# https://github.com/nginxinc/docker-nginx-unprivileged/blob/main/Dockerfile-alpine.template +RUN apk update --no-cache && apk upgrade --no-cache \ + && rm -f /etc/nginx/conf.d/default.conf + USER $UID COPY --from=build-stage /app/build /usr/share/nginx/html -COPY config.json.tmpl /usr/share/nginx/html/ -RUN rm /etc/nginx/conf.d/default.conf +COPY --from=build-stage /app/.env.production /usr/share/nginx/html/ + COPY nginx/nginx.conf.tmpl /etc/nginx/conf.d/ COPY nginx/run_nginx /etc/nginx/ USER root -# update build files permissions so they would be accessible to the running user -RUN chown -R $UID:0 /usr/share/nginx/html && chmod -R g+w /usr/share/nginx/html && chmod 777 /etc/nginx/run_nginx +RUN if [ "$IS_MF" \ + = "true" ]; then \ + INDEX=/usr/share/nginx/html/index.html; \ + [ -f "$INDEX" ] && sed -i 's| /etc/nginx/resolvers.conf && /etc/nginx/run_nginx +CMD ["/etc/nginx/run_nginx"] diff --git a/README.md b/README.md index c95d1d30fe..cfdece970d 100755 --- a/README.md +++ b/README.md @@ -21,6 +21,15 @@ Examples: | `npm run docker` | `mlrun/mlrun-ui:latest` | | `MLRUN_DOCKER_REGISTRY=quay.io/ MLRUN_DOCKER_REPO=iguazio MLRUN_DOCKER_TAG=0.4.9 npm run docker` | `quay.io/iguazio/mlrun-ui:0.4.9` | +### Docker build argument + +The Docker build supports an optional argument: + +`--build-arg IS_MF=${npm_config_IS_MF:-false}` + +By default, `IS_MF` is `false`, and the image is built as a standard **mlrun-ui** build.
+When set to `true`, the image is built in **Module Federation** mode. + ### `docker run` environment variables The Docker container runs a Nginx server, which listens on exposed port number 8090, serves the web-app, and proxies to the backend API. @@ -36,6 +45,7 @@ You can pass the following environment variables to the `docker run` command to | `MLRUN_V3IO_ACCESS_KEY` | Sets the V3IO access key to use for accessing V3IO containers
Example: `a7097c94-6e8f-436d-9717-a84abe2861d1` | | `MLRUN_FUNCTION_CATALOG_URL` | Sets the base URL of the function-template catalog
Default: `https://raw.githubusercontent.com` | | `MLRUN_FUNCTION_CATALOG_PATH` | Sets the base URI of the function-template catalog
Default: `/mlrun/functions/master` | +| `MLRUN_IGZ_UI_ALLOWED_ORIGIN` | Allowed origin for Module Federation and CORS
Example: `https://igz-ui.pini.vmdev90ig4.lab.iguazeng.com` | Example: @@ -71,6 +81,11 @@ This command is run by the Dockerfile that is used by the command [`npm run dock Note: `npm install` should be run first. +### `npm run preview:federation` + +Builds and serves the **mlrun-ui** application in **Module Federation** mode at `http://localhost:5179/`.
+Use this command when developing locally with **igz-ui**, allowing **mlrun-ui** to be consumed as a remote module. + ## Development ### Environment variables diff --git a/config.json.tmpl b/config.json.tmpl index 4784356dd6..3bde3bc9d7 100644 --- a/config.json.tmpl +++ b/config.json.tmpl @@ -1,5 +1,6 @@ { "betaMode": "${MLRUN_BETA_MODE}", "nuclioMode": "${MLRUN_NUCLIO_MODE}", - "nuclioUiUrl": "${MLRUN_NUCLIO_UI_URL}" + "nuclioUiUrl": "${MLRUN_NUCLIO_UI_URL}", + "nuclioRemoteEntryUrl": "${MLRUN_NUCLIO_REMOTE_ENTRY_URL}" } diff --git a/config/loadDevProxyConfig.js b/config/loadDevProxyConfig.js new file mode 100644 index 0000000000..4208bc3239 --- /dev/null +++ b/config/loadDevProxyConfig.js @@ -0,0 +1,57 @@ +/* +Copyright 2019 Iguazio Systems Ltd. + +Licensed under the Apache License, Version 2.0 (the "License") with +an addition restriction as set forth herein. 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. + +In addition, you may not use the software for any purposes that are +illegal under applicable law, and the grant of the foregoing license +under the Apache 2.0 license is conditioned upon your compliance with +such restriction. +*/ +/** + * Dynamically loads the Mlrun proxy configuration for development. + * + * Returns an empty proxy in production. + * In development, it imports the shared `mlrunProxyConfig` function + * from the `iguazio.dashboard-react-controls` package and validates its export. + * + * Node cannot use the aliased import + * (`import { mlrunProxyConfig } from 'igz-controls/utils/proxyServerConfig.util'`) + * since Vite path aliases are resolved only at build time, + * not during Node's module execution. + */ +import path from 'path' +import { pathToFileURL } from 'url' + +export const loadMlrunProxyConfig = async mode => { + const modulePath = path.resolve( + 'node_modules/iguazio.dashboard-react-controls/dist/utils/proxyServerConfig.util.mjs' + ) + + try { + const moduleUrl = pathToFileURL(modulePath).href + + const { mlrunProxyConfig } = await import(moduleUrl) + + if (typeof mlrunProxyConfig !== 'function') { + throw new Error('Invalid export: expected mlrunProxyConfig to be a function.') + } + + return mlrunProxyConfig + } catch (error) { + if (process.env.PREVIEW_MODE === 'true' || mode === 'development') { + throw new Error(`Failed to load mlrunProxyConfig: ${error.message}`) + } + + return () => ({}) + } +} diff --git a/diff_to_dev.md b/diff_to_dev.md new file mode 100644 index 0000000000..c1e994269d --- /dev/null +++ b/diff_to_dev.md @@ -0,0 +1,1031 @@ +# feature/ig4 vs development — Deep Comparison & Merge Plan +# UPDATED 3/20/2045 +# NOTE it may contains some redundant changes such as after many merges comparing diffs are not accurate in git +**103 files changed | +2,921 / -1,885 lines** + +The app in `feature/ig4` runs as a **Module Federation remote** consumed by the Iguazio Dashboard (IGZ4). +The `development` branch is the **standalone app** for IGZ3. + +The user clarified: **MLRun API is unchanged**. Only the **Iguazio platform API** changed for IGZ4. + +--- + +## SECTION 1 — Complete Change Classification + +### ✅ Safe to merge as-is (no conditions needed) + +| File | What changes | Why safe | +|------|-------------|---------| +| [`src/loadRemoteConfig.js`](#2-config-loading) | New file — loads config from host or file | Has IGZ3 fallback built in | +| [`src/index.jsx`](#2-config-loading) | Uses `loadRemoteConfig()` | `loadRemoteConfig()` handles both paths | +| [`src/main.jsx`](#2-config-loading) | New MF entry point | Additive, only used when imported as remote | +| [`src/components/RemoteNuclio/*`](#8-nuclio-navigation) | New MF Nuclio wrapper | Additive, only routed in IGZ4 | +| [`src/utils/nuclio.remotes.utils.js`](#13-module-federation-build) | MF remote loader utils | Additive | +| [`src/utils/getNuclioFuncState.js`](#9-real-time-pipelines) | Nuclio state mapping | Additive util | +| [`public/landing.html`](#10-builddeploy-infrastructure) | MF landing page | Only served in IGZ4 nginx | +| [`config/loadDevProxyConfig.js`](#13-module-federation-build) | Dev proxy via DRC | Only affects dev server | +| [`scripts/previewLocalBuildMF.mjs`](#13-module-federation-build) | MF preview script | Dev tooling | +| [`eslint.mlrun-globals.mjs`](#14-bug-fixes) | ESLint config | Tooling | +| [`src/constants.js`](#2-config-loading) | `FORCE_REFRESH`, `API_TOKEN_TIP` | Additive constants | +| [`config.json.tmpl`](#10-builddeploy-infrastructure) | Added `nuclioRemoteEntryUrl` field | Additive; env var empty in IGZ3 | +| [`.env.production`](#10-builddeploy-infrastructure) | Added `VITE_FEDERATION=false` | Safe default | +| [`src/hooks/nuclioMode.hook.js`](#14-bug-fixes) | Optional chaining on `window?.mlrunConfig` | Defensive improvement | +| [`src/utils/getState.js`](#14-bug-fixes) | Added `standby`, `scaledToZero`, `initialized` | Additive states | +| [`src/api/jobs-api.js`](#14-bug-fixes) | Stream via axios adapter | Bug fix, works in both | +| [`src/utils/getJobLogs.util.js`](#14-bug-fixes) | `res.body` → `res.data` | Pairs with jobs-api.js | +| [`src/reducers/artifactsReducer.js`](#14-bug-fixes) | Error message callback | Bug fix | +| [`src/reducers/functionReducer.js`](#14-bug-fixes) | `return thunkAPI.rejectWithValue` | Bug fix | +| [`src/components/Datasets/datasets.util.jsx`](#14-bug-fixes) | Optional chaining `artifact_limits?.max_download_size` | Bug fix | +| [`src/common/Download/Download.jsx`](#14-bug-fixes) | Optional chaining | Bug fix | +| [`src/utils/getArtifactPreview.jsx`](#14-bug-fixes) | Optional chaining | Bug fix | +| [`src/components/FunctionsPage/Functions.jsx`](#14-bug-fixes) | `!isEmpty(selectedFunction)` guard | Bug fix | +| [`src/common/DatePicker/DatePicker.jsx`](#14-bug-fixes) | `dateTo` null safety | Bug fix | +| [`src/common/ReactFlow/mlReactFlow.util.js`](#14-bug-fixes) | Rename + margin 16→34 | UI improvement | +| [`src/utils/getNoDataMessage.js`](#14-bug-fixes) | Added filter, removed trailing periods | Minor | +| [`src/elements/PanelCredentialsAccessKey/PanelCredentialsAccessKey.jsx`](#7-job-wizard-access-key--api-token) | CE version guard | Already conditional on `!frontendSpec.ce?.version` | +| [`src/elements/SectionTable/SectionTable.jsx`](#8-nuclio-navigation) | Removed `params` prop; name-slicing moved upstream | Logic relocated to `ProjectFunctions.jsx`, behaviour unchanged | +| [`src/hooks/usePagination.hook.js`](#14-bug-fixes) | `FORCE_REFRESH` URL param support | Forces re-fetch without changing bePage; IGZ3-safe | +| [`src/utils/generateTemplatesCategories.js`](#14-bug-fixes) | Category name mapping + case-insensitive deduplication | Pure fix, no API dependency | +| [`src/components/ModelsPage/RealTimePipelines/RealTimePipelinesCounters.jsx`](#9-real-time-pipelines) | Tooltip wrappers on stats cards | UI improvement | +| [`src/components/Details/DetailsTabsContent/DetailsTabsContent.jsx`](#14-bug-fixes) | `handleCancel` prop forwarded; `refresh` on ModelEndpoints | Minor prop additions | +| [`src/elements/DetailsInfoItem/DetailsInfoItem.jsx`](#9-real-time-pipelines) | `linkIsExternal` flag; status icon inline; `listOfFunctions` renderer | Additive rendering modes | +| [`src/components/DetailsPipeline/DetailsPipeline.jsx`](#14-bug-fixes) | Loading from `artifactsStore`; error-step fix | Bug fix | +| [`src/reducers/jobReducer.js`](#7-job-wizard-access-key--api-token) | Removed `function.metadata.credentials.access_key` from initial state | Pairs with access-key removal | +| [`src/components/JobWizard/JobWizard.jsx`](#7-job-wizard-access-key--api-token) | Removed `credentials` from `editJob` dispatch | Pairs with access-key removal | +| [`src/elements/JobsTable/JobsTable.jsx`](#14-bug-fixes) | `!isEmpty(selectedJob)` guard on `isDetailsTabExists` | Bug fix | +| [`src/elements/WorkflowsTable/WorkflowsTable.jsx`](#14-bug-fixes) | `!isEmpty(selectedJob)` guard on `isDetailsTabExists` | Bug fix | +| [`src/components/FunctionsPageOld/FunctionsOld.jsx`](#14-bug-fixes) | `!isEmpty(selectedFunction)` guard | Bug fix | +| [`src/components/FeatureStore/FeatureSets/FeatureSets.jsx`](#14-bug-fixes) | `!isEmpty(selectedFeatureSet)` guard | Bug fix | +| [`src/components/FeatureStore/FeatureVectors/FeatureVectors.jsx`](#14-bug-fixes) | `!isEmpty(selectedFeatureVector)` guard | Bug fix | +| [`src/components/DetailsInputs/DetailsInputs.jsx`](#14-bug-fixes) | Recursive nested-input handling via `getInputsContent` callback | Bug fix — handles object-typed inputs | +| [`vite.config.mjs`](#13-module-federation-build) | MF plugin + proxy config refactor | MF plugin only enabled when `VITE_FEDERATION=true` | + +### ⚠️ Needs conditional logic (`VITE_FEDERATION` / `IS_MF`) + +| File | What to make conditional | Mechanism | +|------|--------------------------|-----------| +| [`src/httpClient.js`](#1-http-client) | `iguazioHttpClient` baseURL (`/api` vs `/igz/api`) | `VITE_FEDERATION` build-time env | +| [`src/api/projects-iguazio-api.js`](#3-iguazio-platform-api) | Entire API — IGZ3 vs IGZ4 endpoints | Export based on `VITE_FEDERATION` | +| [`src/elements/MembersPopUp/membersReducer.js`](#4-members-state) | Restore `users`/`userGroups` state for IGZ3 | Keep both, or conditional | +| [`src/components/ProjectSettings/ProjectSettings.jsx`](#5-project-settings) | Data-fetch strategy, job polling, visibility checks | `VITE_FEDERATION` branch | +| [`src/components/ProjectSettings/projectSettings.util.jsx`](#5-project-settings) | `generateMembers()` and `isProjectMembersTabShown()` — IGZ4 rewrite | `VITE_FEDERATION` branch | +| [`src/elements/MembersPopUp/MembersPopUp.jsx`](#6-members-pop-up) | User search API + response parsing + apply changes | `VITE_FEDERATION` branch | +| [`src/elements/ChangeOwnerPopUp/ChangeOwnerPopUp.jsx`](#11-change-owner-pop-up) | Owner search API + apply changes | `VITE_FEDERATION` branch | +| [`src/utils/projectAuth.util.js`](#12-project-authorization-utility) | IGZ4-only — needs IGZ3 fallback added | `VITE_FEDERATION` branch | +| [`src/components/Workflow/workflow.util.js`](#12-project-authorization-utility) | Permission check: two-call IGZ3 vs `checkProjectWriteAccess` IGZ4 | `VITE_FEDERATION` branch | +| [`src/App.jsx`](#8-nuclio-navigation) | RemoteNuclio routes (fail in IGZ3) | Only add when `VITE_FEDERATION=true` | +| [`src/common/Breadcrumbs/breadcrumbs.util.js`](#8-nuclio-navigation) | External vs internal Nuclio links | `VITE_FEDERATION` branch | +| [`src/elements/ProjectFunctions/ProjectFunctions.jsx`](#8-nuclio-navigation) | Nuclio link path `/functions/` vs `/real-time-functions/` | `VITE_FEDERATION` branch | +| [`src/components/Project/project.utils.jsx`](#8-nuclio-navigation) | "Create real-time function" link path + `window.top` navigation | `VITE_FEDERATION` branch | +| [`src/components/JobWizard/JobWizardSteps/JobWizardAdvanced/JobWizardAdvanced.jsx`](#7-job-wizard-access-key--api-token) | Access key UI vs API Token UI | `VITE_FEDERATION` | +| [`src/components/JobWizard/JobWizard.util.js`](#7-job-wizard-access-key--api-token) | `outputPath` fallback; request body shape | `VITE_FEDERATION` | +| [`src/components/Jobs/jobs.util.js`](#7-job-wizard-access-key--api-token) | Rerun job: credentials vs auth.token_name | `VITE_FEDERATION` | +| [`nginx/nginx.conf.tmpl`](#10-builddeploy-infrastructure) | Create `.mf.tmpl` version; keep IGZ3 original | `IS_MF` in `run_nginx` | +| [`nginx/run_nginx`](#10-builddeploy-infrastructure) | envsubst+resolver (IGZ3) vs cp (IGZ4) | `IS_MF` branch | +| [`Dockerfile`](#10-builddeploy-infrastructure) | Restore `COPY config.json.tmpl` for IGZ3 path | `IS_MF` | + +### ❓ Needs clarification before deciding + +| File | Question | +|------|----------| +| [`Dockerfile`](#10-builddeploy-infrastructure) | Base image `node:20.19.2-slim` — should it be updated to 22 or 24? | + +--- + +## SECTION 2 — What changed, why, and how to merge + +--- + +### 1. HTTP Client + +**File:** `src/httpClient.js` + +#### What changed +Two independent things were changed in the same file: + +**A. `iguazioHttpClient` base URL** (one line) +```diff +- baseURL: import.meta.env.MODE === 'production' ? '/api' : '/iguazio/api' ++ baseURL: import.meta.env.MODE === 'production' ? '/igz/api' : '/iguazio/api' +``` + +**B. `attachHostAuth()` added** (new function, ~50 lines) +```js +export const getHostAuth = () => window.__mlrunHostServices?.auth || window.__igzAuth || null + +const attachHostAuth = client => { + const auth = getHostAuth() + if (!auth) return // ← no-op when not in MF host → SAFE in IGZ3 + + client.interceptors.request.use(...) // adds Bearer token + client.interceptors.response.use(...) // handles 401 → refresh token → retry +} + +// Applied to ALL clients: +attachHostAuth(mainHttpClient) +attachHostAuth(mainHttpClientV2) +attachHostAuth(iguazioHttpClient) +// ... +``` + +#### Why +- **Base URL**: In IGZ4, nginx doesn't proxy `/api`. The Iguazio backend API is served under `/igz/api` by the host. +- **attachHostAuth**: In IGZ4, auth is token-based (Bearer), provided by the host via `window.__mlrunHostServices.auth`. The host injects this before mounting the remote app. + +#### How to merge +- **`attachHostAuth()`** — **SAFE TO MERGE AS-IS**. The `if (!auth) return` guard means it does nothing in IGZ3 where `window.__mlrunHostServices` is not set. +- **Base URL** — **NEEDS CONDITIONAL**: + ```js + baseURL: import.meta.env.MODE === 'production' + ? (import.meta.env.VITE_FEDERATION === 'true' ? '/igz/api' : '/api') + : '/iguazio/api' + ``` + +--- + +### 2. Config Loading + +**Files:** `src/index.jsx`, `src/loadRemoteConfig.js`, `src/constants.js`, `src/main.jsx` + +#### What changed +`index.jsx` replaced inline `fetch('/config.json')` + protocol normalization with a call to `loadRemoteConfig()`: +```diff +- fetch(`${VITE_PUBLIC_URL}/config.json`) +- .then(config => { window.mlrunConfig = ...; normalizeProtocol... }) +- .then(() => render()) ++ loadRemoteConfig().then(() => render()) +``` + +`loadRemoteConfig.js` (new file) implements: +1. If host has already injected `window.mlrunConfig` → use it (IGZ4 path) +2. Otherwise → fetch `config.json` from disk (IGZ3 path, same as before) +3. Normalizes `nuclioUiUrl` protocol in both cases +4. Stores host services at `window.__mlrunHostServices` + +`main.jsx` (new file) is the MF entry point — exposes the React tree for the host to mount. + +`src/constants.js` adds `FORCE_REFRESH` (used by usePagination) and `API_TOKEN_TIP`. + +#### Why +In IGZ4, the Iguazio Dashboard (host) injects `window.mlrunConfig` before mounting the remote app. The standalone fetch is only a fallback for IGZ3 / local dev. + +#### How to merge +**SAFE TO MERGE AS-IS.** `loadRemoteConfig.js` already handles both cases. The fallback to `fetch('/config.json')` preserves IGZ3 behavior exactly. + +--- + +### 3. Iguazio Platform API + +**File:** `src/api/projects-iguazio-api.js` + +#### What changed — complete rewrite + +| Purpose | IGZ3 endpoint (removed) | IGZ4 endpoint (added) | +|---------|------------------------|----------------------| +| Get project + owner | `GET /projects?filter[name]=X&include=owner` | _(replaced by policies endpoint)_ | +| Get project members | `GET /projects/{id}?include=project_authorization_roles.principal_users,...` | _(replaced by policies endpoint)_ | +| Member + owner visibility | `GET /projects/__name__/{name}/authorization?action=...` | _(removed — policies response contains access info)_ | +| Poll member update job | `GET /jobs/{jobId}` | _(removed — new API is synchronous)_ | +| Update members | `POST /async_transactions` (batch job → async, poll for completion) | `PUT /v1/authorization/projects/{name}/roles` (sync, immediate) | +| Get active user | `GET /self` | `GET /v1/authentication/self?format=full` | +| Search users | `GET /scrubbed_users?filter[username][$match-i]=^.*query.*$` | `GET /v1/profile/search-users-metadata?searchTerm=query` | +| Search groups | `GET /scrubbed_user_groups?filter[name][$match-i]=^.*query.*$` | `GET /v1/profile/search-groups-metadata?searchTerm=query` | +| Get project policies | _(not present)_ | `GET /v1/authorization/projects/{name}/policies` | +| Update owner | `PUT /projects/{id}` (via editProject) | `PUT /v1/authorization/projects/{name}/owner` | + +#### Why +IGZ4 has a completely new authorization service (`/v1/authorization/`, `/v1/profile/`, `/v1/authentication/`). The data model also changed: +- **IGZ3**: Members are UUID-based; roles are objects with `principal_users` / `principal_user_groups` UUID arrays; updates are async batch jobs +- **IGZ4**: Members are username-based; roles map to arrays of usernames; updates are synchronous + +#### How to merge +Keep both API implementations, switch on `VITE_FEDERATION`: + +```js +// src/api/projects-iguazio-api.js +const igz3Api = { + editProject: ..., + getProjectJob: ..., + getProjects: ..., + getProjectMembers: ..., + getProjectMembersVisibility: ..., + getProjectOwnerVisibility: ..., + getProjectWorkflowsUpdateAuthorization: ..., + updateProjectMembers: ..., + getScrubbedUsers: ..., + getScrubbedUserGroups: ..., + getActiveUser: () => iguazioHttpClient.get('/self') +} + +const igz4Api = { + updateProjectOwner: ..., + getProjectPolicies: ..., + setProjectMembership: ..., + searchUsersMetadata: ..., + searchGroupsMetadata: ..., + getActiveUser: () => iguazioHttpClient.get('/v1/authentication/self', { params: { format: 'full' } }) +} + +export default import.meta.env.VITE_FEDERATION === 'true' ? igz4Api : igz3Api +``` + +--- + +### 4. Members State + +**File:** `src/elements/MembersPopUp/membersReducer.js` + +#### What changed +```diff +- users: [], // list of user members from IGZ3 API response +- userGroups: [], // list of user-group members from IGZ3 API response ++ // (removed — IGZ4 policies endpoint returns everything in one call) +``` +Also removed actions: `SET_USERS`, `SET_USER_GROUPS` + +#### Why +In IGZ3, members were fetched in two steps: project_authorization_roles + separate user/group lists → stored in `users` / `userGroups`. In IGZ4, `getProjectPolicies()` returns everything at once — no separate user/group arrays needed. + +#### How to merge +**Restore both fields for IGZ3 compatibility.** The IGZ3 `generateMembers()` function (called from `fetchProjectMembers()`) dispatches `SET_USERS` and `SET_USER_GROUPS`. Removing these breaks IGZ3 silently. + +```js +// Restore in initialMembersState: +users: [], +userGroups: [], + +// Restore in membersActions: +SET_USERS: 'SET_USERS', +SET_USER_GROUPS: 'SET_USER_GROUPS' + +// Restore reducer cases for SET_USERS, SET_USER_GROUPS +``` + +Alternatively, keep the IGZ4 version but verify that no IGZ3 code path dispatches or reads these fields. + +--- + +### 5. Project Settings + +**Files:** `src/components/ProjectSettings/ProjectSettings.jsx`, `src/components/ProjectSettings/projectSettings.util.jsx` + +#### What changed — two files, same underlying concern + +**ProjectSettings.jsx — four independent concerns** + +**A. Owner identity model** +```diff +// IGZ3: compared by UUID +- return membersState?.activeUser?.data?.id === membersState?.projectInfo?.owner.id + +// IGZ4: compared by username ++ const activeUsername = membersState?.activeUser?.data?.attributes?.username ++ const ownerUsername = membersState?.projectInfo?.owner?.username ++ return Boolean(activeUsername && activeUsername === ownerUsername) +``` + +**B. System Admin check (new for IGZ4)** +```diff ++ const userIsSystemAdmin = useMemo( ++ () => membersState?.activeUser?.data?.attributes?.user_policies_collection?.has('System Admin') ?? false, ++ [...] ++ ) +``` +In IGZ4, `igz-system-admin` users can change project owners even if they're not the project owner. + +**C. Data-fetching strategy replaced** +```diff +// IGZ3: two-step fetch (project + members separately) + job polling +- fetchProjectIdAndOwner() // GET /projects?include=owner +- .then(({ id, owner }) => { +- fetchActiveUser() // GET /self +- fetchProjectMembersVisibility() // GET /projects/__name__/X/authorization +- fetchProjectOwnerVisibility() // GET /projects/__name__/X/authorization +- fetchProjectMembers(id, owner) // GET /projects/{id}?include=roles +- }) + +// IGZ4: single call ++ fetchActiveUser() // GET /v1/authentication/self?format=full ++ fetchProjectPolicies() // GET /v1/authorization/projects/{name}/policies +``` + +**D. After member update** +```diff +// IGZ3: async job polling +- changeMembersCallback = (jobId, userIsValid) => { +- const fetchJob = () => { +- getProjectJob(jobId).then(response => { +- if (response.data.data.attributes.state !== COMPLETED_STATE) { +- setTimeout(fetchJob, 1000) // poll every second +- } else { +- fetchProjectMembers(...) +- } +- }) +- } +- fetchJob() +- } + +// IGZ4: immediate refetch ++ changeMembersCallback = (userIsStillMember) => { ++ if (userIsStillMember) { ++ fetchProjectPolicies() ++ } else { ++ navigate('/projects/') ++ } ++ } +``` + +**projectSettings.util.jsx — `generateMembers()` and `isProjectMembersTabShown()` rewritten** + +`generateMembers()` in IGZ3 parsed a JSONAPI response with `included` arrays (roles, users, user_groups keyed by UUID). In IGZ4 it parses a policies response where members are identified by username: + +```diff +// IGZ3 +- export const generateMembers = (membersResponse, membersDispatch, owner) => { +- const { project_authorization_role, user, user_group } = groupBy(membersResponse.data.included, ...) +- // dispatch SET_USERS, SET_USER_GROUPS +- // match member UUIDs against user/group lists to build display names + +// IGZ4 ++ export const generateMembers = (policiesResponse, membersDispatch) => { ++ const policies = policiesResponse.data.items || [] ++ // dispatch SET_PROJECT_AUTHORIZATION_ROLES with policies ++ // dispatch SET_PROJECT_INFO with owner derived from OWNER_ROLE policy ++ // build members list directly from assignedMembers[].id (username) +``` + +`isProjectMembersTabShown()` now uses `username` and `user_group_names` Set instead of UUID-based relationships: +```diff +- member.id === activeUser.data?.id || +- activeUser.data?.relationships?.user_groups?.data?.some?.(group => group.id === member.id) ++ member.id === activeUsername || ++ activeUser.data?.attributes?.user_group_names?.has(member.id) +``` + +#### Why +The IGZ4 `/v1/authorization/projects/{name}/policies` endpoint returns all data in one call: active policies, members per role, and owner. No need for separate member/owner visibility checks. Member updates are synchronous (no job polling). The `igz-system-admin` role is a new IGZ4 concept. + +#### How to merge +Wrap the two data-fetching strategies and both utility functions in a conditional: + +```js +// ProjectSettings.jsx +const fetchProjectUsersData = useCallback(() => { + if (projectMembershipIsEnabled) { + if (import.meta.env.VITE_FEDERATION === 'true') { + // IGZ4 path + fetchActiveUser() + fetchProjectPolicies().catch(...) + } else { + // IGZ3 path + fetchProjectOwnerVisibility(params.projectName) + fetchProjectIdAndOwner() + .then(({ id, owner }) => { + fetchActiveUser() + fetchProjectMembersVisibility(params.projectName) + return fetchProjectMembers(id, owner) + }) + .catch(...) + } + } +}, [...]) +``` + +```js +// projectSettings.util.jsx +export const generateMembers = (response, membersDispatch, owner) => { + if (import.meta.env.VITE_FEDERATION === 'true') { + // IGZ4 path — parse policies response + } else { + // IGZ3 path — parse JSONAPI included arrays + } +} +``` + +The `userIsSystemAdmin` check and `changeMembersCallback` also need the same branching. + +--- + +### 6. Members Pop-Up + +**File:** `src/elements/MembersPopUp/MembersPopUp.jsx` + +#### What changed — three independent concerns + +**A. User/group search API and response shape** +```diff +// IGZ3 +- const getUsersPromise = projectsIguazioApi.getScrubbedUsers({ +- params: { 'filter[username]': '[$match-i]^.*query.*$', 'page[size]': 200 } +- }) +// Response: { data: { data: [{ id: 'uuid', type: 'user', attributes: { username, ... } }] } } + +// IGZ4 ++ const getUsersPromise = projectsIguazioApi.searchUsersMetadata(searchQuery) +// Response: { data: { items: [{ username: 'john', ... }] } } + +// IGZ3 groups +- const getUserGroupsPromise = projectsIguazioApi.getScrubbedUserGroups({ +- params: { 'filter[name]': '[$match-i]^.*query.*$', 'page[size]': 200 } +- }) +// Response: { data: { data: [{ id: 'uuid', type: 'user_group', attributes: { name: '/group/path' } }] } } + +// IGZ4 groups ++ const getUserGroupsPromise = projectsIguazioApi.searchGroupsMetadata(searchQuery) +// Response: { data: { items: [{ groupId: 'id', path: '/group/path' }] } } +``` + +**B. Member identity: UUID → username** +```diff +// IGZ3: id is UUID, label is username +- { id: identity.id, label: identity.attributes.username } // user +- { id: identity.id, label: identity.attributes.name } // group + +// IGZ4: id IS the username/groupId ++ { id: user.username, label: user.username } // user ++ { id: group.groupId, label: group.path.replace(/^\//, '') ?? group.groupId } // group +``` + +**C. Applying member changes: async batch → sync call** +```diff +// IGZ3: complex async_transactions batch +- const changesBody = { data: { attributes: { ... }, requests: modifiedRoles.map(...) } } +- projectsIguazioApi.updateProjectMembers(changesBody) +- .then(response => { +- changeMembersCallback(response.data.data.id, validMember || userIsProjectSecurityAdmin) +- }) + +// IGZ4: simple sync call ++ projectsIguazioApi.setProjectMembership(projectName, { membership, override: true }) ++ .then(() => { ++ const userIsStillMember = membersData.members?.some(member => ...) ++ changeMembersCallback(userIsStillMember) ++ }) +``` + +Also removed: `isIgzVersionCompatible` version check (was switching filter syntax). Removed: `Project Security Admin` bypass for self-removal redirect. + +#### Why +New IGZ4 APIs return different shapes. Identity is username-based not UUID-based. Member update is now synchronous and atomic (override replaces entire role membership). `Project Security Admin` no longer exists in IGZ4 auth model. + +#### How to merge +Wrap the three concerns in `VITE_FEDERATION` conditionals within `generateUsersSuggestionList()` and `applyMembersChanges()`. The rest of the component (UI rendering, state management) is the same. + +--- + +### 7. Job Wizard: Access Key → API Token + +**Files:** `src/components/JobWizard/JobWizardSteps/JobWizardAdvanced/JobWizardAdvanced.jsx`, `src/components/JobWizard/JobWizard.util.js`, `src/components/JobWizard/JobWizard.jsx`, `src/components/Jobs/jobs.util.js`, `src/elements/PanelCredentialsAccessKey/PanelCredentialsAccessKey.jsx`, `src/reducers/jobReducer.js` + +#### What changed + +**A. UI in `JobWizardAdvanced.jsx`** +```diff +// IGZ3: checkbox + optional text input +- +- {!formState.values[ADVANCED_STEP].accessKey && ( +- +- )} + +// IGZ4: single text input (only shown for non-CE) ++ {!frontendSpec.ce?.version && ( ++ ++ )} +``` + +**B. Default form data in `JobWizard.util.js`** +```diff +// IGZ3 defaults +- accessKey: true, +- accessKeyInput: '', +- outputPath: currentProject?.spec?.artifact_path +- || (frontendSpec.ce?.version && frontendSpec.default_artifact_path) +- || JOB_DEFAULT_OUTPUT_PATH + +// IGZ4 defaults ++ apiTokenInput: 'default', ++ outputPath: currentProject?.spec?.artifact_path || frontendSpec.default_artifact_path +``` + +**C. Job request body in `JobWizard.util.js`** +```diff +// IGZ3: access key in function metadata +- function: { +- metadata: { +- credentials: { access_key: formData.accessKey ? '$generate' : formData.accessKeyInput } +- } +- } + +// IGZ4: token name in task spec ++ task: { ++ spec: { ++ ...(formData.apiTokenInput && { auth: { token_name: formData.apiTokenInput } }), ++ ... ++ } ++ } +``` + +**D. `JobWizard.jsx` — edit job cleaned up** +```diff +- const credentials = jobRequestData.function?.metadata?.credentials +- delete jobRequestData.function.metadata + dispatch(editJob({ + postData: { +- credentials, + scheduled_object: jobRequestData, + ... + } + })) +``` + +**E. Rerun job in `Jobs/jobs.util.js`** +```diff +- function: { metadata: { credentials: { access_key: functionData?.metadata?.credentials?.access_key } } } ++ task: { spec: { ...(job.auth?.token_name && { auth: { token_name: job.auth.token_name } }), ... } } +``` + +**F. `jobReducer.js` — initial state** +```diff + function: { +- metadata: { +- credentials: { +- access_key: '' +- } +- }, + spec: { ... } + } +``` + +#### Why +IGZ4 uses named API tokens (stored in the platform) instead of v3io access keys. The job spec field changed from `function.metadata.credentials.access_key` to `task.spec.auth.token_name`. + +#### How to merge +The user says MLRun API is the same. The question is: **does IGZ3 MLRun support `task.spec.auth.token_name`?** +- If **yes**: can merge as-is. The `auth.token_name` only appears if the field is filled (`...formData.apiTokenInput && {...}`). +- If **no**: wrap the request body generation in `VITE_FEDERATION`: + ```js + function: import.meta.env.VITE_FEDERATION === 'true' ? {} : { + metadata: { credentials: { access_key: formData.accessKey ? '$generate' : formData.accessKeyInput } } + }, + task: { spec: { + ...(import.meta.env.VITE_FEDERATION === 'true' && formData.apiTokenInput + ? { auth: { token_name: formData.apiTokenInput } } + : {}), + ... + }} + ``` + +The `outputPath` change also differs: IGZ3 uses `JOB_DEFAULT_OUTPUT_PATH` as fallback (v3io path), IGZ4 uses `frontendSpec.default_artifact_path`. This is a **breaking change for IGZ3** if `default_artifact_path` is undefined or points to wrong location. Needs conditional: +```js +outputPath: currentProject?.spec?.artifact_path + || (import.meta.env.VITE_FEDERATION === 'true' + ? frontendSpec.default_artifact_path + : (frontendSpec.ce?.version && frontendSpec.default_artifact_path) || JOB_DEFAULT_OUTPUT_PATH) +``` + +--- + +### 8. Nuclio Navigation + +**Files:** `src/App.jsx`, `src/common/Breadcrumbs/breadcrumbs.util.js`, `src/layout/Navbar/navbar.util.jsx`, `src/utils/parseUri.js`, `src/utils/createRealTimePipelinesContent.js`, `src/components/Details/details.util.js`, `src/elements/ProjectFunctions/ProjectFunctions.jsx`, `src/elements/SectionTable/SectionTable.jsx`, `src/components/Project/project.utils.jsx`, `src/components/RemoteNuclio/*` + +#### What changed + +**A. `src/App.jsx` — new internal routes for Nuclio** +```diff ++ ++ } /> ++ } /> ++ } /> ++ } /> ++ +- } /> +``` + +**B. `src/common/Breadcrumbs/breadcrumbs.util.js` — external links removed** +```diff +// IGZ3: breadcrumbs had external links to Nuclio UI +- { label: 'Real-time functions', id: 'Real-time functions', +- link: generateNuclioLink(`/projects/${params.projectName}/functions`) } + +// IGZ4: breadcrumbs use internal route IDs (React Router handles navigation) ++ { label: 'Real-time functions', id: 'real-time-functions' } +``` + +**C. `src/layout/Navbar/navbar.util.jsx` — path updated** +```diff +- link: generateNuclioLink(`${pathname}/functions`) ++ link: generateNuclioLink(`${pathname}/real-time-functions`) +``` + +**D. `src/utils/parseUri.js` — `generateNuclioLink()` simplified** +```diff +// IGZ3: sets `?origin=...` param so Nuclio UI knows where mlrun UI is hosted +- const linkUrl = new URL(`${window.mlrunConfig.nuclioUiUrl}${pathname}`) +- if (window.location.origin !== window.mlrunConfig.nuclioUiUrl) { +- linkUrl.searchParams.set?.('origin', window.location.origin) +- } + +// IGZ4: simple URL construction ++ const base = window.mlrunConfig?.nuclioUiUrl || window.location.origin ++ return new URL(`${base}${cleanPath}`).toString() +``` + +**E. `src/elements/ProjectFunctions/ProjectFunctions.jsx` — links + name slicing** +```diff +// Nuclio function links: /functions/ → /real-time-functions/ +- href: generateNuclioLink(`/projects/${params.projectName}/functions/${func.metadata.name}`) ++ href: generateNuclioLink(`/projects/${params.projectName}/real-time-functions/${func.metadata.name}`) + +// MLRun-generated function names (prefixed with project name) are sliced: ++ value: has(func.metadata.labels || {}, 'mlrun/class') ++ ? func.metadata.name.slice(params.projectName.length + 1) ++ : func.metadata.name + +// Status via getNuclioFuncState() utility instead of inline logic +- value: func?.status?.state === FUNCTION_READY_STATE && !func?.spec?.disable ? 'Running' : ... ++ value: getNuclioFuncState(func) +``` + +**F. `src/elements/SectionTable/SectionTable.jsx` — `params` prop removed** + +The name-slicing logic (removing project prefix from function names) was previously done in `SectionTable.jsx` using the `params` prop. This was moved upstream to `ProjectFunctions.jsx`, so `SectionTable` no longer needs `params`: +```diff +- const SectionTable = ({ loading = false, params, table }) => { ++ const SectionTable = ({ loading = false, table }) => { + // No longer extracts project name prefix — value is already sliced in ProjectFunctions.jsx +``` + +**G. `src/components/Project/project.utils.jsx` — "Create real-time function" link** +```diff +// IGZ3: direct window.location.assign +- handler: () => window.location.assign(generateNuclioLink(`/projects/${params.projectName}/create-function`)) + +// IGZ4: path updated + window.top for MF iframe context ++ handler: () => { ++ const url = generateNuclioLink(`/projects/${params.projectName}/real-time-functions/create-function`) ++ if (window.top && window.top !== window.self) { ++ window.top.location.assign(url) ++ } else { ++ window.location.assign(url) ++ } ++ } +``` + +#### Why +In IGZ4, Nuclio is loaded as a remote MF app within the same React Router. So "Real-time functions" navigates to an internal route (`/projects/:name/real-time-functions/*`) which loads `RemoteNuclioRouteWrapper`. In IGZ3, they are external links to the standalone Nuclio UI. The `window.top` navigation is needed because the MF app runs inside an iframe provided by the host. + +#### How to merge + +**App.jsx routes**: These new routes are only useful in IGZ4. In IGZ3, navigating to `real-time-functions/*` would load `RemoteNuclioRouteWrapper` which tries to load the Nuclio MF remote — this will fail. Options: +- Wrap in `VITE_FEDERATION` check +- Or make `RemoteNuclioRouteWrapper` redirect to external Nuclio link when `VITE_FEDERATION !== 'true'` + +**Breadcrumbs**: Must restore external links for IGZ3: +```js +import.meta.env.VITE_FEDERATION === 'true' + ? { label: 'Real-time functions', id: 'real-time-functions' } + : { label: 'Real-time functions', id: 'Real-time functions', + link: generateNuclioLink(`/projects/${params.projectName}/functions`) } +``` + +**`ProjectFunctions.jsx` link path**: `/real-time-functions/` breaks IGZ3 if Nuclio UI doesn't recognize that path. Needs: +```js +href: generateNuclioLink( + `/projects/${params.projectName}/${import.meta.env.VITE_FEDERATION === 'true' ? 'real-time-functions' : 'functions'}/${func.metadata.name}` +) +``` + +**`project.utils.jsx`**: The `window.top` fallback is safe; the path change needs the same conditional as above. + +**`generateNuclioLink()`**: The removal of `origin` param might break IGZ3 if the Nuclio UI requires it to function correctly. Needs clarification: does IGZ3's Nuclio UI use `?origin` to redirect back? + +**`SectionTable.jsx`**: Safe to merge — the name-slicing responsibility was just moved upstream, behaviour is identical. + +--- + +### 9. Real-Time Pipelines + +**Files:** `src/components/ModelsPage/RealTimePipelines/RealTimePipelines.jsx`, `src/utils/createRealTimePipelinesContent.js`, `src/components/Details/details.util.js`, `src/elements/DetailsInfoItem/DetailsInfoItem.jsx`, `src/components/ModelsPage/RealTimePipelines/RealTimePipelinesCounters.jsx` + +#### How to merge +Accept all changes from development +--- + +### 10. Build/Deploy Infrastructure + +**Files:** `nginx/nginx.conf.tmpl`, `nginx/run_nginx`, `Dockerfile`, `.env.production` + +#### `nginx/nginx.conf.tmpl` — complete rewrite +| | IGZ3 (dev branch) | IGZ4 (feature/ig4) | +|---|---|---| +| Proxy rules | `/api`, `/nuclio`, `/function-catalog` with v3io header forwarding | **None** | +| DNS resolver | `include resolvers.conf` | None needed | +| v3io key map | `map $http_x_v3io_session_key ...` | Removed | +| Fallback | `/index.html` | `/landing.html` | +| CORS | None | `Access-Control-Allow-Origin: *` (required for cross-origin JS module loading) | +| Gzip | No | Yes | +| Cache-busting | No | `Cache-Control: no-cache` on `.js`/`.json` | + +**Merge plan**: Create `nginx/nginx.conf.mf.tmpl` (IGZ4 content, current file). Restore original content in `nginx/nginx.conf.tmpl` (IGZ3). Select in `run_nginx` via `IS_MF`. + +#### `nginx/run_nginx` — complete rewrite +| IGZ3 | IGZ4 | +|------|------| +| `envsubst` with 8 env vars into `nginx.conf.tmpl` | `cp` (no env vars — no proxy config) | +| `envsubst` config vars into `config.json.tmpl` | Removed — no `config.json.tmpl` | +| DNS: `echo resolver $(awk nameserver /etc/resolv.conf) > resolvers.conf` | Removed — no proxy needs DNS | +| `nginx -g 'daemon off;'` | `exec nginx -g 'daemon off;'` | + +**Merge plan**: Add `IS_MF` branch: +```sh +if [ "$IS_MF" = "true" ]; then + cp /etc/nginx/conf.d/nginx.conf.mf.tmpl /etc/nginx/conf.d/nginx.conf +else + echo resolver $(awk 'BEGIN{ORS=" "} $1=="nameserver" {print $2}' /etc/resolv.conf) ";" > /etc/nginx/resolvers.conf + envsubst '${MLRUN_API_PROXY_URL} ${MLRUN_V3IO_ACCESS_KEY} ...' \ + < /etc/nginx/conf.d/nginx.conf.tmpl > /etc/nginx/conf.d/nginx.conf + envsubst '${MLRUN_BETA_MODE} ${MLRUN_NUCLIO_MODE} ${MLRUN_NUCLIO_UI_URL}' \ + < /usr/share/nginx/html/config.json.tmpl > /usr/share/nginx/html/config.json +fi +exec nginx -g 'daemon off;' +``` + +#### `Dockerfile` +- `IS_MF=false` default already added ✅ +- Base image `20-alpine` → `20.19.2-slim`: coordinate with DevOps +- `CMD` simplified (DNS resolver setup moved to `run_nginx`) +- `COPY config.json.tmpl` removed — in IGZ3 it's still needed for runtime config generation + +**Merge plan**: `IS_MF=false` default makes the standard build safe. Restore `COPY config.json.tmpl` for IGZ3 path (inside `IS_MF` conditional in Dockerfile). The DNS resolver trick should remain in `run_nginx` for IGZ3. + +#### `.env.production` +```diff ++ VITE_FEDERATION=false +``` +**SAFE TO MERGE**. In IGZ4 Dockerfile, the `sed` command overwrites this with `true`. + +--- + +### 11. Change Owner Pop-Up + +**File:** `src/elements/ChangeOwnerPopUp/ChangeOwnerPopUp.jsx` + +#### What changed + +**A. Apply changes: JSONAPI body → `updateProjectOwner()`** +```diff +// IGZ3: builds full JSONAPI relationship body +- const projectData = { +- data: { type: 'project', attributes: {}, +- relationships: { owner: { data: { id: newOwnerId, type: USER_ROLE } } } +- } +- } +- projectsIguazioApi.editProject(projectId, projectData).then(changeOwnerCallback) + +// IGZ4: single direct call ++ projectsIguazioApi.updateProjectOwner(projectId, newOwnerId).then(changeOwnerCallback) +``` + +**B. User search: `getScrubbedUsers` → `searchUsersMetadata`** +```diff +// IGZ3: scrubbed_users endpoint with role filter + version-conditional search syntax +- if (isIgzVersionCompatible(requiredIgzVersion)) { +- params['filter[username]'] = `[$contains_istr]${memberName}` +- } +- const response = await projectsIguazioApi.getScrubbedUsers({ params }) +- const { data: { data: users } } = response +- formattedUsers = users.map(user => ({ +- name: `${user.attributes.first_name} ${user.attributes.last_name}`, +- username: user.attributes.username, +- id: user.id, // UUID +- })) + +// IGZ4: search-users-metadata endpoint, flat response ++ const response = await projectsIguazioApi.searchUsersMetadata(memberName) ++ const users = response.data.items || [] ++ formattedUsers = users.map(user => ({ ++ name: `${user.firstName} ${user.lastName}`, ++ username: user.username, ++ id: user.username, // username as ID ++ })) +``` + +Removed: `isIgzVersionCompatible` import, `USER_ROLE` import. + +#### Why +IGZ4 has a new user search endpoint with a different response shape. Owner updates use a dedicated endpoint. Identity is username-based. + +#### How to merge +Wrap both `applyChanges()` and `generateSuggestionList()` in `VITE_FEDERATION` conditionals: + +```js +const applyChanges = () => { + if (newOwnerId) { + const apiCall = import.meta.env.VITE_FEDERATION === 'true' + ? projectsIguazioApi.updateProjectOwner(projectId, newOwnerId) + : projectsIguazioApi.editProject(projectId, { data: { ... } }) + apiCall.then(changeOwnerCallback).catch(...).finally(handleOnClose) + } +} + +const generateSuggestionList = async (memberName, resolve) => { + if (import.meta.env.VITE_FEDERATION === 'true') { + const response = await projectsIguazioApi.searchUsersMetadata(memberName) + const users = response.data.items || [] + formattedUsers = users.map(user => ({ ..., id: user.username })) + } else { + const response = await projectsIguazioApi.getScrubbedUsers({ params: { ... } }) + const users = response.data.data + formattedUsers = users.map(user => ({ ..., id: user.id })) + } +} +``` + +--- + +### 12. Project Authorization Utility + +**Files:** `src/utils/projectAuth.util.js` (new), `src/components/Workflow/workflow.util.js` + +#### What changed + +**`projectAuth.util.js`** — new utility wrapping the IGZ4 policies endpoint: +```js +// IGZ4 only: checks if the active user has write access to a project +export const checkProjectWriteAccess = async projectName => { + // calls GET /v1/authorization/projects/{name}/policies + // returns true if active user is owner, admin, or security admin +} +``` + +**`workflow.util.js`** — replaced two-step IGZ3 permission check with single call: +```diff +// IGZ3: try owner visibility, fall back to workflows update authorization +- await projectsIguazioApi.getProjectOwnerVisibility(projectName) +- // catch → try getProjectWorkflowsUpdateAuthorization(projectName) + +// IGZ4: single call via new utility ++ const hasAccess = await checkProjectWriteAccess(projectName) +``` + +Both `fetchMissingProjectsPermissions()` and `fetchMissingProjectPermission()` were updated. + +#### Why +IGZ4's new auth model provides a single policies endpoint that contains all permission information. The old two-endpoint chain (`getProjectOwnerVisibility` + `getProjectWorkflowsUpdateAuthorization`) is gone. The new utility centralizes this logic. + +#### How to merge +`projectAuth.util.js` currently contains only IGZ4 logic. Two options: + +**Option A** — Add IGZ3 fallback inside `projectAuth.util.js`: +```js +export const checkProjectWriteAccess = async projectName => { + if (import.meta.env.VITE_FEDERATION === 'true') { + // IGZ4: GET /v1/authorization/projects/{name}/policies + } else { + // IGZ3: try getProjectOwnerVisibility, fall back to getProjectWorkflowsUpdateAuthorization + return projectsIguazioApi + .getProjectOwnerVisibility(projectName) + .then(() => true) + .catch(() => projectsIguazioApi + .getProjectWorkflowsUpdateAuthorization(projectName) + .then(() => true) + .catch(() => false) + ) + } +} +``` +This keeps `workflow.util.js` unchanged after merge. + +**Option B** — Branch in `workflow.util.js` directly, keeping `projectAuth.util.js` IGZ4-only. + +Option A is cleaner — it isolates the platform-specific logic in the auth utility. + +--- + +### 13. Module Federation Build + +**Files:** `vite.config.mjs`, `config/loadDevProxyConfig.js`, `src/utils/nuclio.remotes.utils.js`, `scripts/previewLocalBuildMF.mjs` + +#### What changed + +**`vite.config.mjs`** — three changes: +1. Added `@module-federation/vite` plugin, conditionally enabled: + ```js + const federationPlugin = env.VITE_FEDERATION === 'true' + ? federation({ filename: 'remoteEntry.js', name: 'mlrun', + exposes: { './loadRemoteConfig': './src/loadRemoteConfig.js', './app': './src/main.jsx' }, + shared: { react: { singleton: true }, 'react-dom': { singleton: true } } + }) + : null + ``` +2. Dev proxy config moved to `loadDevProxyConfig.js` (replaces hardcoded proxy object): + ```diff + - proxy: { + - '/api': env.VITE_MLRUN_API_URL ? { target: ..., headers: { 'x-v3io-session-key': ... } } : undefined, + - '/nuclio': ..., + - '/iguazio': ..., + - '/function-catalog': ... + - } + + proxy: { ...mlrunProxyConfig(env) } + ``` +3. Added `build.target: 'esnext'` (required for top-level `await` in MF entry points). + +**`config/loadDevProxyConfig.js`** — reads proxy configuration from a DRC (Dev Remote Config) file, allowing dynamic proxy targets without rebuilding. Falls back to env vars. + +**`src/utils/nuclio.remotes.utils.js`** — utilities for loading Nuclio as a Module Federation remote at runtime. + +#### Why +Module Federation requires the MF Vite plugin to expose `remoteEntry.js`. The `build.target: 'esnext'` is needed because MF entry points use top-level `await`. Proxy config was externalized to support the DRC (Dev Remote Config) tooling used in IGZ4 development. + +#### How to merge +**SAFE TO MERGE AS-IS.** The federation plugin is only instantiated when `VITE_FEDERATION=true`. When `false`, `federationPlugin` is `null` and Vite ignores it. The proxy config now comes from `loadDevProxyConfig.js` which falls back to env vars — same behavior as before for IGZ3 dev. + +--- + +### 14. Bug Fixes + +Various files with pure bug fixes and safe improvements that require no conditional logic. + +#### A. `isDetailsTabExists` guard — 5 components + +`src/elements/JobsTable/JobsTable.jsx`, `src/elements/WorkflowsTable/WorkflowsTable.jsx`, `src/components/FunctionsPageOld/FunctionsOld.jsx`, `src/components/FeatureStore/FeatureSets/FeatureSets.jsx`, `src/components/FeatureStore/FeatureVectors/FeatureVectors.jsx` + +All five had the same race condition: `isDetailsTabExists()` was called when a URL param was set but before the selected item was loaded, causing wrong tab redirects. Fix: added `!isEmpty(selectedXxx)` guard. + +```diff +- if (params.jobId && pageData.details.menu.length > 0) { ++ if (params.jobId && !isEmpty(selectedJob) && pageData.details.menu.length > 0) { + isDetailsTabExists(params.tab, pageData.details.menu, navigate, location) + } +``` + +#### B. `usePagination.hook.js` — FORCE_REFRESH support + +Added a `FORCE_REFRESH` URL search param that forces a content re-fetch even when the `BE_PAGE` number hasn't changed. The param is consumed and deleted after use (single-use trigger). Useful for explicit refresh actions after mutations. + +```js +// Forces re-fetch regardless of bePage +if (!bePage && !forceRefreshData.isForce) return +if (lastRequestedPageRef.current === bePage && !forceRefreshData.isForce) return +``` + +#### C. `DetailsInputs.jsx` — nested inputs + refactor + +Input processing was refactored from an inline `useEffect` body into a `getInputsContent` useCallback. More importantly, it now handles inputs where the value is a nested object (recursive call), which previously would throw on `.startsWith()`: + +```diff ++ if (inputPath && typeof inputPath === 'object') { ++ getInputsContent(inputPath) ++ return ++ } +``` + +#### D. `DetailsPipeline.jsx` — loading state + error step fix + +1. Loading state now comes from `artifactsStore.pipelines.loading` instead of `functionsStore.funcLoading` — the pipeline data is fetched as an artifact, not a function. +2. `addVisualFramesForGroups` renamed to `addVisualFramesForFunctions` (matches actual semantics). +3. Error steps with a `base_step` now inherit the base step's function: + ```diff + + } else if (step.kind === ERROR_STEP_KIND && !step.function && step.base_step) { + + stepData.function = steps[step.base_step]?.function || '' + + } + ``` + +#### E. `generateTemplatesCategories.js` — category deduplication + +1. Added `categoryMap` to normalize category names (e.g., `genai` → `GenAI`). +2. Category deduplication is now case-insensitive (previously two entries with different cases could coexist). + +```diff ++ const categoryMap = { genai: 'GenAI' } ++ categories: template.metadata?.categories?.map(cat => categoryMap[cat.toLowerCase()] || cat) + +// Deduplication: +- if (!hubFunctionsCategories.includes(category)) { ++ const isDuplicate = hubFunctionsCategories.some( ++ existing => existing.toLowerCase() === category.toLowerCase() ++ ) ++ if (!isDuplicate) { +``` + +#### F. Other safe fixes + +| File | Fix | +|------|-----| +| `src/hooks/nuclioMode.hook.js` | Optional chaining on `window?.mlrunConfig` | +| `src/utils/getState.js` | Added `standby`, `scaledToZero`, `initialized` states | +| `src/api/jobs-api.js` | Stream via axios adapter instead of fetch | +| `src/utils/getJobLogs.util.js` | `res.body` → `res.data` (pairs with jobs-api.js) | +| `src/reducers/artifactsReducer.js` | Error message callback | +| `src/reducers/functionReducer.js` | `return thunkAPI.rejectWithValue` | +| `src/components/Datasets/datasets.util.jsx` | Optional chaining `artifact_limits?.max_download_size` | +| `src/common/Download/Download.jsx` | Optional chaining | +| `src/utils/getArtifactPreview.jsx` | Optional chaining | +| `src/components/FunctionsPage/Functions.jsx` | `!isEmpty(selectedFunction)` guard | +| `src/common/DatePicker/DatePicker.jsx` | `dateTo` null safety | +| `src/common/ReactFlow/mlReactFlow.util.js` | Rename `addVisualFramesForGroups` → `addVisualFramesForFunctions`; margin 16→34 | +| `src/utils/getNoDataMessage.js` | Added filter message, removed trailing periods | +| `src/components/ModelsPage/RealTimePipelines/RealTimePipelinesCounters.jsx` | Tooltip wrappers on stats cards | +| `src/components/Details/DetailsTabsContent/DetailsTabsContent.jsx` | `handleCancel` forwarded to `DetailsLLMPrompts`; `refresh` prop on `DetailsModelEndpoints` | diff --git a/eslint.config.mjs b/eslint.config.mjs index d476aa233d..e06e5699e2 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -5,24 +5,28 @@ import react from 'eslint-plugin-react' import reactHooks from 'eslint-plugin-react-hooks' import eslintPluginImport from 'eslint-plugin-import' +import { viteGlobals } from './eslint.mlrun-globals.mjs' + export default [ - { ignores: ['dist'] }, + { ignores: ['dist', '.__mf__temp'] }, js.configs.recommended, eslintConfigPrettier, { - files: ['**/*.{js,jsx,ts,tsx}'], + files: ['**/*.{js,mjs,jsx,ts,tsx}'], languageOptions: { ecmaVersion: 2021, globals: { ...globals.browser, ...globals.jest, - ...globals.node + ...globals.node, + ...viteGlobals }, parserOptions: { ecmaFeatures: { jsx: true } - } + }, + sourceType: 'module' }, plugins: { react: react, @@ -49,9 +53,9 @@ export default [ } }, { - files: ["**/*.test.jsx"], + files: ['**/*.test.jsx'], rules: { - "import/named": "off" + 'import/named': 'off' } } ] diff --git a/eslint.mlrun-globals.mjs b/eslint.mlrun-globals.mjs new file mode 100644 index 0000000000..ea00e909b4 --- /dev/null +++ b/eslint.mlrun-globals.mjs @@ -0,0 +1,9 @@ +export const viteGlobals = { + VITE_PUBLIC_URL: 'readonly', + VITE_MLRUN_API_URL: 'readonly', + VITE_NUCLIO_API_URL: 'readonly', + VITE_IGUAZIO_API_URL: 'readonly', + VITE_FUNCTION_CATALOG_URL: 'readonly', + VITE_MLRUN_V3IO_ACCESS_KEY: 'readonly', + VITE_FEDERATION: 'readonly' +} diff --git a/nginx/nginx.conf.tmpl b/nginx/nginx.conf.tmpl index 4bdb86a342..7989d3eeba 100644 --- a/nginx/nginx.conf.tmpl +++ b/nginx/nginx.conf.tmpl @@ -1,47 +1,30 @@ -map $http_x_v3io_session_key $v3io_session_key { - default $http_x_v3io_session_key; - "" ${MLRUN_V3IO_ACCESS_KEY}; -} - server { - - listen 8090; - - include resolvers.conf; - - # https://raw.githubusercontent.com - set $function_catalog ${MLRUN_FUNCTION_CATALOG_URL}; - location /function-catalog { - rewrite /function-catalog/(.*) ${MLRUN_FUNCTION_CATALOG_PATH}/$1 break; - proxy_pass $function_catalog; - } - - location /mlrun { - rewrite ^/mlrun(/|$)(.*) /$2 last; - } - - location / { - root /usr/share/nginx/html; - index index.html index.htm; - try_files $uri $uri/ /index.html; - } - - set $backend ${MLRUN_API_PROXY_URL}; - location /api { - proxy_set_header x-v3io-session-key $v3io_session_key; - proxy_pass $backend; - } - - set $nuclio_backend ${MLRUN_NUCLIO_API_URL}; - location /nuclio { - rewrite ^/nuclio(/|$)(.*) /$2 break; - proxy_pass $nuclio_backend; - } - - error_page 500 502 503 504 /50x.html; - - location = /50x.html { - root /usr/share/nginx/html; - } - + listen 8090; + server_name localhost; + + root /usr/share/nginx/html; + + # Basic Gzip as per standard defaults + gzip on; + + # CRITICAL: Allow the Global Host (IGZ) to fetch these assets + add_header Access-Control-Allow-Origin *; + add_header Access-Control-Allow-Methods 'GET, OPTIONS'; + add_header Access-Control-Allow-Headers 'Content-Type, Authorization'; + + location / { + # Show landing page when accessing the remote directly + index landing.html; + try_files $uri $uri/ /landing.html; + + # Ensure remote assets are always fresh + if ($request_filename ~* .*\.(?:js|json)$ ) { + add_header Cache-Control "no-cache, no-store, must-revalidate"; + } + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } } diff --git a/nginx/run_nginx b/nginx/run_nginx index b9d3367b4a..f6e1ae65af 100755 --- a/nginx/run_nginx +++ b/nginx/run_nginx @@ -1,23 +1,7 @@ #!/bin/sh -# update nginx configuration with env -envsubst '${MLRUN_API_PROXY_URL} \ - ${MLRUN_BETA_MODE} \ - ${MLRUN_FUNCTION_CATALOG_URL} \ - ${MLRUN_FUNCTION_CATALOG_PATH} \ - ${MLRUN_NUCLIO_API_URL} \ - ${MLRUN_NUCLIO_MODE} \ - ${MLRUN_NUCLIO_UI_URL} \ - ${MLRUN_V3IO_ACCESS_KEY}' \ -< /etc/nginx/conf.d/nginx.conf.tmpl \ -> /etc/nginx/conf.d/nginx.conf +# Simply copy the template as the final config +cp /etc/nginx/conf.d/nginx.conf.tmpl /etc/nginx/conf.d/nginx.conf -# update configuration with env -envsubst '${MLRUN_BETA_MODE} \ - ${MLRUN_NUCLIO_MODE} \ - ${MLRUN_NUCLIO_UI_URL}' \ -< /usr/share/nginx/html/config.json.tmpl \ -> /usr/share/nginx/html/config.json - -# start nginx in the foreground -nginx -g 'daemon off;' +# Start Nginx in foreground +exec nginx -g 'daemon off;' diff --git a/package-lock.json b/package-lock.json index 807df01f5f..69acc97f78 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,8 @@ "hasInstallScript": true, "dependencies": { "@dagrejs/dagre": "^1.1.5", + "@module-federation/runtime": "^0.23.0", + "@module-federation/vite": "^1.2.6", "@monaco-editor/react": "^4.7.0", "@reduxjs/toolkit": "^1.9.5", "axios": "1.13.5", @@ -21,6 +23,7 @@ "concurrently": "^6.4.2", "cronstrue": "^2.49.0", "dotenv": "^10.0.0", + "dotenv-cli": "^10.0.0", "dotenv-expand": "^5.1.0", "file-saver": "^2.0.5", "final-form": "^4.20.10", @@ -40,6 +43,7 @@ "qs": "^6.15.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-error-boundary": "^6.1.0", "react-final-form": "^6.5.9", "react-final-form-arrays": "^3.1.4", "react-modal-promise": "^1.0.2", @@ -4876,13 +4880,20 @@ } }, "node_modules/@cucumber/ci-environment": { +<<<<<<< HEAD "version": "12.0.0", "resolved": "https://registry.npmjs.org/@cucumber/ci-environment/-/ci-environment-12.0.0.tgz", "integrity": "sha512-SqCEnbCNl3zCXCFpqGUuoaSNhLC0jLw4tKeFcAxTw9MD/QRlJjeAC/fyvVLFuXuSq0OunJlFfxLu+Z3HE+oLPg==", +======= + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/ci-environment/-/ci-environment-13.0.0.tgz", + "integrity": "sha512-cs+3NzfNkGbcmHPddjEv4TKFiBpZRQ6WJEEufB9mw+ExS22V/4R/zpDSEG+fsJ/iSNCd6A2sATdY8PFOyY3YnA==", +>>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "dev": true, "license": "MIT" }, "node_modules/@cucumber/cucumber": { +<<<<<<< HEAD "version": "12.6.0", "resolved": "https://registry.npmjs.org/@cucumber/cucumber/-/cucumber-12.6.0.tgz", "integrity": "sha512-z6XKBIcUnJebnR3W8+K7Q2jJKB+pKpoD1l3CygEa9ufq/aeGuS5LAlllNxrod8loepLJhNmp8J8aengGbkL4cg==", @@ -4900,6 +4911,25 @@ "@cucumber/messages": "31.2.0", "@cucumber/pretty-formatter": "1.0.1", "@cucumber/tag-expressions": "8.1.0", +======= + "version": "12.7.0", + "resolved": "https://registry.npmjs.org/@cucumber/cucumber/-/cucumber-12.7.0.tgz", + "integrity": "sha512-7A/9CJpJDxv1SQ7hAZU0zPn2yRxx6XMR+LO4T94Enm3cYNWsEEj+RGX38NLX4INT+H6w5raX3Csb/qs4vUBsOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cucumber/ci-environment": "13.0.0", + "@cucumber/cucumber-expressions": "19.0.0", + "@cucumber/gherkin": "38.0.0", + "@cucumber/gherkin-streams": "6.0.0", + "@cucumber/gherkin-utils": "11.0.0", + "@cucumber/html-formatter": "23.0.0", + "@cucumber/junit-xml-formatter": "0.9.0", + "@cucumber/message-streams": "4.0.1", + "@cucumber/messages": "32.0.1", + "@cucumber/pretty-formatter": "1.0.1", + "@cucumber/tag-expressions": "9.1.0", +>>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "assertion-error-formatter": "^3.0.0", "capital-case": "^1.0.4", "chalk": "^4.1.2", @@ -4922,7 +4952,11 @@ "mz": "^2.7.0", "progress": "^2.0.3", "read-package-up": "^12.0.0", +<<<<<<< HEAD "semver": "7.7.3", +======= + "semver": "7.7.4", +>>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "string-argv": "0.3.1", "supports-color": "^8.1.1", "type-fest": "^4.41.0", @@ -4941,9 +4975,15 @@ } }, "node_modules/@cucumber/cucumber-expressions": { +<<<<<<< HEAD "version": "18.1.0", "resolved": "https://registry.npmjs.org/@cucumber/cucumber-expressions/-/cucumber-expressions-18.1.0.tgz", "integrity": "sha512-9yc+wForrn15FaqLWNjYb19iQ/gPXhcq1kc4X1Ex1lR7NcJpa5pGnCow3bc1HERVM5IoYH+gwwrcJogSMsf+Vw==", +======= + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/cucumber-expressions/-/cucumber-expressions-19.0.0.tgz", + "integrity": "sha512-4FKoOQh2Uf6F6/Ln+1OxuK8LkTg6PyAqekhf2Ix8zqV2M54sH+m7XNJNLhOFOAW/t9nxzRbw2CcvXbCLjcvHZg==", +>>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "dev": true, "license": "MIT", "dependencies": { @@ -4960,6 +5000,7 @@ "node": ">=20" } }, +<<<<<<< HEAD "node_modules/@cucumber/cucumber/node_modules/semver": { "version": "7.7.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", @@ -4981,6 +5022,16 @@ "license": "MIT", "dependencies": { "@cucumber/messages": ">=31.0.0 <32" +======= + "node_modules/@cucumber/gherkin": { + "version": "38.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/gherkin/-/gherkin-38.0.0.tgz", + "integrity": "sha512-duEXK+KDfQUzu3vsSzXjkxQ2tirF5PRsc1Xrts6THKHJO6mjw4RjM8RV+vliuDasmhhrmdLcOcM7d9nurNTJKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cucumber/messages": ">=31.0.0 <33" +>>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) } }, "node_modules/@cucumber/gherkin-streams": { @@ -5013,6 +5064,7 @@ } }, "node_modules/@cucumber/gherkin-utils": { +<<<<<<< HEAD "version": "10.0.0", "resolved": "https://registry.npmjs.org/@cucumber/gherkin-utils/-/gherkin-utils-10.0.0.tgz", "integrity": "sha512-BcujlDT343GXXNrMPl3ws6Il3zs8dQw3Yp/d3HnOJF8i2snGGgiapoTbko7MdvAt7ivDL7SDo+e1d5Cnpl3llA==", @@ -5023,6 +5075,18 @@ "@cucumber/messages": "^29.0.0", "@teppeis/multimaps": "3.0.0", "commander": "14.0.0", +======= + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/gherkin-utils/-/gherkin-utils-11.0.0.tgz", + "integrity": "sha512-LJ+s4+TepHTgdKWDR4zbPyT7rQjmYIcukTwNbwNwgqr6i8Gjcmzf6NmtbYDA19m1ZFg6kWbFsmHnj37ZuX+kZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cucumber/gherkin": "^38.0.0", + "@cucumber/messages": "^32.0.0", + "@teppeis/multimaps": "3.0.0", + "commander": "14.0.2", +>>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "source-map-support": "^0.5.21" }, "bin": { @@ -5071,13 +5135,20 @@ "license": "MIT" }, "node_modules/@cucumber/gherkin-utils/node_modules/commander": { +<<<<<<< HEAD "version": "14.0.0", "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.0.tgz", "integrity": "sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==", +======= + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", + "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", +>>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "dev": true, "license": "MIT", "engines": { "node": ">=20" +<<<<<<< HEAD } }, "node_modules/@cucumber/gherkin-utils/node_modules/uuid": { @@ -5098,6 +5169,14 @@ "version": "22.3.0", "resolved": "https://registry.npmjs.org/@cucumber/html-formatter/-/html-formatter-22.3.0.tgz", "integrity": "sha512-0s3G7kznCRDiiesQ4K0yBdswGqU9E0j2AWUug41NpedBzhaY+Hn192ANRF597GZtuWrCjE53aFb3fOyOsT8B+g==", +======= + } + }, + "node_modules/@cucumber/html-formatter": { + "version": "23.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/html-formatter/-/html-formatter-23.0.0.tgz", + "integrity": "sha512-WwcRzdM8Ixy4e53j+Frm3fKM5rNuIyWUfy4HajEN+Xk/YcjA6yW0ACGTFDReB++VDZz/iUtwYdTlPRY36NbqJg==", +>>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "dev": true, "license": "MIT", "peerDependencies": { @@ -5131,9 +5210,15 @@ } }, "node_modules/@cucumber/messages": { +<<<<<<< HEAD "version": "31.2.0", "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-31.2.0.tgz", "integrity": "sha512-3urzBNCwmU/YKrKR0b3XdioFcOFNuxlLwEImsxeP8rXnweLs+Ky04QURcbKpFom3T6a6v9zVioLCfHUuSQ72pg==", +======= + "version": "32.0.1", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-32.0.1.tgz", + "integrity": "sha512-1OSoW+GQvFUNAl6tdP2CTBexTXMNJF0094goVUcvugtQeXtJ0K8sCP0xbq7GGoiezs/eJAAOD03+zAPT64orHQ==", +>>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "dev": true, "license": "MIT", "dependencies": { @@ -5158,6 +5243,22 @@ "@cucumber/messages": "*" } }, +<<<<<<< HEAD +======= + "node_modules/@cucumber/pretty-formatter/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, +>>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "node_modules/@cucumber/query": { "version": "14.7.0", "resolved": "https://registry.npmjs.org/@cucumber/query/-/query-14.7.0.tgz", @@ -5173,9 +5274,15 @@ } }, "node_modules/@cucumber/tag-expressions": { +<<<<<<< HEAD "version": "8.1.0", "resolved": "https://registry.npmjs.org/@cucumber/tag-expressions/-/tag-expressions-8.1.0.tgz", "integrity": "sha512-UFeOVUyc711/E7VHjThxMwg3jbGod9TlbM1gxNixX/AGDKg82Eha4cE0tKki3GGUs7uB2NyI+hQAuhB8rL2h5A==", +======= + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@cucumber/tag-expressions/-/tag-expressions-9.1.0.tgz", + "integrity": "sha512-bvHjcRFZ+J1TqIa9eFNO1wGHqwx4V9ZKV3hYgkuK/VahHx73uiP4rKV3JVrvWSMrwrFvJG6C8aEwnCWSvbyFdQ==", +>>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "dev": true, "license": "MIT" }, @@ -5204,7 +5311,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5221,7 +5327,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5238,7 +5343,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5255,7 +5359,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5272,7 +5375,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5289,7 +5391,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5306,7 +5407,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5323,7 +5423,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5340,7 +5439,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5357,7 +5455,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5374,7 +5471,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5391,7 +5487,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5408,7 +5503,6 @@ "cpu": [ "mips64el" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5425,7 +5519,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5442,7 +5535,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5459,7 +5551,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5476,7 +5567,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5493,7 +5583,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5510,7 +5599,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5527,7 +5615,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5544,7 +5631,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5561,7 +5647,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5578,7 +5663,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5595,7 +5679,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5612,7 +5695,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5629,7 +5711,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6033,7 +6114,7 @@ "version": "0.3.11", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", - "dev": true, + "devOptional": true, "license": "MIT", "peer": true, "dependencies": { @@ -6081,6 +6162,348 @@ "react": ">=16" } }, + "node_modules/@module-federation/dts-plugin": { + "version": "0.21.6", + "resolved": "https://registry.npmjs.org/@module-federation/dts-plugin/-/dts-plugin-0.21.6.tgz", + "integrity": "sha512-YIsDk8/7QZIWn0I1TAYULniMsbyi2LgKTi9OInzVmZkwMC6644x/ratTWBOUDbdY1Co+feNkoYeot1qIWv2L7w==", + "license": "MIT", + "dependencies": { + "@module-federation/error-codes": "0.21.6", + "@module-federation/managers": "0.21.6", + "@module-federation/sdk": "0.21.6", + "@module-federation/third-party-dts-extractor": "0.21.6", + "adm-zip": "^0.5.10", + "ansi-colors": "^4.1.3", + "axios": "^1.12.0", + "chalk": "3.0.0", + "fs-extra": "9.1.0", + "isomorphic-ws": "5.0.0", + "koa": "3.0.3", + "lodash.clonedeepwith": "4.5.0", + "log4js": "6.9.1", + "node-schedule": "2.1.1", + "rambda": "^9.1.0", + "ws": "8.18.0" + }, + "peerDependencies": { + "typescript": "^4.9.0 || ^5.0.0", + "vue-tsc": ">=1.0.24" + }, + "peerDependenciesMeta": { + "vue-tsc": { + "optional": true + } + } + }, + "node_modules/@module-federation/dts-plugin/node_modules/@module-federation/error-codes": { + "version": "0.21.6", + "resolved": "https://registry.npmjs.org/@module-federation/error-codes/-/error-codes-0.21.6.tgz", + "integrity": "sha512-MLJUCQ05KnoVl8xd6xs9a5g2/8U+eWmVxg7xiBMeR0+7OjdWUbHwcwgVFatRIwSZvFgKHfWEiI7wsU1q1XbTRQ==", + "license": "MIT" + }, + "node_modules/@module-federation/dts-plugin/node_modules/@module-federation/sdk": { + "version": "0.21.6", + "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.21.6.tgz", + "integrity": "sha512-x6hARETb8iqHVhEsQBysuWpznNZViUh84qV2yE7AD+g7uIzHKiYdoWqj10posbo5XKf/147qgWDzKZoKoEP2dw==", + "license": "MIT" + }, + "node_modules/@module-federation/dts-plugin/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@module-federation/dts-plugin/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@module-federation/dts-plugin/node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@module-federation/dts-plugin/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@module-federation/dts-plugin/node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@module-federation/error-codes": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@module-federation/error-codes/-/error-codes-0.23.0.tgz", + "integrity": "sha512-CzcKOPKh/qB1wPkVBC0iEK/Cg4jRAS1DnZsTx7b3JUCIXDcIaRq/XkTdo+EQ0cAsF5Os9lQ0f50O9DC/uFC8eA==", + "license": "MIT" + }, + "node_modules/@module-federation/managers": { + "version": "0.21.6", + "resolved": "https://registry.npmjs.org/@module-federation/managers/-/managers-0.21.6.tgz", + "integrity": "sha512-BeV6m2/7kF5MDVz9JJI5T8h8lMosnXkH2bOxxFewcra7ZjvDOgQu7WIio0mgk5l1zjNPvnEVKhnhrenEdcCiWg==", + "license": "MIT", + "dependencies": { + "@module-federation/sdk": "0.21.6", + "find-pkg": "2.0.0", + "fs-extra": "9.1.0" + } + }, + "node_modules/@module-federation/managers/node_modules/@module-federation/sdk": { + "version": "0.21.6", + "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.21.6.tgz", + "integrity": "sha512-x6hARETb8iqHVhEsQBysuWpznNZViUh84qV2yE7AD+g7uIzHKiYdoWqj10posbo5XKf/147qgWDzKZoKoEP2dw==", + "license": "MIT" + }, + "node_modules/@module-federation/managers/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@module-federation/managers/node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@module-federation/runtime": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.23.0.tgz", + "integrity": "sha512-ZHJcfM1O8RqYVrlIbhyeQ3S6gJW3mqHso3/QY7cKs1za+UvOgB8aTsDwq7Fv+aJZWSmtGzWa4zbSuxthyucw3g==", + "license": "MIT", + "dependencies": { + "@module-federation/error-codes": "0.23.0", + "@module-federation/runtime-core": "0.23.0", + "@module-federation/sdk": "0.23.0" + } + }, + "node_modules/@module-federation/runtime-core": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@module-federation/runtime-core/-/runtime-core-0.23.0.tgz", + "integrity": "sha512-+Orumtyg6Q2v19Gz15P3kDmRf4Q6KEpv8DggKWHdM8AX4xyVT8dMRJxdIxaVddbIYTd7aL7o2U3LLK6EjUe4UA==", + "license": "MIT", + "dependencies": { + "@module-federation/error-codes": "0.23.0", + "@module-federation/sdk": "0.23.0" + } + }, + "node_modules/@module-federation/sdk": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.23.0.tgz", + "integrity": "sha512-1+DICHIF1z6yggtsZypmcn1gL35iitiSDXcsaqWynK4v5aw9MBRUS4zP3kG7eQDFTMmIo+rGbPN37AUsOq/RRQ==", + "license": "MIT" + }, + "node_modules/@module-federation/third-party-dts-extractor": { + "version": "0.21.6", + "resolved": "https://registry.npmjs.org/@module-federation/third-party-dts-extractor/-/third-party-dts-extractor-0.21.6.tgz", + "integrity": "sha512-Il6x4hLsvCgZNk1DFwuMBNeoxD1BsZ5AW2BI/nUgu0k5FiAvfcz1OFawRFEHtaM/kVrCsymMOW7pCao90DaX3A==", + "license": "MIT", + "dependencies": { + "find-pkg": "2.0.0", + "fs-extra": "9.1.0", + "resolve": "1.22.8" + } + }, + "node_modules/@module-federation/third-party-dts-extractor/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@module-federation/third-party-dts-extractor/node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@module-federation/third-party-dts-extractor/node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@module-federation/vite": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@module-federation/vite/-/vite-1.11.0.tgz", + "integrity": "sha512-dawLMF1JUt/4IUwQh7dUF5TBcetGg3qPKBX+hCFL2aHFd8m3EI+HmGc4qIGVlGZMub6mpTIGo9EHfhV5cDIOmQ==", + "license": "MIT", + "dependencies": { + "@module-federation/dts-plugin": "^0.21.6", + "@module-federation/runtime": "^0.21.6", + "@module-federation/sdk": "^0.21.6", + "@rollup/pluginutils": "^5.1.0", + "defu": "^6.1.4", + "estree-walker": "^2", + "magic-string": "^0.30.11", + "pathe": "^1.1.2" + }, + "peerDependencies": { + "vite": "<=7.1.7" + } + }, + "node_modules/@module-federation/vite/node_modules/@module-federation/error-codes": { + "version": "0.21.6", + "resolved": "https://registry.npmjs.org/@module-federation/error-codes/-/error-codes-0.21.6.tgz", + "integrity": "sha512-MLJUCQ05KnoVl8xd6xs9a5g2/8U+eWmVxg7xiBMeR0+7OjdWUbHwcwgVFatRIwSZvFgKHfWEiI7wsU1q1XbTRQ==", + "license": "MIT" + }, + "node_modules/@module-federation/vite/node_modules/@module-federation/runtime": { + "version": "0.21.6", + "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.21.6.tgz", + "integrity": "sha512-+caXwaQqwTNh+CQqyb4mZmXq7iEemRDrTZQGD+zyeH454JAYnJ3s/3oDFizdH6245pk+NiqDyOOkHzzFQorKhQ==", + "license": "MIT", + "dependencies": { + "@module-federation/error-codes": "0.21.6", + "@module-federation/runtime-core": "0.21.6", + "@module-federation/sdk": "0.21.6" + } + }, + "node_modules/@module-federation/vite/node_modules/@module-federation/runtime-core": { + "version": "0.21.6", + "resolved": "https://registry.npmjs.org/@module-federation/runtime-core/-/runtime-core-0.21.6.tgz", + "integrity": "sha512-5Hd1Y5qp5lU/aTiK66lidMlM/4ji2gr3EXAtJdreJzkY+bKcI5+21GRcliZ4RAkICmvdxQU5PHPL71XmNc7Lsw==", + "license": "MIT", + "dependencies": { + "@module-federation/error-codes": "0.21.6", + "@module-federation/sdk": "0.21.6" + } + }, + "node_modules/@module-federation/vite/node_modules/@module-federation/sdk": { + "version": "0.21.6", + "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.21.6.tgz", + "integrity": "sha512-x6hARETb8iqHVhEsQBysuWpznNZViUh84qV2yE7AD+g7uIzHKiYdoWqj10posbo5XKf/147qgWDzKZoKoEP2dw==", + "license": "MIT" + }, + "node_modules/@module-federation/vite/node_modules/@rollup/pluginutils": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", + "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@module-federation/vite/node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "license": "MIT" + }, + "node_modules/@module-federation/vite/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@monaco-editor/loader": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.7.0.tgz", @@ -6153,7 +6576,6 @@ "version": "2.5.6", "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz", "integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -6193,7 +6615,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6214,7 +6635,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6235,7 +6655,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6256,7 +6675,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6277,7 +6695,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6298,7 +6715,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6319,7 +6735,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6340,7 +6755,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6361,7 +6775,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6382,7 +6795,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6403,7 +6815,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6424,7 +6835,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6445,7 +6855,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6460,10 +6869,9 @@ } }, "node_modules/@parcel/watcher/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", "optional": true, "engines": { @@ -6649,7 +7057,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6663,7 +7070,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6677,7 +7083,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6691,7 +7096,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6705,7 +7109,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6719,7 +7122,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6733,7 +7135,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6747,7 +7148,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6761,7 +7161,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6775,7 +7174,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6789,7 +7187,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6803,7 +7200,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6817,7 +7213,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6831,7 +7226,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6845,7 +7239,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6859,7 +7252,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6873,7 +7265,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6887,7 +7278,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6901,7 +7291,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6915,7 +7304,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6929,7 +7317,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6943,7 +7330,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6957,7 +7343,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6971,7 +7356,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6985,7 +7369,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8498,7 +8881,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, "license": "MIT" }, "node_modules/@types/geojson": { @@ -8605,10 +8987,17 @@ "license": "MIT" }, "node_modules/@types/node": { +<<<<<<< HEAD "version": "25.2.3", "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.3.tgz", "integrity": "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==", "dev": true, +======= + "version": "25.2.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.0.tgz", + "integrity": "sha512-DZ8VwRFUNzuqJ5khrvwMXHmvPe+zGayJhr2CDNiKB1WBE1ST8Djl00D0IC4vvNmHMdj6DlbYRIaFE7WHjlDl5w==", + "devOptional": true, +>>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "license": "MIT", "dependencies": { "undici-types": "~7.16.0" @@ -9113,7 +9502,6 @@ "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dev": true, "license": "MIT", "dependencies": { "mime-types": "~2.1.34", @@ -9169,6 +9557,15 @@ "node": ">=8.9" } }, + "node_modules/adm-zip": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", + "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==", + "license": "MIT", + "engines": { + "node": ">=12.0" + } + }, "node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", @@ -9251,6 +9648,15 @@ "ajv": "^6.9.1" } }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/ansi-regex": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", @@ -9597,7 +10003,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true, "license": "ISC", "engines": { "node": ">= 4.0.0" @@ -10356,7 +10761,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/bytes": { @@ -10922,7 +11327,6 @@ "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dev": true, "license": "MIT", "dependencies": { "safe-buffer": "5.2.1" @@ -10935,7 +11339,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -10965,6 +11368,19 @@ "dev": true, "license": "MIT" }, + "node_modules/cookies": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.9.1.tgz", + "integrity": "sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "keygrip": "~1.1.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/core-js": { "version": "3.48.0", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.48.0.tgz", @@ -11025,6 +11441,18 @@ "node": ">= 6" } }, + "node_modules/cron-parser": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz", + "integrity": "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==", + "license": "MIT", + "dependencies": { + "luxon": "^3.2.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/cronstrue": { "version": "2.59.0", "resolved": "https://registry.npmjs.org/cronstrue/-/cronstrue-2.59.0.tgz", @@ -11057,7 +11485,6 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -11793,11 +12220,19 @@ "url": "https://github.com/sponsors/kossnocorp" } }, + "node_modules/date-format": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", + "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==", + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -11888,6 +12323,12 @@ "node": ">=6" } }, + "node_modules/deep-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", + "integrity": "sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==", + "license": "MIT" + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -11951,6 +12392,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "license": "MIT" + }, "node_modules/degenerator": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", @@ -11989,11 +12436,16 @@ "node": ">=0.4.0" } }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "license": "MIT" + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" @@ -12013,7 +12465,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.8", @@ -12024,7 +12475,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "dev": true, "license": "Apache-2.0", "optional": true, "engines": { @@ -12224,6 +12674,60 @@ "node": ">=10" } }, + "node_modules/dotenv-cli": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv-cli/-/dotenv-cli-10.0.0.tgz", + "integrity": "sha512-lnOnttzfrzkRx2echxJHQRB6vOAMSCzzZg79IxpC00tU42wZPuZkQxNNrrwVAxaQZIIh001l4PxVlCrBxngBzA==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.6", + "dotenv": "^17.1.0", + "dotenv-expand": "^11.0.0", + "minimist": "^1.2.6" + }, + "bin": { + "dotenv": "cli.js" + } + }, + "node_modules/dotenv-cli/node_modules/dotenv": { + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", + "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-cli/node_modules/dotenv-expand": { + "version": "11.0.7", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.7.tgz", + "integrity": "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==", + "license": "BSD-2-Clause", + "dependencies": { + "dotenv": "^16.4.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-cli/node_modules/dotenv-expand/node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dotenv-expand": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", @@ -12265,7 +12769,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "dev": true, "license": "MIT" }, "node_modules/electron-to-chromium": { @@ -12294,7 +12797,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" @@ -12564,7 +13066,6 @@ "version": "0.25.12", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", - "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -12629,7 +13130,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "dev": true, "license": "MIT" }, "node_modules/escape-string-regexp": { @@ -13218,7 +13718,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true, "license": "MIT" }, "node_modules/esutils": { @@ -13272,6 +13771,18 @@ "node": ">=8" } }, + "node_modules/expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", + "license": "MIT", + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/expect-type": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", @@ -13679,6 +14190,30 @@ "node": ">=6" } }, + "node_modules/find-file-up": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/find-file-up/-/find-file-up-2.0.1.tgz", + "integrity": "sha512-qVdaUhYO39zmh28/JLQM5CoYN9byEOKEH4qfa8K1eNV17W0UUMJ9WgbR/hHFH+t5rcl+6RTb5UC7ck/I+uRkpQ==", + "license": "MIT", + "dependencies": { + "resolve-dir": "^1.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/find-pkg/-/find-pkg-2.0.0.tgz", + "integrity": "sha512-WgZ+nKbELDa6N3i/9nrHeNznm+lY3z4YfhDDWgW+5P0pdmMj26bxaxU11ookgY3NyP9GC7HvZ9etp0jRFqGEeQ==", + "license": "MIT", + "dependencies": { + "find-file-up": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -13721,10 +14256,16 @@ } }, "node_modules/flatted": { +<<<<<<< HEAD "version": "3.4.2", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true, +======= + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", +>>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "license": "ISC" }, "node_modules/follow-redirects": { @@ -13944,7 +14485,6 @@ "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -14215,6 +14755,7 @@ } }, "node_modules/glob": { +<<<<<<< HEAD "version": "13.0.4", "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.4.tgz", "integrity": "sha512-KACie1EOs9BIOMtenFaxwmYODWA3/fTfGSUnLhMJpXRntu1g+uL/Xvub5f8SCTppvo9q62Qy4LeOoUiaL54G5A==", @@ -14227,6 +14768,20 @@ }, "engines": { "node": "20 || >=22" +======= + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" +>>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -14562,7 +15117,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", - "dev": true, "license": "MIT", "dependencies": { "parse-passwd": "^1.0.0" @@ -14666,11 +15220,57 @@ "node": ">= 6" } }, + "node_modules/http-assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.5.0.tgz", + "integrity": "sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==", + "license": "MIT", + "dependencies": { + "deep-equal": "~1.0.1", + "http-errors": "~1.8.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-assert/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-assert/node_modules/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-assert/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "dev": true, "license": "MIT", "dependencies": { "depd": "~2.0.0", @@ -14945,7 +15545,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, "license": "ISC" }, "node_modules/ini": { @@ -15645,6 +16244,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-wsl": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", @@ -15680,7 +16288,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, "license": "ISC" }, "node_modules/isobject": { @@ -15693,6 +16300,15 @@ "node": ">=0.10.0" } }, + "node_modules/isomorphic-ws": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", + "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", + "license": "MIT", + "peerDependencies": { + "ws": "*" + } + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -16007,6 +16623,18 @@ "setimmediate": "^1.0.5" } }, + "node_modules/keygrip": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz", + "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==", + "license": "MIT", + "dependencies": { + "tsscmp": "1.0.6" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -16063,6 +16691,89 @@ "seed-random": "~2.2.0" } }, + "node_modules/koa": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/koa/-/koa-3.0.3.tgz", + "integrity": "sha512-MeuwbCoN1daWS32/Ni5qkzmrOtQO2qrnfdxDHjrm6s4b59yG4nexAJ0pTEFyzjLp0pBVO80CZp0vW8Ze30Ebow==", + "license": "MIT", + "dependencies": { + "accepts": "^1.3.8", + "content-disposition": "~0.5.4", + "content-type": "^1.0.5", + "cookies": "~0.9.1", + "delegates": "^1.0.0", + "destroy": "^1.2.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "fresh": "~0.5.2", + "http-assert": "^1.5.0", + "http-errors": "^2.0.0", + "koa-compose": "^4.1.0", + "mime-types": "^3.0.1", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/koa-compose": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-4.1.0.tgz", + "integrity": "sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==", + "license": "MIT" + }, + "node_modules/koa/node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/koa/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/koa/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/koa/node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -16153,6 +16864,12 @@ "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", "license": "MIT" }, + "node_modules/lodash.clonedeepwith": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeepwith/-/lodash.clonedeepwith-4.5.0.tgz", + "integrity": "sha512-QRBRSxhbtsX1nc0baxSkkK5WlVTTm/s48DSukcGcWZwIyI8Zz+lB+kFiELJXtzfH4Aj6kMWQ1VWW4U5uUDgZMA==", + "license": "MIT" + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -16212,6 +16929,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/log4js": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz", + "integrity": "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==", + "license": "Apache-2.0", + "dependencies": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "flatted": "^3.2.7", + "rfdc": "^1.3.0", + "streamroller": "^3.1.5" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/loglevel": { "version": "1.9.2", "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz", @@ -16233,6 +16966,12 @@ "dev": true, "license": "MIT" }, + "node_modules/long-timeout": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/long-timeout/-/long-timeout-0.1.1.tgz", + "integrity": "sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==", + "license": "MIT" + }, "node_modules/longest-streak": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-2.0.4.tgz", @@ -16290,7 +17029,10 @@ "version": "3.7.2", "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz", "integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==", +<<<<<<< HEAD "dev": true, +======= +>>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "license": "MIT", "engines": { "node": ">=12" @@ -16311,7 +17053,6 @@ "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" @@ -16725,7 +17466,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -16757,11 +17497,11 @@ } }, "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": ">=16 || 14 >=14.17" } @@ -16816,7 +17556,6 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/mz": { @@ -16859,7 +17598,6 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -16914,7 +17652,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "dev": true, "license": "MIT", "optional": true }, @@ -16999,6 +17736,20 @@ "dev": true, "license": "MIT" }, + "node_modules/node-schedule": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/node-schedule/-/node-schedule-2.1.1.tgz", + "integrity": "sha512-OXdegQq03OmXEjt2hZP33W2YPs/E5BcFQks46+G2gAxs4gHOIVD1u7EqlYLYSKsaIpyKCK9Gbk0ta1/gjRSMRQ==", + "license": "MIT", + "dependencies": { + "cron-parser": "^4.2.0", + "long-timeout": "0.1.1", + "sorted-array-functions": "^1.3.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/nodemon": { "version": "3.1.11", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.11.tgz", @@ -17279,7 +18030,6 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dev": true, "license": "MIT", "dependencies": { "ee-first": "1.1.1" @@ -17505,7 +18255,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -17528,7 +18277,6 @@ "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" @@ -17558,7 +18306,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -17571,9 +18318,15 @@ "license": "MIT" }, "node_modules/path-scurry": { +<<<<<<< HEAD "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", +======= + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", +>>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -17581,7 +18334,11 @@ "minipass": "^7.1.2" }, "engines": { +<<<<<<< HEAD "node": "20 || >=22" +======= + "node": "18 || 20 || >=22" +>>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -19828,6 +20585,12 @@ "performance-now": "^2.1.0" } }, + "node_modules/rambda": { + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/rambda/-/rambda-9.4.2.tgz", + "integrity": "sha512-++euMfxnl7OgaEKwXh9QqThOjMeta2HH001N1v4mYQzBjJBnmXBh2BCK6dZAbICFVXOFUVD3xFG0R3ZPU0mxXw==", + "license": "MIT" + }, "node_modules/randexp": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.5.3.tgz", @@ -20083,6 +20846,15 @@ "react": "^18.3.1" } }, + "node_modules/react-error-boundary": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-6.1.1.tgz", + "integrity": "sha512-BrYwPOdXi5mqkk5lw+Uvt0ThHx32rCt3BkukS4X23A2AIWDPSGX6iaWTc0y9TU/mHDA/6qOSGel+B2ERkOvD1w==", + "license": "MIT", + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0" + } + }, "node_modules/react-error-overlay": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.1.0.tgz", @@ -20282,6 +21054,132 @@ "version": "12.0.0", "resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-12.0.0.tgz", "integrity": "sha512-Q5hMVBYur/eQNWDdbF4/Wqqr9Bjvtrw2kjGxxBbKLbx8bVCL8gcArjTy8zDUuLGQicftpMuU0riQNcAsbtOVsw==", +<<<<<<< HEAD +======= + "dev": true, + "license": "MIT", + "dependencies": { + "find-up-simple": "^1.0.1", + "read-pkg": "^10.0.0", + "type-fest": "^5.2.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-package-up/node_modules/hosted-git-info": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.2.tgz", + "integrity": "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^11.1.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/read-package-up/node_modules/lru-cache": { + "version": "11.2.6", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", + "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/read-package-up/node_modules/normalize-package-data": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-8.0.0.tgz", + "integrity": "sha512-RWk+PI433eESQ7ounYxIp67CYuVsS1uYSonX3kA6ps/3LWfjVQa/ptEg6Y3T6uAMq1mWpX9PQ+qx+QaHpsc7gQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^9.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/read-package-up/node_modules/parse-json": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", + "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "index-to-position": "^1.1.0", + "type-fest": "^4.39.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-package-up/node_modules/parse-json/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-package-up/node_modules/read-pkg": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-10.1.0.tgz", + "integrity": "sha512-I8g2lArQiP78ll51UeMZojewtYgIRCKCWqZEgOO8c/uefTI+XDXvCSXu3+YNUaTNvZzobrL5+SqHjBrByRRTdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.4", + "normalize-package-data": "^8.0.0", + "parse-json": "^8.3.0", + "type-fest": "^5.4.4", + "unicorn-magic": "^0.4.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-package-up/node_modules/type-fest": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.4.4.tgz", + "integrity": "sha512-JnTrzGu+zPV3aXIUhnyWJj4z/wigMsdYajGLIYakqyOW1nPllzXEJee0QQbHj+CTIQtXGlAjuK0UY+2xTyjVAw==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "dependencies": { + "tagged-tag": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", +>>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "dev": true, "license": "MIT", "dependencies": { @@ -20854,6 +21752,67 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", + "license": "MIT", + "dependencies": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-dir/node_modules/global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "license": "MIT", + "dependencies": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-dir/node_modules/global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", + "license": "MIT", + "dependencies": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-dir/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/resolve-dir/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, "node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -20906,6 +21865,12 @@ "node": ">=0.10.0" } }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "license": "MIT" + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -20946,10 +21911,16 @@ } }, "node_modules/rollup": { +<<<<<<< HEAD "version": "4.59.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", "dev": true, +======= + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz", + "integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==", +>>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "license": "MIT", "dependencies": { "@types/estree": "1.0.8" @@ -21055,7 +22026,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -21155,7 +22125,7 @@ "version": "1.97.3", "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.3.tgz", "integrity": "sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "chokidar": "^4.0.0", @@ -21215,7 +22185,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "readdirp": "^4.0.1" @@ -21228,17 +22198,24 @@ } }, "node_modules/sass/node_modules/immutable": { +<<<<<<< HEAD "version": "5.1.5", "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.5.tgz", "integrity": "sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==", "dev": true, +======= + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz", + "integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==", + "devOptional": true, +>>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "license": "MIT" }, "node_modules/sass/node_modules/readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">= 14.18.0" @@ -21486,7 +22463,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true, "license": "ISC" }, "node_modules/shallow-clone": { @@ -21506,7 +22482,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -21519,7 +22494,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -21617,6 +22591,7 @@ "dev": true, "license": "ISC" }, +<<<<<<< HEAD "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -21624,6 +22599,8 @@ "dev": true, "license": "ISC" }, +======= +>>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "node_modules/simple-update-notifier": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", @@ -21739,6 +22716,12 @@ "node": ">= 14" } }, + "node_modules/sorted-array-functions": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz", + "integrity": "sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA==", + "license": "MIT" + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -21795,7 +22778,7 @@ "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", @@ -21892,7 +22875,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" @@ -21947,6 +22929,52 @@ } } }, + "node_modules/streamroller": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", + "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==", + "license": "MIT", + "dependencies": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "fs-extra": "^8.1.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/streamroller/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/streamroller/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/streamroller/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -21988,6 +23016,7 @@ "node": ">=8" } }, +<<<<<<< HEAD "node_modules/string-width/node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -22009,6 +23038,8 @@ "node": ">=8" } }, +======= +>>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "node_modules/string.prototype.matchall": { "version": "4.0.12", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", @@ -22108,6 +23139,7 @@ } }, "node_modules/strip-ansi": { +<<<<<<< HEAD "version": "7.1.2", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", @@ -22121,6 +23153,17 @@ }, "funding": { "url": "https://github.com/chalk/strip-ansi?sponsor=1" +======= + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" +>>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) } }, "node_modules/strip-ansi/node_modules/ansi-regex": { @@ -22499,7 +23542,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/sugarss/-/sugarss-2.0.0.tgz", "integrity": "sha512-WfxjozUk0UVA4jm+U1d736AUpzSrNsQcIbyOkoE364GrtWmIrFdk5lksEupgWMD4VaT/0kVx1dobpiDumSgmJQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "postcss": "^7.0.2" @@ -22672,6 +23715,7 @@ "dev": true, "license": "MIT" }, +<<<<<<< HEAD "node_modules/table/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -22685,6 +23729,8 @@ "node": ">=8" } }, +======= +>>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "node_modules/tagged-tag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz", @@ -22798,7 +23844,7 @@ "version": "5.46.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz", "integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==", - "dev": true, + "devOptional": true, "license": "BSD-2-Clause", "peer": true, "dependencies": { @@ -22931,7 +23977,7 @@ "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true, + "devOptional": true, "license": "MIT", "peer": true, "bin": { @@ -22945,7 +23991,7 @@ "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, + "devOptional": true, "license": "MIT", "peer": true }, @@ -23126,6 +24172,7 @@ "dev": true, "license": "MIT" }, +<<<<<<< HEAD "node_modules/tmp": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", @@ -23136,6 +24183,8 @@ "node": ">=14.14" } }, +======= +>>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -23159,7 +24208,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.6" @@ -23300,6 +24348,15 @@ "dev": true, "license": "0BSD" }, + "node_modules/tsscmp": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", + "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", + "license": "MIT", + "engines": { + "node": ">=0.6.x" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -23442,7 +24499,6 @@ "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, "license": "Apache-2.0", "peer": true, "bin": { @@ -23483,7 +24539,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { @@ -23850,7 +24906,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" @@ -23892,7 +24947,6 @@ "version": "6.4.1", "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", - "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", @@ -24101,7 +25155,6 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, "license": "MIT", "engines": { "node": ">=12.0.0" @@ -24116,10 +25169,9 @@ } }, "node_modules/vite/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", "engines": { "node": ">=12" @@ -24482,7 +25534,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -24610,6 +25661,7 @@ "node": ">=0.10.0" } }, +<<<<<<< HEAD "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -24663,6 +25715,8 @@ "node": ">=8" } }, +======= +>>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -24688,7 +25742,6 @@ "version": "8.19.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", - "dev": true, "license": "MIT", "engines": { "node": ">=10.0.0" diff --git a/package.json b/package.json index 107eafb65a..9a23fc4f27 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,8 @@ "homepage": "/mlrun", "dependencies": { "@dagrejs/dagre": "^1.1.5", + "@module-federation/runtime": "^0.23.0", + "@module-federation/vite": "^1.2.6", "@monaco-editor/react": "^4.7.0", "@reduxjs/toolkit": "^1.9.5", "axios": "1.13.5", @@ -16,6 +18,7 @@ "concurrently": "^6.4.2", "cronstrue": "^2.49.0", "dotenv": "^10.0.0", + "dotenv-cli": "^10.0.0", "dotenv-expand": "^5.1.0", "file-saver": "^2.0.5", "final-form": "^4.20.10", @@ -35,6 +38,7 @@ "qs": "^6.15.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-error-boundary": "^6.1.0", "react-final-form": "^6.5.9", "react-final-form-arrays": "^3.1.4", "react-modal-promise": "^1.0.2", @@ -67,6 +71,7 @@ "scripts": { "start": "vite", "build": "vite build", + "preview:federation": "cross-env PREVIEW_MODE=true dotenv -e .env.development.mf -- vite build && node scripts/previewLocalBuildMF.mjs", "lint": "eslint \"src/**/*.{js,jsx}\"", "lint:fix": "eslint \"src/**/*.{js,jsx}\" --fix", "format:check": "prettier --check \"src/**/*.{js,jsx}\"", @@ -74,7 +79,7 @@ "preview": "vite preview", "preinstall": "npx force-resolutions", "test:coverage": "npm run test -- --coverage --watchAll=false", - "docker": "docker build -t ${MLRUN_DOCKER_REGISTRY}${MLRUN_DOCKER_REPO:-mlrun}/mlrun-ui:${MLRUN_DOCKER_TAG:-latest} --build-arg COMMIT_HASH=\"`git rev-parse --short HEAD`\" --build-arg DATE=\"`date -u`\" -f Dockerfile .", + "docker": "docker buildx build --platform linux/amd64 -t ${MLRUN_DOCKER_REGISTRY}${MLRUN_DOCKER_REPO:-mlrun}/mlrun-ui:${MLRUN_DOCKER_TAG:-latest} --build-arg COMMIT_HASH=\"`git rev-parse --short HEAD`\" --build-arg DATE=\"`date -u`\" --build-arg IS_MF=${npm_config_IS_MF:-false} -f Dockerfile .", "generate-rn": "./generate-release-notes.js ${MLRUN_OLD_VERSION} ${MLRUN_VERSION} ${MLRUN_RELEASE_BRANCH} ${MLRUN_RELEASE_TYPE}", "storybook": "start-storybook -p 6006", "build-storybook": "build-storybook", diff --git a/public/config.json b/public/config.json index 8d111a5c0b..06bc4b8e3a 100644 --- a/public/config.json +++ b/public/config.json @@ -1,5 +1,6 @@ { "betaMode": "enabled", "nuclioMode": "enabled", - "nuclioUiUrl": "http://localhost:8070" + "nuclioUiUrl": "http://localhost:4000", + "nuclioRemoteEntryUrl": "http://localhost:5189" } diff --git a/public/landing.html b/public/landing.html new file mode 100644 index 0000000000..3872792cf4 --- /dev/null +++ b/public/landing.html @@ -0,0 +1,59 @@ + + + + + + MLRun UI + + + +
+

MLRun Remote UI

+

This is a remote module component.

+

Please access it via the main Iguazio Dashboard.

+
+ + diff --git a/scripts/previewLocalBuildMF.mjs b/scripts/previewLocalBuildMF.mjs new file mode 100644 index 0000000000..f1ac97f363 --- /dev/null +++ b/scripts/previewLocalBuildMF.mjs @@ -0,0 +1,9 @@ +import { spawn } from 'child_process' + +const preview = spawn( + 'dotenv', + ['-e', '.env.development.mf', '--', 'vite', 'preview', '--port', '5179'], + { stdio: 'inherit', shell: true } +) + +preview.on('exit', code => process.exit(code ?? 0)) diff --git a/src/App.jsx b/src/App.jsx index 77dfeccb37..a307895dab 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -69,6 +69,7 @@ import { import 'reactflow/dist/style.css' import 'igz-controls/index.css' import './scss/main.scss' +import RemoteNuclioRouteWrapper from './components/RemoteNuclio/RemoteNuclioRouteWrapper' const Page = lazyRetry(() => import('./layout/Page/Page')) const Datasets = lazyRetry(() => import('./components/Datasets/Datasets')) @@ -158,6 +159,12 @@ const App = () => { <> }> } /> + + } /> + } /> + } /> + } /> + }> {[ `${JOBS_MONITORING_JOBS_TAB}/:jobName/:jobId/:tab`, @@ -184,7 +191,6 @@ const App = () => { } /> } /> - } /> } /> {[ diff --git a/src/api/jobs-api.js b/src/api/jobs-api.js index 34da1baddc..bd5686840b 100644 --- a/src/api/jobs-api.js +++ b/src/api/jobs-api.js @@ -19,7 +19,7 @@ such restriction. */ import { isNil } from 'lodash' -import { mainBaseUrl, mainHttpClient } from '../httpClient' +import { mainHttpClient } from '../httpClient' const jobsApi = { abortJob: (project, jobId, iter) => { @@ -76,10 +76,17 @@ const jobsApi = { params = `?attempt=${attempt}` } - return fetch(`${mainBaseUrl}/projects/${project}/logs/${id}${params}`, { - method: 'get', - signal - }) + // when we use adapter: 'fetch' in axios, we need to pass params as query string, because axios drops params in this case + const queryParams = new URLSearchParams(params).toString() + + return mainHttpClient.get( + `/projects/${project}/logs/${id}${queryParams ? '?' + queryParams : ''}`, + { + signal, + responseType: 'stream', + adapter: 'fetch' + } + ) }, getScheduledJobs: (project, newConfig) => { return mainHttpClient.get(`/projects/${project}/schedules`, newConfig) diff --git a/src/api/projects-iguazio-api.js b/src/api/projects-iguazio-api.js index edc7d3c702..04d9e3f79d 100644 --- a/src/api/projects-iguazio-api.js +++ b/src/api/projects-iguazio-api.js @@ -20,50 +20,21 @@ such restriction. import { iguazioHttpClient } from '../httpClient' const projectsIguazioApi = { - editProject: (projectId, data) => iguazioHttpClient.put(`/projects/${projectId}`, data), - getProjectJob: jobId => iguazioHttpClient.get(`/jobs/${jobId}`), - getProjects: config => { - return iguazioHttpClient.get('/projects', config) - }, - getProjectMembers: projectId => { - return iguazioHttpClient.get(`/projects/${projectId}`, { - params: { - include: - 'project_authorization_roles.principal_users,project_authorization_roles.principal_user_groups' - } - }) - }, - getProjectMembersVisibility: project => { - return iguazioHttpClient.get(`/projects/__name__/${project}/authorization`, { - params: { - action: 'authorization/roles', - sub_resource: 'authorization/roles' - } - }) - }, - getProjectOwnerVisibility: project => { - return iguazioHttpClient.get(`/projects/__name__/${project}/authorization`, { - params: { - action: 'update', - sub_resource: 'authorization/owner' - } - }) - }, - getProjectWorkflowsUpdateAuthorization: project => { - return iguazioHttpClient.get(`/projects/__name__/${project}/authorization`, { - params: { - action: 'update', - sub_resource: 'workflow' - } - }) - }, - - updateProjectMembers: data => { - return iguazioHttpClient.post('/async_transactions', data) - }, - getScrubbedUsers: config => iguazioHttpClient.get('/scrubbed_users', config), - getScrubbedUserGroups: config => iguazioHttpClient.get('/scrubbed_user_groups', config), - getActiveUser: () => iguazioHttpClient.get('/self') + updateProjectOwner: (projectName, owner) => + iguazioHttpClient.put(`/v1/authorization/projects/${projectName}/owner`, { + owner, + project: projectName + }), + getProjectPolicies: projectName => + iguazioHttpClient.get(`/v1/authorization/projects/${projectName}/policies`), + setProjectMembership: (projectName, data) => + iguazioHttpClient.put(`/v1/authorization/projects/${projectName}/roles`, data), + searchUsersMetadata: searchTerm => + iguazioHttpClient.get('/v1/profile/search-users-metadata', { params: { searchTerm } }), + searchGroupsMetadata: searchTerm => + iguazioHttpClient.get('/v1/profile/search-groups-metadata', { params: { searchTerm } }), + getActiveUser: () => + iguazioHttpClient.get('/v1/authentication/self', { params: { format: 'full' } }) } export default projectsIguazioApi diff --git a/src/common/ActionsMenu/ActionsMenu.jsx b/src/common/ActionsMenu/ActionsMenu.jsx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/common/Breadcrumbs/breadcrumbs.util.js b/src/common/Breadcrumbs/breadcrumbs.util.js index f09aa4bbd2..593c890c68 100644 --- a/src/common/Breadcrumbs/breadcrumbs.util.js +++ b/src/common/Breadcrumbs/breadcrumbs.util.js @@ -37,7 +37,6 @@ import { DOCUMENTS_PAGE, LLM_PROMPTS_PAGE } from '../../constants' -import { generateNuclioLink } from '../../utils' export const generateMlrunScreens = params => params.projectName @@ -60,16 +59,8 @@ export const generateMlrunScreens = params => { label: 'Monitoring app', id: MONITORING_APP_PAGE }, { label: 'Jobs and workflows', id: 'jobs' }, { label: 'ML functions', id: 'functions' }, - { - label: 'Real-time functions', - id: 'Real-time functions', - link: generateNuclioLink(`/projects/${params.projectName}/functions`) - }, - { - label: 'API gateways', - id: 'API gateways', - link: generateNuclioLink(`/projects/${params.projectName}/api-gateways`) - }, + { label: 'Real-time functions', id: 'real-time-functions' }, + { label: 'API gateways', id: 'api-gateways' }, { label: 'Alerts', id: ALERTS_PAGE_PATH, diff --git a/src/common/Download/Download.jsx b/src/common/Download/Download.jsx index 2789e10b0c..145bd9d8e9 100644 --- a/src/common/Download/Download.jsx +++ b/src/common/Download/Download.jsx @@ -48,8 +48,8 @@ const Download = ({ () => disabled || !path || - (artifactLimits?.max_download_size && fileSize > artifactLimits.max_download_size), - [disabled, artifactLimits.max_download_size, fileSize, path] + (artifactLimits?.max_download_size && fileSize > artifactLimits?.max_download_size), + [disabled, artifactLimits?.max_download_size, fileSize, path] ) const downloadClassNames = classnames( 'download', diff --git a/src/components/Datasets/datasets.util.jsx b/src/components/Datasets/datasets.util.jsx index be254d15cf..3003f9d074 100644 --- a/src/components/Datasets/datasets.util.jsx +++ b/src/components/Datasets/datasets.util.jsx @@ -176,7 +176,7 @@ export const generateActionsMenu = ( disabled: !isTargetPathValid || datasetMin.size > - (frontendSpec.artifact_limits.max_download_size ?? ARTIFACT_MAX_DOWNLOAD_SIZE), + (frontendSpec.artifact_limits?.max_download_size ?? ARTIFACT_MAX_DOWNLOAD_SIZE), icon: , onClick: datasetMin => { getFullDataset(datasetMin).then(dataset => { diff --git a/src/components/Details/details.util.js b/src/components/Details/details.util.js index 5fa49e7b9f..5a67137aa3 100644 --- a/src/components/Details/details.util.js +++ b/src/components/Details/details.util.js @@ -314,8 +314,9 @@ export const generateRealTimePipelinesContent = selectedItem => { value: selectedItem.name, status: selectedItem.state.value, className: selectedItem.state.className, - link: generateNuclioLink(`/projects/${selectedItem.project}/functions/${nuclioFunctionName}`), - linkIsExternal: true + link: generateNuclioLink( + `/projects/${selectedItem.project}/real-time-functions/${nuclioFunctionName}` + ) }, childFunction: { value: selectedItem.childFunctions ?? selectedItem.function_refs ?? [], diff --git a/src/components/JobWizard/JobWizard.jsx b/src/components/JobWizard/JobWizard.jsx index 779234c451..ef83e7f5cc 100644 --- a/src/components/JobWizard/JobWizard.jsx +++ b/src/components/JobWizard/JobWizard.jsx @@ -363,14 +363,9 @@ const JobWizard = ({ mode, true ) - const credentials = jobRequestData.function?.metadata?.credentials - - delete jobRequestData.function.metadata - dispatch( editJob({ postData: { - credentials, scheduled_object: jobRequestData, cron_trigger: jobRequestData.schedule }, diff --git a/src/components/JobWizard/JobWizard.util.js b/src/components/JobWizard/JobWizard.util.js index e3e596b06e..0e6bb67f5c 100644 --- a/src/components/JobWizard/JobWizard.util.js +++ b/src/components/JobWizard/JobWizard.util.js @@ -42,10 +42,8 @@ import { EXISTING_IMAGE_SOURCE, FUNCTION_DEFAULT_HANDLER, HYPERPARAMETER_STRATEGY_STEP, - JOB_DEFAULT_OUTPUT_PATH, LIST_TUNING_STRATEGY, MAX_SELECTOR_CRITERIA, - PANEL_DEFAULT_ACCESS_KEY, PANEL_RERUN_MODE, PARAMETERS_FROM_FILE_VALUE, PARAMETERS_FROM_UI_VALUE, @@ -167,12 +165,8 @@ export const generateJobWizardData = ( }, [ADVANCED_STEP]: { inputPath: null, - outputPath: - currentProject?.spec?.artifact_path || - (frontendSpec.ce?.version && frontendSpec.default_artifact_path) || - JOB_DEFAULT_OUTPUT_PATH, - accessKey: true, - accessKeyInput: '', + outputPath: currentProject?.spec?.artifact_path || frontendSpec.default_artifact_path, + apiTokenInput: 'default', environmentVariablesTable: parseEnvironmentVariables(environmentVariables) // secretSourcesTable - currently not shown // secretSourcesTable: [] @@ -290,12 +284,7 @@ export const generateJobWizardDefaultData = ( [ADVANCED_STEP]: { inputPath: defaultData.task.spec.input_path, outputPath: defaultData.task.spec.output_path, - accessKey: - defaultData.function?.metadata?.credentials?.access_key === PANEL_DEFAULT_ACCESS_KEY, - accessKeyInput: - defaultData.function?.metadata?.credentials?.access_key === PANEL_DEFAULT_ACCESS_KEY - ? '' - : defaultData.function?.metadata?.credentials?.access_key, + apiTokenInput: defaultData.task.spec?.auth?.token_name ?? '', environmentVariablesTable: parseEnvironmentVariables(defaultData.function?.spec?.env ?? []) // secretSourcesTable - currently not shown // secretSourcesTable: parseSecretSources(defaultData.task.spec.secret_sources) @@ -1025,6 +1014,9 @@ export const generateJobRequestData = ( labels: convertChipsData(labels) }, spec: { + ...(formData[ADVANCED_STEP].apiTokenInput && { + auth: { token_name: formData[ADVANCED_STEP].apiTokenInput } + }), inputs: generateDataInputs(formData[DATA_INPUTS_STEP].dataInputsTable), parameters: generateParameters(formData[PARAMETERS_STEP].parametersTable), // secretSourcesTable - currently not shown @@ -1044,13 +1036,6 @@ export const generateJobRequestData = ( } }, function: { - metadata: { - credentials: { - access_key: formData[ADVANCED_STEP].accessKey - ? PANEL_DEFAULT_ACCESS_KEY - : formData[ADVANCED_STEP].accessKeyInput - } - }, spec: { image: formData[RUN_DETAILS_STEP].image?.imageSource === EXISTING_IMAGE_SOURCE diff --git a/src/components/JobWizard/JobWizardSteps/JobWizardAdvanced/JobWizardAdvanced.jsx b/src/components/JobWizard/JobWizardSteps/JobWizardAdvanced/JobWizardAdvanced.jsx index af40a04ab6..6b59fa9d4b 100644 --- a/src/components/JobWizard/JobWizardSteps/JobWizardAdvanced/JobWizardAdvanced.jsx +++ b/src/components/JobWizard/JobWizardSteps/JobWizardAdvanced/JobWizardAdvanced.jsx @@ -22,9 +22,9 @@ import PropTypes from 'prop-types' import { useSelector } from 'react-redux' import FormEnvironmentVariablesTable from '../../../../elements/FormEnvironmentVariablesTable/FormEnvironmentVariablesTable' -import { FormCheckBox, FormInput, FormKeyValueTable, FormOnChange } from 'igz-controls/components' +import { FormInput, FormKeyValueTable } from 'igz-controls/components' -import { ADVANCED_STEP } from '../../../../constants' +import { ADVANCED_STEP, API_TOKEN_TIP } from '../../../../constants' import { secretsKindOptions } from './JobWizardAdvanced.util' import './jobWizardAdvanced.scss' @@ -73,23 +73,17 @@ const JobWizardAdvanced = ({ formState, stepIsActive = false }) => { -
- {!frontendSpec.ce?.version && ( -
- -
- )} - {!formState.values?.[ADVANCED_STEP]?.accessKey && ( -
- + {!frontendSpec.ce?.version && ( +
+
+
- )} -
- {stepIsActive && ( - formState.form.change(`${ADVANCED_STEP}.accessKeyInput`, '')} - name={`${ADVANCED_STEP}.accessKey`} - /> +
)}
) diff --git a/src/components/JobWizard/JobWizardSteps/JobWizardAdvanced/jobWizardAdvanced.scss b/src/components/JobWizard/JobWizardSteps/JobWizardAdvanced/jobWizardAdvanced.scss index d4dbe92516..a9484d1ba1 100644 --- a/src/components/JobWizard/JobWizardSteps/JobWizardAdvanced/jobWizardAdvanced.scss +++ b/src/components/JobWizard/JobWizardSteps/JobWizardAdvanced/jobWizardAdvanced.scss @@ -1,5 +1,6 @@ .job-wizard__advanced { - .access-key-checkbox { - margin: 30px 10px 0 10px; + .api-token-field { + display: flex; + align-items: center; } } diff --git a/src/components/Jobs/jobs.util.js b/src/components/Jobs/jobs.util.js index deaeb4d74b..51612e2cc3 100644 --- a/src/components/Jobs/jobs.util.js +++ b/src/components/Jobs/jobs.util.js @@ -177,11 +177,6 @@ const generateEditableItem = (functionData, job) => { return { rerun_object: { function: { - metadata: { - credentials: { - access_key: functionData?.metadata?.credentials?.access_key ?? '' - } - }, spec: { env: functionData?.spec.env ?? [], resources: functionData?.spec.resources, @@ -200,6 +195,7 @@ const generateEditableItem = (functionData, job) => { project: job.project }, spec: { + ...(job.auth?.token_name && { auth: { token_name: job.auth.token_name } }), hyper_param_options: job.hyper_param_options, function: job.function, handler: job?.handler ?? '', diff --git a/src/components/LLMPrompts/llmPrompts.util.jsx b/src/components/LLMPrompts/llmPrompts.util.jsx index f10cdc8a23..c02e782cee 100644 --- a/src/components/LLMPrompts/llmPrompts.util.jsx +++ b/src/components/LLMPrompts/llmPrompts.util.jsx @@ -143,7 +143,7 @@ export const generateActionsMenu = ( disabled: !isTargetPathValid || llmPromptMin.size > - (frontendSpec.artifact_limits.max_download_size ?? ARTIFACT_MAX_DOWNLOAD_SIZE), + (frontendSpec.artifact_limits?.max_download_size ?? ARTIFACT_MAX_DOWNLOAD_SIZE), icon: , onClick: llmPromptMin => { getFullLLMPrompt(llmPromptMin).then(llmPrompt => { diff --git a/src/components/MonitoringApplicationsPage/MonitoringApplications/monitoringApplications.util.js b/src/components/MonitoringApplicationsPage/MonitoringApplications/monitoringApplications.util.js index 0030cddf53..8fbffbaa51 100644 --- a/src/components/MonitoringApplicationsPage/MonitoringApplications/monitoringApplications.util.js +++ b/src/components/MonitoringApplicationsPage/MonitoringApplications/monitoringApplications.util.js @@ -52,7 +52,9 @@ export const generateOperatingFunctionsTable = (functions, projectName) => { return { name: { value: func.name, - href: generateNuclioLink(`/projects/${projectName}/functions/${nuclioFunctionName}`), + href: generateNuclioLink( + `/projects/${projectName}/real-time-functions/${nuclioFunctionName}` + ), className: 'table-cell_big' }, status: { diff --git a/src/components/Project/ProjectOverview/ProjectOverview.util.jsx b/src/components/Project/ProjectOverview/ProjectOverview.util.jsx index ba917a7a29..c1b2ba5fb6 100644 --- a/src/components/Project/ProjectOverview/ProjectOverview.util.jsx +++ b/src/components/Project/ProjectOverview/ProjectOverview.util.jsx @@ -392,7 +392,7 @@ export const getInitialCards = (params, navigate, isDemoMode) => { icon: , label: 'Create real-time function', handleClick: () => ({ - path: generateNuclioLink(`${base_url}/create-function`), + path: generateNuclioLink(`${base_url}/real-time-functions/create-function`), externalLink: true }), tooltip: @@ -464,7 +464,7 @@ export const getInitialCards = (params, navigate, isDemoMode) => { { id: 'nuclioFunctions', handleClick: () => ({ - path: generateNuclioLink(`${base_url}/functions`), + path: generateNuclioLink(`${base_url}/real-time-functions`), externalLink: true }), label: 'Nuclio functions' diff --git a/src/components/Project/project.utils.jsx b/src/components/Project/project.utils.jsx index 81ba1719dc..94d4e5f4d5 100644 --- a/src/components/Project/project.utils.jsx +++ b/src/components/Project/project.utils.jsx @@ -114,7 +114,9 @@ export const generateCreateNewOptions = ( id: 'createRealTimeFunction', icon: , handler: () => { - const url = generateNuclioLink(`/projects/${params.projectName}/create-function`) + const url = generateNuclioLink( + `/projects/${params.projectName}/real-time-functions/create-function` + ) if (window.top && window.top !== window.self) { window.top.location.assign(url) diff --git a/src/components/ProjectSettings/ProjectSettings.jsx b/src/components/ProjectSettings/ProjectSettings.jsx index 5d75f70f82..280713dd15 100644 --- a/src/components/ProjectSettings/ProjectSettings.jsx +++ b/src/components/ProjectSettings/ProjectSettings.jsx @@ -42,11 +42,7 @@ import { } from '../../elements/MembersPopUp/membersReducer' import projectsIguazioApi from '../../api/projects-iguazio-api' import { DANGER_BUTTON, TERTIARY_BUTTON } from 'igz-controls/constants' -import { - COMPLETED_STATE, - PROJECTS_SETTINGS_MEMBERS_TAB, - PROJECTS_SETTINGS_SECRETS_TAB -} from '../../constants' +import { PROJECTS_SETTINGS_MEMBERS_TAB, PROJECTS_SETTINGS_SECRETS_TAB } from '../../constants' import { fetchProjects } from '../../reducers/projectReducer' import { onDeleteProject } from '../ProjectsPage/projects.util' import { setNotification } from 'igz-controls/reducers/notificationReducer' @@ -75,101 +71,76 @@ const ProjectSettings = () => { ) const userIsProjectOwner = useMemo(() => { - return membersState?.activeUser?.data?.id === membersState?.projectInfo?.owner.id + const activeUsername = membersState?.activeUser?.data?.attributes?.username + const ownerUsername = membersState?.projectInfo?.owner?.username + return Boolean(activeUsername && activeUsername === ownerUsername) }, [membersState]) + const userIsSystemAdmin = useMemo( + () => + membersState?.activeUser?.data?.attributes?.user_policies_collection?.has('System Admin') ?? + false, + [membersState?.activeUser?.data?.attributes?.user_policies_collection] + ) + const projectMembersTabIsShown = useMemo( () => isProjectMembersTabShown(projectMembershipIsEnabled, userIsProjectOwner, membersState), [userIsProjectOwner, membersState, projectMembershipIsEnabled] ) - const fetchProjectIdAndOwner = useCallback(() => { + const fetchProjectPolicies = useCallback(() => { return projectsIguazioApi - .getProjects({ - params: { 'filter[name]': params.projectName, include: 'owner' } + .getProjectPolicies(params.projectName) + .then(policiesResponse => generateMembers(policiesResponse, membersDispatch)) + .catch(error => { + showErrorNotification(dispatch, error, 'Failed to fetch project members') + throw error }) - .then(projects => { - const currentProjectInfo = projects.data - const currentProjectData = currentProjectInfo.data?.[0] - const projectId = currentProjectData?.id - const ownerId = currentProjectData?.relationships?.owner?.data?.id ?? '' - const ownerInfo = currentProjectInfo?.included.find(data => data.id === ownerId) - const { - attributes: { username = '', first_name: firstName = '', last_name: lastName = '' } = {} - } = ownerInfo ?? {} - const payload = { - id: projectId, - owner: { id: ownerId, username, firstName, lastName } - } + }, [dispatch, params.projectName]) - membersDispatch({ - type: membersActions.SET_PROJECT_INFO, - payload - }) - - return payload - }) - }, [params.projectName]) - - const fetchProjectMembers = useCallback( - (projectId, owner) => { - return projectsIguazioApi - .getProjectMembers(projectId) - .then(membersResponse => generateMembers(membersResponse, membersDispatch, owner)) - .catch(error => showErrorNotification(dispatch, error, 'Failed to fetch project members')) - }, - [dispatch] - ) - const fetchProjectMembersVisibility = project => { - projectsIguazioApi - .getProjectMembersVisibility(project) - .then(() => { - setProjectMembersIsShown(true) - }) - .catch(() => { - setProjectMembersIsShown(false) - }) - } const fetchActiveUser = () => { projectsIguazioApi.getActiveUser().then(response => { const activeUser = response.data - activeUser.data.attributes.user_policies_collection = new Set([ - ...activeUser.data.attributes.assigned_policies, - ...(activeUser.included?.reduce?.( - (policies, group) => [...policies, ...group.attributes.assigned_policies], - [] - ) || []) - ]) + const relationships = activeUser.relationships || [] + + const userGroupNames = new Set( + relationships + .filter(rel => rel['@type']?.includes('usergroup')) + .map(rel => rel.spec?.name) + .filter(Boolean) + ) + + const userPoliciesCollection = new Set( + relationships + .filter(rel => rel['@type']?.includes('policy.Policy')) + .map(rel => rel.spec?.displayName) + .filter(Boolean) + ) membersDispatch({ type: membersActions.SET_ACTIVE_USER, - payload: activeUser + payload: { + data: { + id: activeUser.metadata?.id, + attributes: { + username: activeUser.metadata?.username, + user_policies_collection: userPoliciesCollection, + user_group_names: userGroupNames + } + } + } }) }) } - const fetchProjectOwnerVisibility = project => { - projectsIguazioApi - .getProjectOwnerVisibility(project) - .then(() => { - setProjectOwnerIsShown(true) - }) - .catch(() => { - setProjectOwnerIsShown(false) - }) - } const fetchProjectUsersData = useCallback(() => { if (projectMembershipIsEnabled) { - fetchProjectOwnerVisibility(params.projectName) - fetchProjectIdAndOwner() - .then(({ id: projectId, owner }) => { - fetchActiveUser() - fetchProjectMembersVisibility(params.projectName) + fetchActiveUser() - return fetchProjectMembers(projectId, owner) - }) + fetchProjectPolicies() .catch(() => { setProjectMembersIsShown(false) + setProjectOwnerIsShown(false) }) .finally(() => membersDispatch({ @@ -177,62 +148,65 @@ const ProjectSettings = () => { }) ) } - }, [fetchProjectIdAndOwner, fetchProjectMembers, params.projectName, projectMembershipIsEnabled]) - - const changeMembersCallback = (jobId, userIsValid) => { - const fetchJob = () => { - projectsIguazioApi - .getProjectJob(jobId) - .then(response => { - if (response.data.data.attributes.state !== COMPLETED_STATE) { - setTimeout(fetchJob, 1000) - } else { - if (userIsValid) { - fetchProjectMembers(membersState.projectInfo.id, membersState.projectInfo.owner).then( - () => { - membersDispatch({ - type: membersActions.GET_PROJECT_USERS_DATA_END - }) - dispatch( - setNotification({ - status: 200, - id: Math.random(), - message: 'Members updated successfully' - }) - ) - } - ) - } else { - dispatch( - setNotification({ - status: 200, - id: Math.random(), - message: 'Members updated successfully' - }) - ) - navigate('/projects/') - } - } + }, [fetchProjectPolicies, projectMembershipIsEnabled]) + + useEffect(() => { + const activeUsername = membersState?.activeUser?.data?.attributes?.username + const projectId = membersState?.projectInfo?.id + + if (activeUsername && projectId) { + setProjectOwnerIsShown(userIsProjectOwner || userIsSystemAdmin) + setProjectMembersIsShown(projectMembersTabIsShown) + } + }, [ + membersState?.activeUser?.data?.attributes?.username, + membersState?.projectInfo?.id, + userIsProjectOwner, + userIsSystemAdmin, + projectMembersTabIsShown + ]) + + const changeMembersCallback = userIsStillMember => { + membersDispatch({ + type: membersActions.GET_PROJECT_USERS_DATA_BEGIN + }) + + if (userIsStillMember) { + fetchProjectPolicies() + .then(() => { + membersDispatch({ + type: membersActions.GET_PROJECT_USERS_DATA_END + }) + dispatch( + setNotification({ + status: 200, + id: Math.random(), + message: 'Members updated successfully' + }) + ) }) .catch(() => { membersDispatch({ type: membersActions.GET_PROJECT_USERS_DATA_END }) }) + } else { + dispatch( + setNotification({ + status: 200, + id: Math.random(), + message: 'Members updated successfully' + }) + ) + navigate('/projects/') } - - membersDispatch({ - type: membersActions.GET_PROJECT_USERS_DATA_BEGIN - }) - - fetchJob() } const changeOwnerCallback = () => { - const prevOwner = membersState.projectInfo.owner.id + const prevOwnerUsername = membersState.projectInfo.owner.username - return fetchProjectIdAndOwner().then(() => { - if (!membersState.users.some(member => member.id === prevOwner)) { + return fetchProjectPolicies().then(() => { + if (!membersState.members.some(member => member.id === prevOwnerUsername)) { navigate('/projects/') } }) diff --git a/src/components/ProjectSettings/projectSettings.util.jsx b/src/components/ProjectSettings/projectSettings.util.jsx index 81f7bd982d..e06e01f908 100644 --- a/src/components/ProjectSettings/projectSettings.util.jsx +++ b/src/components/ProjectSettings/projectSettings.util.jsx @@ -18,7 +18,6 @@ under the Apache 2.0 license is conditioned upon your compliance with such restriction. */ import React from 'react' -import { forEach, groupBy } from 'lodash' import { membersActions } from '../../elements/MembersPopUp/membersReducer' @@ -77,58 +76,52 @@ const addMember = (members, name, id, type, initialRole, role) => { }) } -export const generateMembers = (membersResponse, membersDispatch, owner) => { +export const generateMembers = (policiesResponse, membersDispatch) => { const members = [] - const { - project_authorization_role: projectAuthorizationRoles = [], - user: users = [], - user_group: userGroups = [] - } = groupBy(membersResponse.data.included, includeItem => { - return includeItem.type - }) + const memberSet = new Set() + const policies = policiesResponse.data.items || [] + membersDispatch({ type: membersActions.SET_PROJECT_AUTHORIZATION_ROLES, - payload: projectAuthorizationRoles - }) - membersDispatch({ - type: membersActions.SET_USERS, - payload: users - }) - membersDispatch({ - type: membersActions.SET_USER_GROUPS, - payload: userGroups + payload: policies }) - if (owner.id && !isOwnerInMembersList(owner.id, users)) { - addMember(members, owner.username, owner.id, USER_ROLE, OWNER_ROLE, OWNER_ROLE) - } + const ownerPolicy = policies.find(policy => policy.spec.displayName === OWNER_ROLE) + const ownerMembers = ownerPolicy?.status?.assignedMembers || [] + const ownerMemberIds = new Set(ownerMembers.map(m => m.id)) + const ownerUsername = ownerMembers[0]?.id || '' - projectAuthorizationRoles.forEach(role => { - if (role.relationships) { - forEach(role.relationships, relationData => { - relationData.data.forEach(identity => { - const identityList = identity.type === USER_ROLE ? users : userGroups - - const { - attributes: { name, username }, - id, - type - } = identityList.find(identityData => { - return identityData.id === identity.id - }) - - addMember( - members, - type === USER_ROLE ? username : name, - id, - type, - owner.id === id ? OWNER_ROLE : role.attributes.name, - owner.id === id ? OWNER_ROLE : role.attributes.name - ) - }) - }) + membersDispatch({ + type: membersActions.SET_PROJECT_INFO, + payload: { + id: policies[0]?.spec?.projectName || '', + owner: { id: ownerUsername, username: ownerUsername, firstName: '', lastName: '' } } }) + + policies.forEach(policy => { + const roleName = policy.spec.displayName + const assignedMembers = policy.status?.assignedMembers || [] + + assignedMembers.forEach(assignedMember => { + if (memberSet.has(assignedMember.id)) return + + const memberType = assignedMember.kind === USER_ROLE ? USER_ROLE : USER_GROUP_ROLE + const isOwner = ownerMemberIds.has(assignedMember.id) + const effectiveRole = isOwner ? OWNER_ROLE : roleName + + addMember( + members, + assignedMember.id, + assignedMember.id, + memberType, + effectiveRole, + effectiveRole + ) + memberSet.add(assignedMember.id) + }) + }) + membersDispatch({ type: membersActions.SET_MEMBERS_ORIGINAL, payload: members @@ -150,21 +143,14 @@ export const isProjectMembersTabShown = ( const userIsProjectSecurityAdmin = activeUser.data?.attributes?.user_policies_collection?.has('Project Security Admin') ?? false + const activeUsername = activeUser.data?.attributes?.username const userIsAdmin = members.some( member => member.role === ADMIN_ROLE && - (member.id === activeUser.data?.id || + (member.id === activeUsername || (member.type === USER_GROUP_ROLE && - activeUser.data?.relationships?.user_groups?.data?.some?.( - group => group.id === member.id - ))) + activeUser.data?.attributes?.user_group_names?.has(member.id))) ) return userIsProjectOwner || userIsAdmin || userIsProjectSecurityAdmin } - -const isOwnerInMembersList = (ownerId, membersList) => { - return membersList.some(member => { - return member.id === ownerId - }) -} diff --git a/src/components/ProjectsPage/projects.util.jsx b/src/components/ProjectsPage/projects.util.jsx index 70631352a9..eaa1853860 100644 --- a/src/components/ProjectsPage/projects.util.jsx +++ b/src/components/ProjectsPage/projects.util.jsx @@ -107,7 +107,7 @@ export const generateProjectActionsMenu = ( icon: , className: 'danger', hidden: - window.mlrunConfig.nuclioMode === 'enabled' && project.metadata.name === 'default', + window.mlrunConfig?.nuclioMode === 'enabled' && project?.metadata?.name === 'default', disabled: projectIsDeleting, onClick: deleteProject } diff --git a/src/components/RemoteNuclio/NuclioRemoteError.jsx b/src/components/RemoteNuclio/NuclioRemoteError.jsx new file mode 100644 index 0000000000..54d3f3ad8f --- /dev/null +++ b/src/components/RemoteNuclio/NuclioRemoteError.jsx @@ -0,0 +1,36 @@ +/* +Copyright 2019 Iguazio Systems Ltd. + +Licensed under the Apache License, Version 2.0 (the "License") with +an addition restriction as set forth herein. 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. + +In addition, you may not use the software for any purposes that are +illegal under applicable law, and the grant of the foregoing license +under the Apache 2.0 license is conditioned upon your compliance with +such restriction. +*/ +import React from 'react' + +const NuclioRemoteError = () => { + return ( +
+

Nuclio App Unavailable

+

+ The Nuclio remote app could not be loaded. + + Please check your network connection or make sure the remote is running. + +

+
+ ) +} + +export default NuclioRemoteError diff --git a/src/components/RemoteNuclio/RemoteNuclio.scss b/src/components/RemoteNuclio/RemoteNuclio.scss new file mode 100644 index 0000000000..93c94676d5 --- /dev/null +++ b/src/components/RemoteNuclio/RemoteNuclio.scss @@ -0,0 +1,58 @@ +.remote-nuclio-container { + width: 100%; + height: 100%; + + .nuclio-app-wrapper { + display: flex; + flex-direction: column; + width: 100%; + height: 100%; + min-height: 800px; + } + + .flex-center { + display: flex; + align-items: center; + justify-content: center; + height: 100%; + min-height: 400px; + } + + .remote-error-card { + display: flex; + flex-direction: column; + align-items: center; + max-width: 576px; + margin: 160px auto; + padding: 56px; + text-align: center; + color: #000000; + background-color: #f9fafb; + border-radius: 16px; + box-shadow: + 0 20px 25px -5px rgba(0, 0, 0, 0.1), + 0 10px 10px -5px rgba(0, 0, 0, 0.04); + transition: all 0.3s ease-in-out; + + .title { + margin-bottom: 20px; + font-weight: 600; + font-size: 24px; + line-height: 1.4; + } + + .description { + max-width: 440px; + color: #1f2937; + font-size: 16px; + line-height: 1.625; + + .subtext { + display: block; + margin-top: 4px; + font-size: 14px; + color: #4b5563; + } + } + } +} diff --git a/src/components/RemoteNuclio/RemoteNuclioRouteWrapper.jsx b/src/components/RemoteNuclio/RemoteNuclioRouteWrapper.jsx new file mode 100644 index 0000000000..93ec9b8d87 --- /dev/null +++ b/src/components/RemoteNuclio/RemoteNuclioRouteWrapper.jsx @@ -0,0 +1,107 @@ +/* +Copyright 2019 Iguazio Systems Ltd. + +Licensed under the Apache License, Version 2.0 (the "License") with +an addition restriction as set forth herein. 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. + +In addition, you may not use the software for any purposes that are +illegal under applicable law, and the grant of the foregoing license +under the Apache 2.0 license is conditioned upon your compliance with +such restriction. +*/ +import React, { useEffect, useState, Suspense } from 'react' +import { ErrorBoundary } from 'react-error-boundary' +import { ensureNuclioRemote, loadNuclioApp } from '../../utils/nuclio.remotes.utils' +import NuclioRemoteError from './NuclioRemoteError' +import { Loader } from 'igz-controls/components' + +import './RemoteNuclio.scss' +import Breadcrumbs from '../../common/Breadcrumbs/Breadcrumbs' + +const RemoteNuclioApp = React.lazy(() => loadNuclioApp()) + +const RemoteNuclioRouteWrapper = () => { + const [ready, setReady] = useState(false) + const [error, setError] = useState(false) + + useEffect(() => { + const origPushState = history.pushState + const origReplaceState = history.replaceState + + history.pushState = function (...args) { + origPushState.apply(this, args) + Promise.resolve().then(() => { + window.dispatchEvent(new PopStateEvent('popstate')) + }) + } + + history.replaceState = function (...args) { + origReplaceState.apply(this, args) + Promise.resolve().then(() => { + window.dispatchEvent(new PopStateEvent('popstate')) + }) + } + + return () => { + history.pushState = origPushState + history.replaceState = origReplaceState + } + }, []) + + useEffect(() => { + const init = async () => { + try { + await ensureNuclioRemote() + setReady(true) + } catch { + setError(true) + } + } + void init() + }, []) + + const renderContent = () => { + if (error) { + return + } + + if (!ready) { + return ( +
+ +
+ ) + } + + return ( + }> + + + + } + > +
+
+ +
+ +
+
+
+ ) + } + + return
{renderContent()}
+} + +export default RemoteNuclioRouteWrapper diff --git a/src/components/Workflow/workflow.util.js b/src/components/Workflow/workflow.util.js index c3285093b2..a6941afebc 100644 --- a/src/components/Workflow/workflow.util.js +++ b/src/components/Workflow/workflow.util.js @@ -31,7 +31,7 @@ import { JOBS_MONITORING_WORKFLOWS_TAB, WORKFLOW_TYPE_SKIPPED } from '../../constants' -import projectsIguazioApi from '../../api/projects-iguazio-api' +import { checkProjectWriteAccess } from '../../utils/projectAuth.util' import tasksApi from '../../api/tasks-api' import workflowsApi from '../../api/workflow-api' import { page } from '../Jobs/jobs.util' @@ -312,15 +312,10 @@ export const fetchMissingProjectsPermissions = async (projectNames, currentMap, await Promise.all( missingProjects.map(async projectName => { try { - await projectsIguazioApi.getProjectOwnerVisibility(projectName) - return [projectName, true] + const hasAccess = await checkProjectWriteAccess(projectName) + return [projectName, hasAccess] } catch { - try { - await projectsIguazioApi.getProjectWorkflowsUpdateAuthorization(projectName) - return [projectName, true] - } catch { - return [projectName, false] - } + return [projectName, false] } }) ) @@ -333,15 +328,12 @@ export const fetchMissingProjectsPermissions = async (projectNames, currentMap, export const fetchMissingProjectPermission = async (projectName, currentMap, dispatch) => { if (projectName in currentMap) return - const hasPermission = await projectsIguazioApi - .getProjectOwnerVisibility(projectName) - .then(() => true) - .catch(() => - projectsIguazioApi - .getProjectWorkflowsUpdateAuthorization(projectName) - .then(() => true) - .catch(() => false) - ) + let hasPermission = false + try { + hasPermission = await checkProjectWriteAccess(projectName) + } catch { + hasPermission = false + } dispatch(setAccessibleProjectsMap({ [projectName]: hasPermission })) } diff --git a/src/constants.js b/src/constants.js index 76adcbe734..adf47f206e 100644 --- a/src/constants.js +++ b/src/constants.js @@ -444,6 +444,8 @@ export const ENV_VARIABLE_TYPE_VALUE = 'value' export const ENV_VARIABLE_TYPE_SECRET = 'secret' export const PANEL_DEFAULT_ACCESS_KEY = '$generate' +export const API_TOKEN_TIP = + 'Get a valid API Token from your API tokens list (under Personal Settings)' /*=========== ML REACT FLOW =============*/ diff --git a/src/elements/BreadcrumbsDropdown/breadcrumbsDropdown.scss b/src/elements/BreadcrumbsDropdown/breadcrumbsDropdown.scss index dad7cf67ec..e450338b29 100644 --- a/src/elements/BreadcrumbsDropdown/breadcrumbsDropdown.scss +++ b/src/elements/BreadcrumbsDropdown/breadcrumbsDropdown.scss @@ -11,7 +11,7 @@ position: absolute; top: 35px; left: 14px; - z-index: 7; + z-index: 100; min-width: 130px; padding: 12px 0; font-size: 15px; diff --git a/src/elements/ChangeOwnerPopUp/ChangeOwnerPopUp.jsx b/src/elements/ChangeOwnerPopUp/ChangeOwnerPopUp.jsx index 3b045f4fcb..9fc0885d35 100644 --- a/src/elements/ChangeOwnerPopUp/ChangeOwnerPopUp.jsx +++ b/src/elements/ChangeOwnerPopUp/ChangeOwnerPopUp.jsx @@ -34,13 +34,10 @@ import { } from 'igz-controls/constants' import { deleteUnsafeHtml } from 'igz-controls/utils/string.util' import { getErrorMsg } from 'igz-controls/utils/common.util' -import { isIgzVersionCompatible } from '../../utils/isIgzVersionCompatible' import { setNotification } from 'igz-controls/reducers/notificationReducer' import { showErrorNotification } from 'igz-controls/utils/notification.util' import { useDetectOutsideClick } from 'igz-controls/hooks' -import { USER_ROLE } from '../../constants' - import SearchIcon from 'igz-controls/images/search.svg?react' import './changeOwnerPopUp.scss' @@ -76,23 +73,8 @@ const ChangeOwnerPopUp = ({ changeOwnerCallback, projectId }) => { const applyChanges = () => { if (newOwnerId) { - const projectData = { - data: { - type: 'project', - attributes: {}, - relationships: { - owner: { - data: { - id: newOwnerId, - type: USER_ROLE - } - } - } - } - } - projectsIguazioApi - .editProject(projectId, projectData) + .updateProjectOwner(projectId, newOwnerId) .then(changeOwnerCallback) .then(() => { dispatch( @@ -109,39 +91,25 @@ const ChangeOwnerPopUp = ({ changeOwnerCallback, projectId }) => { ? 'Missing edit permission for the project' : getErrorMsg(error, 'Failed to edit project data') - showErrorNotification(dispatch, error, '', customErrorMsg, () => applyChanges(newOwnerId)) + showErrorNotification(dispatch, error, '', customErrorMsg, () => applyChanges()) }) .finally(handleOnClose) } } const generateSuggestionList = async (memberName, resolve) => { - const params = { - 'filter[assigned_policies]': '[$contains_any]Developer,Project Admin', - 'page[size]': 200 - } - const requiredIgzVersion = '3.5.3' let formattedUsers = [] - if (isIgzVersionCompatible(requiredIgzVersion)) { - params['filter[username]'] = `[$contains_istr]${memberName}` - } - try { - const response = await projectsIguazioApi.getScrubbedUsers({ - params - }) - - const { - data: { data: users } - } = response + const response = await projectsIguazioApi.searchUsersMetadata(memberName) + const users = response.data.items || [] formattedUsers = users.map(user => { return { - name: `${user.attributes.first_name} ${user.attributes.last_name}`, - username: user.attributes.username, - label: `${user.attributes.first_name} ${user.attributes.last_name} (${user.attributes.username})`, - id: user.id, + name: `${user.firstName} ${user.lastName}`, + username: user.username, + label: `${user.firstName} ${user.lastName} (${user.username})`, + id: user.username, role: '' } }) diff --git a/src/elements/MembersPopUp/MembersPopUp.jsx b/src/elements/MembersPopUp/MembersPopUp.jsx index 6f757b0408..ad19e366ec 100644 --- a/src/elements/MembersPopUp/MembersPopUp.jsx +++ b/src/elements/MembersPopUp/MembersPopUp.jsx @@ -34,11 +34,10 @@ import projectsIguazioApi from '../../api/projects-iguazio-api' import { FORBIDDEN_ERROR_STATUS_CODE } from 'igz-controls/constants' import { getErrorMsg } from 'igz-controls/utils/common.util' import { getRoleOptions, initialNewMembersRole, DELETE_MODIFICATION } from './membersPopUp.util' -import { isIgzVersionCompatible } from '../../utils/isIgzVersionCompatible' import { membersActions } from './membersReducer' import { showErrorNotification } from 'igz-controls/utils/notification.util' -import { USER_GROUP_ROLE, USER_ROLE } from '../../constants' +import { OWNER_ROLE, USER_GROUP_ROLE, USER_ROLE } from '../../constants' import Add from 'igz-controls/images/add.svg?react' import Close from 'igz-controls/images/close.svg?react' @@ -97,79 +96,27 @@ const MembersPopUp = ({ changeMembersCallback, membersDispatch, membersState }) } const applyMembersChanges = () => { - const changesBody = { - data: { - attributes: { - metadata: { - project_ids: [membersData.projectInfo.id], - notify_by_email: notifyByEmail - }, - requests: [] - } - } - } - const rolesData = {} - const modifiedRoles = Array.from( - membersData.members.reduce((prevValue, member) => { - if (member.modification) { - prevValue.add(member.role) - - if (member.initialRole) { - prevValue.add(member.initialRole) - } - } - - return prevValue - }, new Set()) - ) - const groupedVisibleMembers = groupBy( - membersData.members.filter(member => member.modification !== DELETE_MODIFICATION), - item => item.role + const projectName = membersData.projectInfo.id + const visibleMembers = membersData.members.filter( + member => member.modification !== DELETE_MODIFICATION && member.role !== OWNER_ROLE ) + const groupedByRole = groupBy(visibleMembers, 'role') - membersState.projectAuthorizationRoles.forEach(roleData => { - rolesData[roleData.attributes.name] = roleData - }) + const membership = {} - changesBody.data.attributes.requests = modifiedRoles.map(modifiedRole => { - const membersCopy = groupedVisibleMembers[modifiedRole] ?? [] - - return { - method: 'put', - resource: `project_authorization_roles/${rolesData[modifiedRole].id}`, - body: { - data: { - type: rolesData[modifiedRole].type, - attributes: { - name: modifiedRole, - permissions: rolesData[modifiedRole].attributes.permissions - }, - relationships: { - project: { - data: { - type: 'project', - id: membersData.projectInfo.id - } - }, - principal_users: { - data: membersCopy - .filter(member => member.type === USER_ROLE) - .map(member => { - return { id: member.id, type: member.type } - }) - }, - principal_user_groups: { - data: membersCopy - .filter(member => member.type === 'user_group') - .map(member => { - return { id: member.id, type: member.type } - }) - } - } - } + membersState.projectAuthorizationRoles + .filter(policy => policy.spec.displayName !== OWNER_ROLE) + .forEach(policy => { + const roleName = policy.spec.displayName.toLowerCase() + membership[roleName] = { + values: (groupedByRole[policy.spec.displayName] || []).map(member => member.id) } - } - }) + }) + + const body = { + membership, + override: true + } membersDispatch({ type: membersActions.SET_MEMBERS, @@ -177,23 +124,18 @@ const MembersPopUp = ({ changeMembersCallback, membersDispatch, membersState }) }) projectsIguazioApi - .updateProjectMembers(changesBody) - .then(response => { - const validMember = membersData.members?.some( + .setProjectMembership(projectName, body) + .then(() => { + const activeUsername = membersData.activeUser.data?.attributes?.username + const userIsStillMember = membersData.members?.some( member => member.modification !== DELETE_MODIFICATION && - (member.id === membersData.activeUser.data?.id || + (member.id === activeUsername || (member.type === USER_GROUP_ROLE && - membersData.activeUser.data?.relationships?.user_groups?.data?.some?.( - group => group.id === member.id - ))) + membersData.activeUser.data?.attributes?.user_group_names?.has(member.id))) ) - const userIsProjectSecurityAdmin = - membersData.activeUser.data?.attributes?.user_policies_collection?.has( - 'Project Security Admin' - ) ?? false - changeMembersCallback(response.data.data.id, validMember || userIsProjectSecurityAdmin) + changeMembersCallback(userIsStillMember) }) .catch(error => { const customErrorMsg = @@ -225,59 +167,48 @@ const MembersPopUp = ({ changeMembersCallback, membersDispatch, membersState }) handleOnClose() } - const generateUsersSuggestionList = debounce(searchQuery => { - const requiredIgzVersion = '3.5.3' - let paramsScrubbedUsers = { - 'filter[username]': `[$match-i]^.*${searchQuery}.*$`, - 'page[size]': 200 - } - let paramsUserGroups = { 'filter[name]': `[$match-i]^.*${searchQuery}.*$`, 'page[size]': 200 } + const toSuggestionItem = (id, label, type) => { + const existingMember = membersData.members.find( + member => member.id === id && member.modification !== DELETE_MODIFICATION + ) - if (isIgzVersionCompatible(requiredIgzVersion)) { - paramsScrubbedUsers['filter[username]'] = `[$contains_istr]${searchQuery}` - paramsUserGroups['filter[name]'] = `[$contains_istr]${searchQuery}` + return { + label, + id, + subLabel: existingMember?.role ?? '', + disabled: Boolean(existingMember), + icon: + type === USER_ROLE ? ( + + + + ) : ( + + + + ), + ui: { type } } + } - const getUsersPromise = projectsIguazioApi.getScrubbedUsers({ - params: paramsScrubbedUsers - }) - const getUserGroupsPromise = projectsIguazioApi.getScrubbedUserGroups({ - params: paramsUserGroups - }) - const suggestionList = [] + const generateUsersSuggestionList = debounce(searchQuery => { + const getUsersPromise = projectsIguazioApi.searchUsersMetadata(searchQuery) + const getUserGroupsPromise = projectsIguazioApi.searchGroupsMetadata(searchQuery) Promise.all([getUsersPromise, getUserGroupsPromise]) - .then(response => { - response.forEach(identityResponse => { - identityResponse.data.data.forEach(identity => { - const existingMember = membersData.members.find( - member => member.id === identity.id && member.modification !== DELETE_MODIFICATION + .then(([usersResponse, groupsResponse]) => { + const users = usersResponse.data.items || [] + const groups = groupsResponse.data.items || [] + const suggestionList = [ + ...users.map(user => toSuggestionItem(user.username, user.username, USER_ROLE)), + ...groups.map(group => + toSuggestionItem( + group.groupId, + group.path.replace(/^\//, '') ?? group.groupId, + USER_GROUP_ROLE ) - - suggestionList.push({ - label: - identity.type === USER_ROLE - ? identity.attributes.username - : identity.attributes.name, - id: identity.id, - subLabel: existingMember?.role ?? '', - disabled: Boolean(existingMember), - icon: - identity.type === USER_ROLE ? ( - - - - ) : ( - - - - ), - ui: { - type: identity.type - } - }) - }) - }) + ) + ] setNewMembersSuggestionList(suggestionList) }) diff --git a/src/elements/MembersPopUp/membersReducer.js b/src/elements/MembersPopUp/membersReducer.js index 9a045856a9..d4f25f4a9b 100644 --- a/src/elements/MembersPopUp/membersReducer.js +++ b/src/elements/MembersPopUp/membersReducer.js @@ -23,8 +23,7 @@ import { groupBy } from 'lodash' * - activeUser : logged in user data * - projectInfo : additional information about the project such as ID and Owner of the project * (data is received from iguazio API). - * - users : the list of user members (original list from response) - * - useGroups : the list of user-group members (original list from response) + * - projectAuthorizationRoles: the list of project policies from /authorization/projects/{project}/policies * - membersOriginal : the list of users and user-groups that is used to revert changes * - members : the list of users and user-groups that is displayed in the table of the `Member` dialog (includes owner) * - groupedOriginalMembers : grouped members list by their role, which is used to display the number of @@ -43,8 +42,6 @@ export const initialMembersState = { } }, projectAuthorizationRoles: [], - users: [], - userGroups: [], membersOriginal: [], members: [], groupedOriginalMembers: [], @@ -60,9 +57,7 @@ export const membersActions = { SET_MEMBERS: 'SET_MEMBERS', SET_MEMBERS_ORIGINAL: 'SET_MEMBERS_ORIGINAL', SET_PROJECT_AUTHORIZATION_ROLES: 'SET_PROJECT_AUTHORIZATION_ROLES', - SET_PROJECT_INFO: 'SET_PROJECT_INFO', - SET_USERS: 'SET_USERS', - SET_USER_GROUPS: 'SET_USER_GROUPS' + SET_PROJECT_INFO: 'SET_PROJECT_INFO' } export const membersReducer = (state, { type, payload }) => { @@ -107,16 +102,6 @@ export const membersReducer = (state, { type, payload }) => { ...state, projectInfo: payload } - case membersActions.SET_USERS: - return { - ...state, - users: payload - } - case membersActions.SET_USER_GROUPS: - return { - ...state, - userGroups: payload - } default: return state } diff --git a/src/elements/ProjectFunctions/ProjectFunctions.jsx b/src/elements/ProjectFunctions/ProjectFunctions.jsx index ea8c54617a..2c3cad4d24 100644 --- a/src/elements/ProjectFunctions/ProjectFunctions.jsx +++ b/src/elements/ProjectFunctions/ProjectFunctions.jsx @@ -82,7 +82,7 @@ const ProjectFunctions = ({ nuclioStreamsAreEnabled, project }) => { label: 'Running', className: RUNNING_STATE, status: RUNNING_STATE, - href: generateNuclioLink(`/projects/${params.projectName}/functions`), + href: generateNuclioLink(`/projects/${params.projectName}/real-time-functions`), loading: nuclioStore.loading }, failed: { @@ -91,7 +91,7 @@ const ProjectFunctions = ({ nuclioStreamsAreEnabled, project }) => { label: 'Failed', status: FAILED_STATE, className: functionsFailed > 0 ? FAILED_STATE : RUNNING_STATE, - href: generateNuclioLink(`/projects/${params.projectName}/functions`), + href: generateNuclioLink(`/projects/${params.projectName}/real-time-functions`), loading: nuclioStore.loading }, apiGateways: { @@ -150,7 +150,7 @@ const ProjectFunctions = ({ nuclioStreamsAreEnabled, project }) => { func.metadata.name.slice(params.projectName.length + 1) : func.metadata.name, href: generateNuclioLink( - `/projects/${params.projectName}/functions/${func.metadata.name}` + `/projects/${params.projectName}/real-time-functions/${func.metadata.name}` ), className: 'table-cell_big' }, @@ -177,7 +177,7 @@ const ProjectFunctions = ({ nuclioStreamsAreEnabled, project }) => { loading: nuclioStore.loading }} footerLinkText="All real-time functions" - href={generateNuclioLink(`/projects/${params.projectName}/functions`)} + href={generateNuclioLink(`/projects/${params.projectName}/real-time-functions`)} statistics={functions} subTitle="Recent real-time functions" table={functionsTable} diff --git a/src/hooks/nuclioMode.hook.js b/src/hooks/nuclioMode.hook.js index 88c5fea164..0ba4da3fab 100644 --- a/src/hooks/nuclioMode.hook.js +++ b/src/hooks/nuclioMode.hook.js @@ -33,11 +33,11 @@ import { useLayoutEffect, useState } from 'react' */ export const useNuclioMode = () => { - const [mode, setMode] = useState(window.mlrunConfig.nuclioMode) + const [mode, setMode] = useState(window?.mlrunConfig?.nuclioMode) useLayoutEffect(() => { - if (mode !== window.mlrunConfig.nuclioMode) { - setMode(window.mlrunConfig.nuclioMode) + if (mode !== window?.mlrunConfig?.nuclioMode) { + setMode(window?.mlrunConfig?.nuclioMode) } }, [mode]) diff --git a/src/httpClient.js b/src/httpClient.js index a61f075bf2..47425cff32 100755 --- a/src/httpClient.js +++ b/src/httpClient.js @@ -29,6 +29,13 @@ const headers = { 'Cache-Control': 'no-cache' } +/** + * Resolve auth bridge provided by igz-ui (host). + * - Primary: injected via Module Federation + * - Fallback: host global (timing safety) + */ +export const getHostAuth = () => window.__mlrunHostServices?.auth || window.__igzAuth || null + // serialize a param with an array value as a repeated param, for example: // { label: ['host', 'owner=admin'] } => 'label=host&label=owner%3Dadmin' const paramsSerializer = params => qs.stringify(params, { arrayFormat: 'repeat' }) @@ -62,10 +69,61 @@ export const nuclioHttpClient = axios.create({ }) export const iguazioHttpClient = axios.create({ - baseURL: import.meta.env.MODE === 'production' ? '/api' : '/iguazio/api', + baseURL: import.meta.env.MODE === 'production' ? '/igz/api' : '/iguazio/api', headers }) +/** + * Module Federation auth: + * token injection and refresh are handled by the igz-ui host + */ + +const attachHostAuth = client => { + const auth = getHostAuth() + if (!auth) return + + client.interceptors.request.use(config => { + const token = auth.getAccessToken?.() + if (token) { + config.headers = config.headers ?? {} + config.headers.Authorization = `Bearer ${token}` + } + return config + }) + + client.interceptors.response.use( + res => res, + async err => { + const status = err?.response?.status + const req = err?.config + if (!req) throw err + + if (status === 401 && !req._retry) { + req._retry = true + + const token = await auth.refreshAccessToken?.() + if (!token) { + auth.redirectToLogin?.() + throw err + } + + req.headers = req.headers ?? {} + req.headers.Authorization = `Bearer ${token}` + + return client(req) + } + + throw err + } + ) +} + +attachHostAuth(mainHttpClient) +attachHostAuth(mainHttpClientV2) +attachHostAuth(functionTemplatesHttpClient) +attachHostAuth(nuclioHttpClient) +attachHostAuth(iguazioHttpClient) + const getAbortSignal = (controller, abortCallback, timeoutMs) => { let timeoutId = null const newController = new AbortController() diff --git a/src/index.jsx b/src/index.jsx index 9a141aaa0e..57839eb9e0 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -26,40 +26,23 @@ import ErrorBoundary from './components/ErrorBoundary/ErrorBoundary' import * as serviceWorker from './serviceWorker' import { Provider } from 'react-redux' import toolkitStore from './store/toolkitStore' -import { HTTP, HTTPS } from './constants' +import { loadRemoteConfig } from './loadRemoteConfig' if (!window.location.pathname.includes(import.meta.env.VITE_PUBLIC_URL)) { window.location.href = import.meta.env.VITE_PUBLIC_URL } -fetch(`${import.meta.env.VITE_PUBLIC_URL}/config.json`, { cache: 'no-store' }) - .then(response => response.json()) - .then(config => { - if (config.nuclioUiUrl) { - const mlrunProtocol = - config.nuclioUiUrl.startsWith(HTTP) || config.nuclioUiUrl.startsWith(HTTPS) - ? '' - : `${window.location.protocol}//` - - window.mlrunConfig = { - ...config, - nuclioUiUrl: `${mlrunProtocol}${config.nuclioUiUrl}` - } - } else { - window.mlrunConfig = config - } - }) - .then(() => { - const root = createRoot(document.getElementById('root')) - - root.render( - - - - - - ) - }) +loadRemoteConfig().then(() => { + const root = createRoot(document.getElementById('root')) + + root.render( + + + + + + ) +}) // If you want your app to work offline and load faster, you can change // unregister() to register() below. Note this comes with some pitfalls. diff --git a/src/layout/Navbar/navbar.util.jsx b/src/layout/Navbar/navbar.util.jsx index ea71474e0c..e52bbb02ad 100644 --- a/src/layout/Navbar/navbar.util.jsx +++ b/src/layout/Navbar/navbar.util.jsx @@ -123,7 +123,7 @@ export const getLinks = projectName => { icon: , id: 'real-time-functions', label: 'Real-time functions', - link: generateNuclioLink(`${pathname}/functions`), + link: generateNuclioLink(`${pathname}/real-time-functions`), externalLink: true }, { diff --git a/src/loadRemoteConfig.js b/src/loadRemoteConfig.js new file mode 100644 index 0000000000..febebc05d6 --- /dev/null +++ b/src/loadRemoteConfig.js @@ -0,0 +1,60 @@ +/* +Copyright 2019 Iguazio Systems Ltd. + +Licensed under the Apache License, Version 2.0 (the "License") with +an addition restriction as set forth herein. 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. + +In addition, you may not use the software for any purposes that are +illegal under applicable law, and the grant of the foregoing license +under the Apache 2.0 license is conditioned upon your compliance with +such restriction. +*/ + +import { HTTP, HTTPS } from './constants' + +const withProtocol = url => { + if (!url || url.startsWith(HTTP) || url.startsWith(HTTPS)) return url + return `${window.location.protocol}//${url.replace(/^\/+/, '')}` +} + +export const loadRemoteConfig = async (url, services = {}) => { + /** + * Store host-provided services (auth bridge from igz-ui) + */ + if (services && Object.keys(services).length > 0) { + window.__mlrunHostServices = services + } + + // Priority 1: Use config injected by the Host (igz-ui) + if (window.mlrunConfig && Object.keys(window.mlrunConfig).length > 0) { + window.mlrunConfig.nuclioUiUrl = withProtocol(window.mlrunConfig.nuclioUiUrl) + window.mlrunConfig.nuclioRemoteEntryUrl = withProtocol(window.mlrunConfig.nuclioRemoteEntryUrl) + return + } + + // Priority 2: Standalone Fallback (Local Dev) + try { + const configPath = `${url ?? import.meta.env.VITE_PUBLIC_URL}/config.json` + const response = await fetch(configPath, { cache: 'no-store' }) + if (!response.ok) throw new Error(response.status) + + const config = await response.json() + const uiUrl = withProtocol(config.nuclioUiUrl) + + window.mlrunConfig = { + ...config, + nuclioUiUrl: uiUrl, + nuclioRemoteEntryUrl: withProtocol(config.nuclioRemoteEntryUrl || uiUrl) + } + } catch (err) { + throw new Error('[mlrun-ui] Config load failed. Falling back to Host injection.', err) + } +} diff --git a/src/main.jsx b/src/main.jsx new file mode 100644 index 0000000000..30c0994e12 --- /dev/null +++ b/src/main.jsx @@ -0,0 +1,16 @@ +import App from './App' +import { Provider } from 'react-redux' +import store from './store/toolkitStore' +import ErrorBoundary from './components/ErrorBoundary/ErrorBoundary' + +const RemoteApp = () => { + return ( + + + + + + ) +} + +export default RemoteApp diff --git a/src/reducers/jobReducer.js b/src/reducers/jobReducer.js index 4d0a98bccb..ac3f18890c 100644 --- a/src/reducers/jobReducer.js +++ b/src/reducers/jobReducer.js @@ -65,11 +65,6 @@ const initialState = { } }, function: { - metadata: { - credentials: { - access_key: '' - } - }, spec: { env: [], node_selector: {}, diff --git a/src/utils/createApplicationContent.jsx b/src/utils/createApplicationContent.jsx index d8422185dc..301e12846d 100644 --- a/src/utils/createApplicationContent.jsx +++ b/src/utils/createApplicationContent.jsx @@ -105,7 +105,7 @@ export const createApplicationContent = (application, projectName) => { value: application.name, className: 'table-cell-2', getLink: () => - generateNuclioLink(`/projects/${projectName}/functions/${nuclioFunctionName}`), + generateNuclioLink(`/projects/${projectName}/real-time-functions/${nuclioFunctionName}`), linkIsExternal: true, showStatus: true } diff --git a/src/utils/createConsumerGroupsContent.js b/src/utils/createConsumerGroupsContent.js index db9ea22f54..8dc1accdce 100644 --- a/src/utils/createConsumerGroupsContent.js +++ b/src/utils/createConsumerGroupsContent.js @@ -46,7 +46,7 @@ const createConsumerGroupsContent = (content, params) => { value: contentItem.functionName, getLink: () => { return generateNuclioLink( - `/projects/${params.projectName}/functions/${contentItem.functionName}` + `/projects/${params.projectName}/real-time-functions/${contentItem.functionName}` ) }, linkIsExternal: true, diff --git a/src/utils/createRealTimePipelinesContent.js b/src/utils/createRealTimePipelinesContent.js index 5121112a3e..2143d53f59 100644 --- a/src/utils/createRealTimePipelinesContent.js +++ b/src/utils/createRealTimePipelinesContent.js @@ -52,7 +52,7 @@ const createRealTimePipelinesContent = (pipelines, projectName) => showTag: true, linkIsExternal: true, getLink: () => - generateNuclioLink(`/projects/${projectName}/functions/${nuclioFunctionName}`) + generateNuclioLink(`/projects/${projectName}/real-time-functions/${nuclioFunctionName}`) }, { id: `topology.${pipeline.ui.identifierUnique}`, diff --git a/src/utils/getArtifactPreview.jsx b/src/utils/getArtifactPreview.jsx index 6cdce1792c..1d77309949 100644 --- a/src/utils/getArtifactPreview.jsx +++ b/src/utils/getArtifactPreview.jsx @@ -165,7 +165,7 @@ export const fetchArtifactPreviewFromPath = async ( { data: { content: `The artifact is too large to ${ - fileSize > artifactLimits.max_download_size + fileSize > artifactLimits?.max_download_size ? `download. Go to ${path} to retrieve the data, or use mlrun api/sdk project.get_artifact('${artifact.db_key || artifact.name}').to_dataitem().get()` : 'preview, use the download option instead' }` diff --git a/src/utils/getJobLogs.util.js b/src/utils/getJobLogs.util.js index c78b971249..7d41761d6c 100644 --- a/src/utils/getJobLogs.util.js +++ b/src/utils/getJobLogs.util.js @@ -34,7 +34,7 @@ export const getJobLogs = ( dispatch(fetchJobLogs({ id, project, attempt, signal })) .unwrap() .then(res => { - const reader = res.body?.getReader() + const reader = res.data?.getReader() if (reader) { const decoder = new TextDecoder() diff --git a/src/utils/nuclio.remotes.utils.js b/src/utils/nuclio.remotes.utils.js new file mode 100644 index 0000000000..e67f2a3f3c --- /dev/null +++ b/src/utils/nuclio.remotes.utils.js @@ -0,0 +1,74 @@ +/* +Copyright 2019 Iguazio Systems Ltd. + +Licensed under the Apache License, Version 2.0 (the "License") with +an addition restriction as set forth herein. 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. + +In addition, you may not use the software for any purposes that are +illegal under applicable law, and the grant of the foregoing license +under the Apache 2.0 license is conditioned upon your compliance with +such restriction. +*/ +import { loadRemote, registerRemotes } from '@module-federation/runtime' + +let registerPromise = null + +const ensureNuclioRemote = async () => { + if (registerPromise) return registerPromise + + const config = window?.mlrunConfig + let remoteEntryUrl = config?.nuclioRemoteEntryUrl + + if (!remoteEntryUrl) { + throw new Error('[MF] Missing window.mlrunConfig.nuclioRemoteEntryUrl') + } + + /** + * FIX: Ensure the URL contains the /nuclio-ui proxy path. + * If it's just the domain, append the required prefix. + */ + + // TODO: Automate URL resolution (Local: remove suffix | Prod: append it) + if (!remoteEntryUrl.includes('/nuclio-ui')) { + // remoteEntryUrl = remoteEntryUrl.replace(/\/$/, ''); // Uncomment for Local + remoteEntryUrl = `${remoteEntryUrl.replace(/\/$/, '')}/nuclio-ui` + } + + registerPromise = (async () => { + try { + registerRemotes([ + { + name: 'nuclio', + entry: `${remoteEntryUrl.replace(/\/$/, '')}/remoteEntry.js`, + type: 'module', + shareScope: 'default' + } + ]) + } catch (err) { + registerPromise = null + throw err + } + })() + + return registerPromise +} + +const loadNuclioApp = async () => { + await ensureNuclioRemote() + const module = await loadRemote('nuclio/App') + + if (!module) throw new Error('[MF] Failed to load Nuclio application') + + const component = module.default?.default || module.default || module + return { default: component } +} + +export { ensureNuclioRemote, loadNuclioApp } diff --git a/src/utils/parseUri.js b/src/utils/parseUri.js index 26167d094d..2c8571a5a7 100644 --- a/src/utils/parseUri.js +++ b/src/utils/parseUri.js @@ -117,13 +117,11 @@ const generateLinkPath = (uri = '') => { } const generateNuclioLink = pathname => { - const linkUrl = new URL(`${window.mlrunConfig.nuclioUiUrl}${pathname}`) + const base = window.mlrunConfig?.nuclioUiUrl || window.location.origin - if (window.location.origin !== window.mlrunConfig.nuclioUiUrl) { - linkUrl.searchParams.set?.('origin', window.location.origin) - } + const cleanPath = pathname.startsWith('/') ? pathname : `/${pathname}` - return linkUrl.toString() + return new URL(`${base}${cleanPath}`).toString() } export { generateLinkPath, generateNuclioLink, parseUri, parseIdentifier } diff --git a/src/utils/projectAuth.util.js b/src/utils/projectAuth.util.js new file mode 100644 index 0000000000..19620891c6 --- /dev/null +++ b/src/utils/projectAuth.util.js @@ -0,0 +1,41 @@ +/* +Copyright 2019 Iguazio Systems Ltd. + +Licensed under the Apache License, Version 2.0 (the "License") with +an addition restriction as set forth herein. 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. + +In addition, you may not use the software for any purposes that are +illegal under applicable law, and the grant of the foregoing license +under the Apache 2.0 license is conditioned upon your compliance with +such restriction. +*/ +import projectsIguazioApi from '../api/projects-iguazio-api' + +const WRITE_ROLES = ['Owner', 'Admin', 'Editor'] + +export const getActiveUsername = async () => { + const response = await projectsIguazioApi.getActiveUser() + return response.data.metadata?.username +} + +export const checkProjectWriteAccess = async (projectName, activeUsername = null) => { + if (!activeUsername) { + activeUsername = await getActiveUsername() + } + + const policiesResponse = await projectsIguazioApi.getProjectPolicies(projectName) + const policies = policiesResponse.data.items || [] + return policies.some( + policy => + WRITE_ROLES.includes(policy.spec.displayName) && + policy.status?.assignedMembers?.some(member => member.id === activeUsername) + ) +} diff --git a/vite.config.mjs b/vite.config.mjs index 20b71de28d..2c886a4fd5 100644 --- a/vite.config.mjs +++ b/vite.config.mjs @@ -1,50 +1,40 @@ import commonjs from 'vite-plugin-commonjs' import eslint from 'vite-plugin-eslint' +import { federation } from '@module-federation/vite' import path from 'path' import react from '@vitejs/plugin-react-swc' import svgr from 'vite-plugin-svgr' import { defineConfig, loadEnv } from 'vite' -export default defineConfig(({ mode }) => { +import { loadMlrunProxyConfig } from './config/loadDevProxyConfig.js' +import { dependencies } from './package.json' + +export default defineConfig(async ({ mode }) => { const env = loadEnv(mode, path.resolve(process.cwd()), '') + const mlrunProxyConfig = await loadMlrunProxyConfig(mode) + + const federationPlugin = + env.VITE_FEDERATION === 'true' + ? federation({ + filename: 'remoteEntry.js', + name: 'mlrun', + exposes: { + './loadRemoteConfig': './src/loadRemoteConfig.js', + './app': './src/main.jsx' + }, + shared: { + react: { requiredVersion: dependencies.react, singleton: true }, + 'react-dom': { requiredVersion: dependencies['react-dom'], singleton: true } + } + }) + : null return { - plugins: [commonjs(), react(), svgr(), eslint({ failOnError: false })], + plugins: [commonjs(), react(), federationPlugin, svgr(), eslint({ failOnError: false })], base: env.NODE_ENV === 'production' ? env.VITE_PUBLIC_URL : '/', server: { proxy: { - '/api': env.VITE_MLRUN_API_URL - ? { - target: env.VITE_MLRUN_API_URL, - changeOrigin: true, - headers: { - Connection: 'keep-alive', - 'x-v3io-session-key': env.VITE_MLRUN_V3IO_ACCESS_KEY, - 'x-remote-user': 'admin' - } - } - : undefined, - '/nuclio': env.VITE_NUCLIO_API_URL - ? { - target: env.VITE_NUCLIO_API_URL, - changeOrigin: true, - rewrite: path => path.replace(/^\/nuclio/, '') - } - : undefined, - '/iguazio': env.VITE_IGUAZIO_API_URL - ? { - target: env.VITE_IGUAZIO_API_URL, - changeOrigin: true, - rewrite: path => path.replace(/^\/iguazio/, '') - } - : undefined, - '/function-catalog': env.VITE_FUNCTION_CATALOG_URL - ? { - target: env.VITE_FUNCTION_CATALOG_URL, - changeOrigin: true, - rewrite: path => path.replace(/^\/function-catalog/, '') - } - : undefined + ...mlrunProxyConfig(env) }, fs: { strict: false @@ -79,6 +69,7 @@ export default defineConfig(({ mode }) => { force: true }, build: { + target: 'esnext', sourcemap: true, outDir: 'build', chunkSizeWarningLimit: 3000 From d1275d07e7a129079d538f293009f56f72718d78 Mon Sep 17 00:00:00 2001 From: Taras Hlukhovetskyi Date: Fri, 3 Apr 2026 16:29:25 +0300 Subject: [PATCH 2/2] [mlrun-ui] Unify feature/ig4 and development for Standalone & Module Federation --- .claude/settings.local.json | 7 + Dockerfile | 33 +- Dockerfile.mf | 67 + diff_to_dev.md | 1031 ------------- nginx/nginx.conf.mf.tmpl | 30 + nginx/nginx.conf.tmpl | 73 +- nginx/run_nginx | 24 +- nginx/run_nginx.mf | 7 + package-lock.json | 1310 +++++------------ package.json | 3 +- public/config.json | 2 +- src/App.jsx | 131 +- src/api/projects-iguazio-api.js | 33 +- src/common/ActionsMenu/ActionsMenu.jsx | 0 src/common/Breadcrumbs/breadcrumbs.util.js | 20 +- src/components/Details/details.util.js | 9 +- src/components/JobWizard/JobWizard.jsx | 7 +- src/components/JobWizard/JobWizard.util.js | 38 +- .../JobWizardAdvanced/JobWizardAdvanced.jsx | 42 +- .../JobWizardAdvanced/jobWizardAdvanced.scss | 4 + src/components/Jobs/jobs.util.js | 10 +- .../monitoringApplications.util.js | 5 +- .../ProjectOverview/ProjectOverview.util.jsx | 12 +- src/components/Project/project.utils.jsx | 10 +- .../ProjectSettings/ProjectSettings.jsx | 321 +++- .../ProjectSettings/projectSettings.util.jsx | 105 +- src/constants.js | 4 + .../ChangeOwnerPopUp/ChangeOwnerPopUp.jsx | 131 +- src/elements/MembersPopUp/MembersPopUp.jsx | 188 ++- src/elements/MembersPopUp/membersReducer.js | 16 +- .../ProjectFunctions/ProjectFunctions.jsx | 26 +- src/httpClient.js | 7 +- src/layout/Navbar/navbar.util.jsx | 10 +- src/lazyWithRetry.js | 21 +- src/main.jsx | 4 + src/reducers/jobReducer.js | 10 +- src/utils/createApplicationContent.jsx | 8 +- src/utils/createConsumerGroupsContent.js | 5 +- src/utils/createRealTimePipelinesContent.js | 14 +- src/utils/nuclio.remotes.utils.js | 11 +- src/utils/parseUri.js | 13 +- src/utils/projectAuth.util.js | 32 +- 42 files changed, 1567 insertions(+), 2267 deletions(-) create mode 100644 .claude/settings.local.json create mode 100644 Dockerfile.mf delete mode 100644 diff_to_dev.md create mode 100644 nginx/nginx.conf.mf.tmpl create mode 100644 nginx/run_nginx.mf delete mode 100644 src/common/ActionsMenu/ActionsMenu.jsx diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000000..51882da855 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,7 @@ +{ + "permissions": { + "allow": [ + "Bash(grep -l \"$l\" build/assets/*.js)" + ] + } +} diff --git a/Dockerfile b/Dockerfile index 2d20731ac8..e38ba4add5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,16 +21,6 @@ COPY package*.json ./ RUN npm install COPY . . - -# build arg -ARG IS_MF=false - -RUN echo ">>> IS_MF ARG = $IS_MF" && \ - sed -i "/^VITE_FEDERATION=/d" .env.production && \ - echo "VITE_FEDERATION=$IS_MF" >> .env.production && \ - sed -i "s|^VITE_PUBLIC_URL=/mlrun|VITE_PUBLIC_URL=|" .env.production && \ - echo ">>> Final .env.production:" && grep '^VITE_' .env.production - RUN npm run build ARG COMMIT_HASH @@ -43,7 +33,6 @@ FROM gcr.io/iguazio/nginx-unprivileged:1.21-alpine AS production-stage ARG UID=101 ARG GID=101 -ARG IS_MF=false USER root RUN apk update --no-cache && apk upgrade --no-cache \ @@ -52,23 +41,25 @@ RUN apk update --no-cache && apk upgrade --no-cache \ USER $UID COPY --from=build-stage /app/build /usr/share/nginx/html -COPY --from=build-stage /app/.env.production /usr/share/nginx/html/ +COPY config.json.tmpl /usr/share/nginx/html/ COPY nginx/nginx.conf.tmpl /etc/nginx/conf.d/ COPY nginx/run_nginx /etc/nginx/ USER root -RUN if [ "$IS_MF" \ - = "true" ]; then \ - INDEX=/usr/share/nginx/html/index.html; \ - [ -f "$INDEX" ] && sed -i 's| /etc/nginx/resolvers.conf && /etc/nginx/run_nginx diff --git a/Dockerfile.mf b/Dockerfile.mf new file mode 100644 index 0000000000..3ef084252e --- /dev/null +++ b/Dockerfile.mf @@ -0,0 +1,67 @@ +# Copyright 2019 Iguazio +# +# 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. +# +# build stage — Module Federation remote +FROM quay.io/mlrun/node:20.19.2-slim AS build-stage + +WORKDIR /app + +COPY package*.json ./ +RUN npm install + +COPY . . + +RUN echo ">>> Building Module Federation remote" && \ + sed -i "/^VITE_FEDERATION=/d" .env.production && \ + echo "VITE_FEDERATION=true" >> .env.production && \ + sed -i "s|^VITE_PUBLIC_URL=/mlrun|VITE_PUBLIC_URL=|" .env.production && \ + echo ">>> Final .env.production:" && grep '^VITE_' .env.production + +RUN npm run build + +ARG COMMIT_HASH +ARG DATE +RUN echo "${COMMIT_HASH}" > ./build/COMMIT_HASH && \ + echo "${DATE}" > ./build/BUILD_DATE + +# production stage +FROM gcr.io/iguazio/nginx-unprivileged:1.21-alpine AS production-stage + +ARG UID=101 +ARG GID=101 + +USER root +RUN apk update --no-cache && apk upgrade --no-cache \ + && rm -f /etc/nginx/conf.d/default.conf + +USER $UID + +COPY --from=build-stage /app/build /usr/share/nginx/html +COPY --from=build-stage /app/.env.production /usr/share/nginx/html/ + +COPY nginx/nginx.conf.mf.tmpl /etc/nginx/conf.d/nginx.conf.tmpl +COPY nginx/run_nginx.mf /etc/nginx/run_nginx + +USER root +RUN INDEX=/usr/share/nginx/html/index.html && \ + [ -f "$INDEX" ] && sed -i 's| window.__mlrunHostServices?.auth || window.__igzAuth || null - -const attachHostAuth = client => { - const auth = getHostAuth() - if (!auth) return // ← no-op when not in MF host → SAFE in IGZ3 - - client.interceptors.request.use(...) // adds Bearer token - client.interceptors.response.use(...) // handles 401 → refresh token → retry -} - -// Applied to ALL clients: -attachHostAuth(mainHttpClient) -attachHostAuth(mainHttpClientV2) -attachHostAuth(iguazioHttpClient) -// ... -``` - -#### Why -- **Base URL**: In IGZ4, nginx doesn't proxy `/api`. The Iguazio backend API is served under `/igz/api` by the host. -- **attachHostAuth**: In IGZ4, auth is token-based (Bearer), provided by the host via `window.__mlrunHostServices.auth`. The host injects this before mounting the remote app. - -#### How to merge -- **`attachHostAuth()`** — **SAFE TO MERGE AS-IS**. The `if (!auth) return` guard means it does nothing in IGZ3 where `window.__mlrunHostServices` is not set. -- **Base URL** — **NEEDS CONDITIONAL**: - ```js - baseURL: import.meta.env.MODE === 'production' - ? (import.meta.env.VITE_FEDERATION === 'true' ? '/igz/api' : '/api') - : '/iguazio/api' - ``` - ---- - -### 2. Config Loading - -**Files:** `src/index.jsx`, `src/loadRemoteConfig.js`, `src/constants.js`, `src/main.jsx` - -#### What changed -`index.jsx` replaced inline `fetch('/config.json')` + protocol normalization with a call to `loadRemoteConfig()`: -```diff -- fetch(`${VITE_PUBLIC_URL}/config.json`) -- .then(config => { window.mlrunConfig = ...; normalizeProtocol... }) -- .then(() => render()) -+ loadRemoteConfig().then(() => render()) -``` - -`loadRemoteConfig.js` (new file) implements: -1. If host has already injected `window.mlrunConfig` → use it (IGZ4 path) -2. Otherwise → fetch `config.json` from disk (IGZ3 path, same as before) -3. Normalizes `nuclioUiUrl` protocol in both cases -4. Stores host services at `window.__mlrunHostServices` - -`main.jsx` (new file) is the MF entry point — exposes the React tree for the host to mount. - -`src/constants.js` adds `FORCE_REFRESH` (used by usePagination) and `API_TOKEN_TIP`. - -#### Why -In IGZ4, the Iguazio Dashboard (host) injects `window.mlrunConfig` before mounting the remote app. The standalone fetch is only a fallback for IGZ3 / local dev. - -#### How to merge -**SAFE TO MERGE AS-IS.** `loadRemoteConfig.js` already handles both cases. The fallback to `fetch('/config.json')` preserves IGZ3 behavior exactly. - ---- - -### 3. Iguazio Platform API - -**File:** `src/api/projects-iguazio-api.js` - -#### What changed — complete rewrite - -| Purpose | IGZ3 endpoint (removed) | IGZ4 endpoint (added) | -|---------|------------------------|----------------------| -| Get project + owner | `GET /projects?filter[name]=X&include=owner` | _(replaced by policies endpoint)_ | -| Get project members | `GET /projects/{id}?include=project_authorization_roles.principal_users,...` | _(replaced by policies endpoint)_ | -| Member + owner visibility | `GET /projects/__name__/{name}/authorization?action=...` | _(removed — policies response contains access info)_ | -| Poll member update job | `GET /jobs/{jobId}` | _(removed — new API is synchronous)_ | -| Update members | `POST /async_transactions` (batch job → async, poll for completion) | `PUT /v1/authorization/projects/{name}/roles` (sync, immediate) | -| Get active user | `GET /self` | `GET /v1/authentication/self?format=full` | -| Search users | `GET /scrubbed_users?filter[username][$match-i]=^.*query.*$` | `GET /v1/profile/search-users-metadata?searchTerm=query` | -| Search groups | `GET /scrubbed_user_groups?filter[name][$match-i]=^.*query.*$` | `GET /v1/profile/search-groups-metadata?searchTerm=query` | -| Get project policies | _(not present)_ | `GET /v1/authorization/projects/{name}/policies` | -| Update owner | `PUT /projects/{id}` (via editProject) | `PUT /v1/authorization/projects/{name}/owner` | - -#### Why -IGZ4 has a completely new authorization service (`/v1/authorization/`, `/v1/profile/`, `/v1/authentication/`). The data model also changed: -- **IGZ3**: Members are UUID-based; roles are objects with `principal_users` / `principal_user_groups` UUID arrays; updates are async batch jobs -- **IGZ4**: Members are username-based; roles map to arrays of usernames; updates are synchronous - -#### How to merge -Keep both API implementations, switch on `VITE_FEDERATION`: - -```js -// src/api/projects-iguazio-api.js -const igz3Api = { - editProject: ..., - getProjectJob: ..., - getProjects: ..., - getProjectMembers: ..., - getProjectMembersVisibility: ..., - getProjectOwnerVisibility: ..., - getProjectWorkflowsUpdateAuthorization: ..., - updateProjectMembers: ..., - getScrubbedUsers: ..., - getScrubbedUserGroups: ..., - getActiveUser: () => iguazioHttpClient.get('/self') -} - -const igz4Api = { - updateProjectOwner: ..., - getProjectPolicies: ..., - setProjectMembership: ..., - searchUsersMetadata: ..., - searchGroupsMetadata: ..., - getActiveUser: () => iguazioHttpClient.get('/v1/authentication/self', { params: { format: 'full' } }) -} - -export default import.meta.env.VITE_FEDERATION === 'true' ? igz4Api : igz3Api -``` - ---- - -### 4. Members State - -**File:** `src/elements/MembersPopUp/membersReducer.js` - -#### What changed -```diff -- users: [], // list of user members from IGZ3 API response -- userGroups: [], // list of user-group members from IGZ3 API response -+ // (removed — IGZ4 policies endpoint returns everything in one call) -``` -Also removed actions: `SET_USERS`, `SET_USER_GROUPS` - -#### Why -In IGZ3, members were fetched in two steps: project_authorization_roles + separate user/group lists → stored in `users` / `userGroups`. In IGZ4, `getProjectPolicies()` returns everything at once — no separate user/group arrays needed. - -#### How to merge -**Restore both fields for IGZ3 compatibility.** The IGZ3 `generateMembers()` function (called from `fetchProjectMembers()`) dispatches `SET_USERS` and `SET_USER_GROUPS`. Removing these breaks IGZ3 silently. - -```js -// Restore in initialMembersState: -users: [], -userGroups: [], - -// Restore in membersActions: -SET_USERS: 'SET_USERS', -SET_USER_GROUPS: 'SET_USER_GROUPS' - -// Restore reducer cases for SET_USERS, SET_USER_GROUPS -``` - -Alternatively, keep the IGZ4 version but verify that no IGZ3 code path dispatches or reads these fields. - ---- - -### 5. Project Settings - -**Files:** `src/components/ProjectSettings/ProjectSettings.jsx`, `src/components/ProjectSettings/projectSettings.util.jsx` - -#### What changed — two files, same underlying concern - -**ProjectSettings.jsx — four independent concerns** - -**A. Owner identity model** -```diff -// IGZ3: compared by UUID -- return membersState?.activeUser?.data?.id === membersState?.projectInfo?.owner.id - -// IGZ4: compared by username -+ const activeUsername = membersState?.activeUser?.data?.attributes?.username -+ const ownerUsername = membersState?.projectInfo?.owner?.username -+ return Boolean(activeUsername && activeUsername === ownerUsername) -``` - -**B. System Admin check (new for IGZ4)** -```diff -+ const userIsSystemAdmin = useMemo( -+ () => membersState?.activeUser?.data?.attributes?.user_policies_collection?.has('System Admin') ?? false, -+ [...] -+ ) -``` -In IGZ4, `igz-system-admin` users can change project owners even if they're not the project owner. - -**C. Data-fetching strategy replaced** -```diff -// IGZ3: two-step fetch (project + members separately) + job polling -- fetchProjectIdAndOwner() // GET /projects?include=owner -- .then(({ id, owner }) => { -- fetchActiveUser() // GET /self -- fetchProjectMembersVisibility() // GET /projects/__name__/X/authorization -- fetchProjectOwnerVisibility() // GET /projects/__name__/X/authorization -- fetchProjectMembers(id, owner) // GET /projects/{id}?include=roles -- }) - -// IGZ4: single call -+ fetchActiveUser() // GET /v1/authentication/self?format=full -+ fetchProjectPolicies() // GET /v1/authorization/projects/{name}/policies -``` - -**D. After member update** -```diff -// IGZ3: async job polling -- changeMembersCallback = (jobId, userIsValid) => { -- const fetchJob = () => { -- getProjectJob(jobId).then(response => { -- if (response.data.data.attributes.state !== COMPLETED_STATE) { -- setTimeout(fetchJob, 1000) // poll every second -- } else { -- fetchProjectMembers(...) -- } -- }) -- } -- fetchJob() -- } - -// IGZ4: immediate refetch -+ changeMembersCallback = (userIsStillMember) => { -+ if (userIsStillMember) { -+ fetchProjectPolicies() -+ } else { -+ navigate('/projects/') -+ } -+ } -``` - -**projectSettings.util.jsx — `generateMembers()` and `isProjectMembersTabShown()` rewritten** - -`generateMembers()` in IGZ3 parsed a JSONAPI response with `included` arrays (roles, users, user_groups keyed by UUID). In IGZ4 it parses a policies response where members are identified by username: - -```diff -// IGZ3 -- export const generateMembers = (membersResponse, membersDispatch, owner) => { -- const { project_authorization_role, user, user_group } = groupBy(membersResponse.data.included, ...) -- // dispatch SET_USERS, SET_USER_GROUPS -- // match member UUIDs against user/group lists to build display names - -// IGZ4 -+ export const generateMembers = (policiesResponse, membersDispatch) => { -+ const policies = policiesResponse.data.items || [] -+ // dispatch SET_PROJECT_AUTHORIZATION_ROLES with policies -+ // dispatch SET_PROJECT_INFO with owner derived from OWNER_ROLE policy -+ // build members list directly from assignedMembers[].id (username) -``` - -`isProjectMembersTabShown()` now uses `username` and `user_group_names` Set instead of UUID-based relationships: -```diff -- member.id === activeUser.data?.id || -- activeUser.data?.relationships?.user_groups?.data?.some?.(group => group.id === member.id) -+ member.id === activeUsername || -+ activeUser.data?.attributes?.user_group_names?.has(member.id) -``` - -#### Why -The IGZ4 `/v1/authorization/projects/{name}/policies` endpoint returns all data in one call: active policies, members per role, and owner. No need for separate member/owner visibility checks. Member updates are synchronous (no job polling). The `igz-system-admin` role is a new IGZ4 concept. - -#### How to merge -Wrap the two data-fetching strategies and both utility functions in a conditional: - -```js -// ProjectSettings.jsx -const fetchProjectUsersData = useCallback(() => { - if (projectMembershipIsEnabled) { - if (import.meta.env.VITE_FEDERATION === 'true') { - // IGZ4 path - fetchActiveUser() - fetchProjectPolicies().catch(...) - } else { - // IGZ3 path - fetchProjectOwnerVisibility(params.projectName) - fetchProjectIdAndOwner() - .then(({ id, owner }) => { - fetchActiveUser() - fetchProjectMembersVisibility(params.projectName) - return fetchProjectMembers(id, owner) - }) - .catch(...) - } - } -}, [...]) -``` - -```js -// projectSettings.util.jsx -export const generateMembers = (response, membersDispatch, owner) => { - if (import.meta.env.VITE_FEDERATION === 'true') { - // IGZ4 path — parse policies response - } else { - // IGZ3 path — parse JSONAPI included arrays - } -} -``` - -The `userIsSystemAdmin` check and `changeMembersCallback` also need the same branching. - ---- - -### 6. Members Pop-Up - -**File:** `src/elements/MembersPopUp/MembersPopUp.jsx` - -#### What changed — three independent concerns - -**A. User/group search API and response shape** -```diff -// IGZ3 -- const getUsersPromise = projectsIguazioApi.getScrubbedUsers({ -- params: { 'filter[username]': '[$match-i]^.*query.*$', 'page[size]': 200 } -- }) -// Response: { data: { data: [{ id: 'uuid', type: 'user', attributes: { username, ... } }] } } - -// IGZ4 -+ const getUsersPromise = projectsIguazioApi.searchUsersMetadata(searchQuery) -// Response: { data: { items: [{ username: 'john', ... }] } } - -// IGZ3 groups -- const getUserGroupsPromise = projectsIguazioApi.getScrubbedUserGroups({ -- params: { 'filter[name]': '[$match-i]^.*query.*$', 'page[size]': 200 } -- }) -// Response: { data: { data: [{ id: 'uuid', type: 'user_group', attributes: { name: '/group/path' } }] } } - -// IGZ4 groups -+ const getUserGroupsPromise = projectsIguazioApi.searchGroupsMetadata(searchQuery) -// Response: { data: { items: [{ groupId: 'id', path: '/group/path' }] } } -``` - -**B. Member identity: UUID → username** -```diff -// IGZ3: id is UUID, label is username -- { id: identity.id, label: identity.attributes.username } // user -- { id: identity.id, label: identity.attributes.name } // group - -// IGZ4: id IS the username/groupId -+ { id: user.username, label: user.username } // user -+ { id: group.groupId, label: group.path.replace(/^\//, '') ?? group.groupId } // group -``` - -**C. Applying member changes: async batch → sync call** -```diff -// IGZ3: complex async_transactions batch -- const changesBody = { data: { attributes: { ... }, requests: modifiedRoles.map(...) } } -- projectsIguazioApi.updateProjectMembers(changesBody) -- .then(response => { -- changeMembersCallback(response.data.data.id, validMember || userIsProjectSecurityAdmin) -- }) - -// IGZ4: simple sync call -+ projectsIguazioApi.setProjectMembership(projectName, { membership, override: true }) -+ .then(() => { -+ const userIsStillMember = membersData.members?.some(member => ...) -+ changeMembersCallback(userIsStillMember) -+ }) -``` - -Also removed: `isIgzVersionCompatible` version check (was switching filter syntax). Removed: `Project Security Admin` bypass for self-removal redirect. - -#### Why -New IGZ4 APIs return different shapes. Identity is username-based not UUID-based. Member update is now synchronous and atomic (override replaces entire role membership). `Project Security Admin` no longer exists in IGZ4 auth model. - -#### How to merge -Wrap the three concerns in `VITE_FEDERATION` conditionals within `generateUsersSuggestionList()` and `applyMembersChanges()`. The rest of the component (UI rendering, state management) is the same. - ---- - -### 7. Job Wizard: Access Key → API Token - -**Files:** `src/components/JobWizard/JobWizardSteps/JobWizardAdvanced/JobWizardAdvanced.jsx`, `src/components/JobWizard/JobWizard.util.js`, `src/components/JobWizard/JobWizard.jsx`, `src/components/Jobs/jobs.util.js`, `src/elements/PanelCredentialsAccessKey/PanelCredentialsAccessKey.jsx`, `src/reducers/jobReducer.js` - -#### What changed - -**A. UI in `JobWizardAdvanced.jsx`** -```diff -// IGZ3: checkbox + optional text input -- -- {!formState.values[ADVANCED_STEP].accessKey && ( -- -- )} - -// IGZ4: single text input (only shown for non-CE) -+ {!frontendSpec.ce?.version && ( -+ -+ )} -``` - -**B. Default form data in `JobWizard.util.js`** -```diff -// IGZ3 defaults -- accessKey: true, -- accessKeyInput: '', -- outputPath: currentProject?.spec?.artifact_path -- || (frontendSpec.ce?.version && frontendSpec.default_artifact_path) -- || JOB_DEFAULT_OUTPUT_PATH - -// IGZ4 defaults -+ apiTokenInput: 'default', -+ outputPath: currentProject?.spec?.artifact_path || frontendSpec.default_artifact_path -``` - -**C. Job request body in `JobWizard.util.js`** -```diff -// IGZ3: access key in function metadata -- function: { -- metadata: { -- credentials: { access_key: formData.accessKey ? '$generate' : formData.accessKeyInput } -- } -- } - -// IGZ4: token name in task spec -+ task: { -+ spec: { -+ ...(formData.apiTokenInput && { auth: { token_name: formData.apiTokenInput } }), -+ ... -+ } -+ } -``` - -**D. `JobWizard.jsx` — edit job cleaned up** -```diff -- const credentials = jobRequestData.function?.metadata?.credentials -- delete jobRequestData.function.metadata - dispatch(editJob({ - postData: { -- credentials, - scheduled_object: jobRequestData, - ... - } - })) -``` - -**E. Rerun job in `Jobs/jobs.util.js`** -```diff -- function: { metadata: { credentials: { access_key: functionData?.metadata?.credentials?.access_key } } } -+ task: { spec: { ...(job.auth?.token_name && { auth: { token_name: job.auth.token_name } }), ... } } -``` - -**F. `jobReducer.js` — initial state** -```diff - function: { -- metadata: { -- credentials: { -- access_key: '' -- } -- }, - spec: { ... } - } -``` - -#### Why -IGZ4 uses named API tokens (stored in the platform) instead of v3io access keys. The job spec field changed from `function.metadata.credentials.access_key` to `task.spec.auth.token_name`. - -#### How to merge -The user says MLRun API is the same. The question is: **does IGZ3 MLRun support `task.spec.auth.token_name`?** -- If **yes**: can merge as-is. The `auth.token_name` only appears if the field is filled (`...formData.apiTokenInput && {...}`). -- If **no**: wrap the request body generation in `VITE_FEDERATION`: - ```js - function: import.meta.env.VITE_FEDERATION === 'true' ? {} : { - metadata: { credentials: { access_key: formData.accessKey ? '$generate' : formData.accessKeyInput } } - }, - task: { spec: { - ...(import.meta.env.VITE_FEDERATION === 'true' && formData.apiTokenInput - ? { auth: { token_name: formData.apiTokenInput } } - : {}), - ... - }} - ``` - -The `outputPath` change also differs: IGZ3 uses `JOB_DEFAULT_OUTPUT_PATH` as fallback (v3io path), IGZ4 uses `frontendSpec.default_artifact_path`. This is a **breaking change for IGZ3** if `default_artifact_path` is undefined or points to wrong location. Needs conditional: -```js -outputPath: currentProject?.spec?.artifact_path - || (import.meta.env.VITE_FEDERATION === 'true' - ? frontendSpec.default_artifact_path - : (frontendSpec.ce?.version && frontendSpec.default_artifact_path) || JOB_DEFAULT_OUTPUT_PATH) -``` - ---- - -### 8. Nuclio Navigation - -**Files:** `src/App.jsx`, `src/common/Breadcrumbs/breadcrumbs.util.js`, `src/layout/Navbar/navbar.util.jsx`, `src/utils/parseUri.js`, `src/utils/createRealTimePipelinesContent.js`, `src/components/Details/details.util.js`, `src/elements/ProjectFunctions/ProjectFunctions.jsx`, `src/elements/SectionTable/SectionTable.jsx`, `src/components/Project/project.utils.jsx`, `src/components/RemoteNuclio/*` - -#### What changed - -**A. `src/App.jsx` — new internal routes for Nuclio** -```diff -+ -+ } /> -+ } /> -+ } /> -+ } /> -+ -- } /> -``` - -**B. `src/common/Breadcrumbs/breadcrumbs.util.js` — external links removed** -```diff -// IGZ3: breadcrumbs had external links to Nuclio UI -- { label: 'Real-time functions', id: 'Real-time functions', -- link: generateNuclioLink(`/projects/${params.projectName}/functions`) } - -// IGZ4: breadcrumbs use internal route IDs (React Router handles navigation) -+ { label: 'Real-time functions', id: 'real-time-functions' } -``` - -**C. `src/layout/Navbar/navbar.util.jsx` — path updated** -```diff -- link: generateNuclioLink(`${pathname}/functions`) -+ link: generateNuclioLink(`${pathname}/real-time-functions`) -``` - -**D. `src/utils/parseUri.js` — `generateNuclioLink()` simplified** -```diff -// IGZ3: sets `?origin=...` param so Nuclio UI knows where mlrun UI is hosted -- const linkUrl = new URL(`${window.mlrunConfig.nuclioUiUrl}${pathname}`) -- if (window.location.origin !== window.mlrunConfig.nuclioUiUrl) { -- linkUrl.searchParams.set?.('origin', window.location.origin) -- } - -// IGZ4: simple URL construction -+ const base = window.mlrunConfig?.nuclioUiUrl || window.location.origin -+ return new URL(`${base}${cleanPath}`).toString() -``` - -**E. `src/elements/ProjectFunctions/ProjectFunctions.jsx` — links + name slicing** -```diff -// Nuclio function links: /functions/ → /real-time-functions/ -- href: generateNuclioLink(`/projects/${params.projectName}/functions/${func.metadata.name}`) -+ href: generateNuclioLink(`/projects/${params.projectName}/real-time-functions/${func.metadata.name}`) - -// MLRun-generated function names (prefixed with project name) are sliced: -+ value: has(func.metadata.labels || {}, 'mlrun/class') -+ ? func.metadata.name.slice(params.projectName.length + 1) -+ : func.metadata.name - -// Status via getNuclioFuncState() utility instead of inline logic -- value: func?.status?.state === FUNCTION_READY_STATE && !func?.spec?.disable ? 'Running' : ... -+ value: getNuclioFuncState(func) -``` - -**F. `src/elements/SectionTable/SectionTable.jsx` — `params` prop removed** - -The name-slicing logic (removing project prefix from function names) was previously done in `SectionTable.jsx` using the `params` prop. This was moved upstream to `ProjectFunctions.jsx`, so `SectionTable` no longer needs `params`: -```diff -- const SectionTable = ({ loading = false, params, table }) => { -+ const SectionTable = ({ loading = false, table }) => { - // No longer extracts project name prefix — value is already sliced in ProjectFunctions.jsx -``` - -**G. `src/components/Project/project.utils.jsx` — "Create real-time function" link** -```diff -// IGZ3: direct window.location.assign -- handler: () => window.location.assign(generateNuclioLink(`/projects/${params.projectName}/create-function`)) - -// IGZ4: path updated + window.top for MF iframe context -+ handler: () => { -+ const url = generateNuclioLink(`/projects/${params.projectName}/real-time-functions/create-function`) -+ if (window.top && window.top !== window.self) { -+ window.top.location.assign(url) -+ } else { -+ window.location.assign(url) -+ } -+ } -``` - -#### Why -In IGZ4, Nuclio is loaded as a remote MF app within the same React Router. So "Real-time functions" navigates to an internal route (`/projects/:name/real-time-functions/*`) which loads `RemoteNuclioRouteWrapper`. In IGZ3, they are external links to the standalone Nuclio UI. The `window.top` navigation is needed because the MF app runs inside an iframe provided by the host. - -#### How to merge - -**App.jsx routes**: These new routes are only useful in IGZ4. In IGZ3, navigating to `real-time-functions/*` would load `RemoteNuclioRouteWrapper` which tries to load the Nuclio MF remote — this will fail. Options: -- Wrap in `VITE_FEDERATION` check -- Or make `RemoteNuclioRouteWrapper` redirect to external Nuclio link when `VITE_FEDERATION !== 'true'` - -**Breadcrumbs**: Must restore external links for IGZ3: -```js -import.meta.env.VITE_FEDERATION === 'true' - ? { label: 'Real-time functions', id: 'real-time-functions' } - : { label: 'Real-time functions', id: 'Real-time functions', - link: generateNuclioLink(`/projects/${params.projectName}/functions`) } -``` - -**`ProjectFunctions.jsx` link path**: `/real-time-functions/` breaks IGZ3 if Nuclio UI doesn't recognize that path. Needs: -```js -href: generateNuclioLink( - `/projects/${params.projectName}/${import.meta.env.VITE_FEDERATION === 'true' ? 'real-time-functions' : 'functions'}/${func.metadata.name}` -) -``` - -**`project.utils.jsx`**: The `window.top` fallback is safe; the path change needs the same conditional as above. - -**`generateNuclioLink()`**: The removal of `origin` param might break IGZ3 if the Nuclio UI requires it to function correctly. Needs clarification: does IGZ3's Nuclio UI use `?origin` to redirect back? - -**`SectionTable.jsx`**: Safe to merge — the name-slicing responsibility was just moved upstream, behaviour is identical. - ---- - -### 9. Real-Time Pipelines - -**Files:** `src/components/ModelsPage/RealTimePipelines/RealTimePipelines.jsx`, `src/utils/createRealTimePipelinesContent.js`, `src/components/Details/details.util.js`, `src/elements/DetailsInfoItem/DetailsInfoItem.jsx`, `src/components/ModelsPage/RealTimePipelines/RealTimePipelinesCounters.jsx` - -#### How to merge -Accept all changes from development ---- - -### 10. Build/Deploy Infrastructure - -**Files:** `nginx/nginx.conf.tmpl`, `nginx/run_nginx`, `Dockerfile`, `.env.production` - -#### `nginx/nginx.conf.tmpl` — complete rewrite -| | IGZ3 (dev branch) | IGZ4 (feature/ig4) | -|---|---|---| -| Proxy rules | `/api`, `/nuclio`, `/function-catalog` with v3io header forwarding | **None** | -| DNS resolver | `include resolvers.conf` | None needed | -| v3io key map | `map $http_x_v3io_session_key ...` | Removed | -| Fallback | `/index.html` | `/landing.html` | -| CORS | None | `Access-Control-Allow-Origin: *` (required for cross-origin JS module loading) | -| Gzip | No | Yes | -| Cache-busting | No | `Cache-Control: no-cache` on `.js`/`.json` | - -**Merge plan**: Create `nginx/nginx.conf.mf.tmpl` (IGZ4 content, current file). Restore original content in `nginx/nginx.conf.tmpl` (IGZ3). Select in `run_nginx` via `IS_MF`. - -#### `nginx/run_nginx` — complete rewrite -| IGZ3 | IGZ4 | -|------|------| -| `envsubst` with 8 env vars into `nginx.conf.tmpl` | `cp` (no env vars — no proxy config) | -| `envsubst` config vars into `config.json.tmpl` | Removed — no `config.json.tmpl` | -| DNS: `echo resolver $(awk nameserver /etc/resolv.conf) > resolvers.conf` | Removed — no proxy needs DNS | -| `nginx -g 'daemon off;'` | `exec nginx -g 'daemon off;'` | - -**Merge plan**: Add `IS_MF` branch: -```sh -if [ "$IS_MF" = "true" ]; then - cp /etc/nginx/conf.d/nginx.conf.mf.tmpl /etc/nginx/conf.d/nginx.conf -else - echo resolver $(awk 'BEGIN{ORS=" "} $1=="nameserver" {print $2}' /etc/resolv.conf) ";" > /etc/nginx/resolvers.conf - envsubst '${MLRUN_API_PROXY_URL} ${MLRUN_V3IO_ACCESS_KEY} ...' \ - < /etc/nginx/conf.d/nginx.conf.tmpl > /etc/nginx/conf.d/nginx.conf - envsubst '${MLRUN_BETA_MODE} ${MLRUN_NUCLIO_MODE} ${MLRUN_NUCLIO_UI_URL}' \ - < /usr/share/nginx/html/config.json.tmpl > /usr/share/nginx/html/config.json -fi -exec nginx -g 'daemon off;' -``` - -#### `Dockerfile` -- `IS_MF=false` default already added ✅ -- Base image `20-alpine` → `20.19.2-slim`: coordinate with DevOps -- `CMD` simplified (DNS resolver setup moved to `run_nginx`) -- `COPY config.json.tmpl` removed — in IGZ3 it's still needed for runtime config generation - -**Merge plan**: `IS_MF=false` default makes the standard build safe. Restore `COPY config.json.tmpl` for IGZ3 path (inside `IS_MF` conditional in Dockerfile). The DNS resolver trick should remain in `run_nginx` for IGZ3. - -#### `.env.production` -```diff -+ VITE_FEDERATION=false -``` -**SAFE TO MERGE**. In IGZ4 Dockerfile, the `sed` command overwrites this with `true`. - ---- - -### 11. Change Owner Pop-Up - -**File:** `src/elements/ChangeOwnerPopUp/ChangeOwnerPopUp.jsx` - -#### What changed - -**A. Apply changes: JSONAPI body → `updateProjectOwner()`** -```diff -// IGZ3: builds full JSONAPI relationship body -- const projectData = { -- data: { type: 'project', attributes: {}, -- relationships: { owner: { data: { id: newOwnerId, type: USER_ROLE } } } -- } -- } -- projectsIguazioApi.editProject(projectId, projectData).then(changeOwnerCallback) - -// IGZ4: single direct call -+ projectsIguazioApi.updateProjectOwner(projectId, newOwnerId).then(changeOwnerCallback) -``` - -**B. User search: `getScrubbedUsers` → `searchUsersMetadata`** -```diff -// IGZ3: scrubbed_users endpoint with role filter + version-conditional search syntax -- if (isIgzVersionCompatible(requiredIgzVersion)) { -- params['filter[username]'] = `[$contains_istr]${memberName}` -- } -- const response = await projectsIguazioApi.getScrubbedUsers({ params }) -- const { data: { data: users } } = response -- formattedUsers = users.map(user => ({ -- name: `${user.attributes.first_name} ${user.attributes.last_name}`, -- username: user.attributes.username, -- id: user.id, // UUID -- })) - -// IGZ4: search-users-metadata endpoint, flat response -+ const response = await projectsIguazioApi.searchUsersMetadata(memberName) -+ const users = response.data.items || [] -+ formattedUsers = users.map(user => ({ -+ name: `${user.firstName} ${user.lastName}`, -+ username: user.username, -+ id: user.username, // username as ID -+ })) -``` - -Removed: `isIgzVersionCompatible` import, `USER_ROLE` import. - -#### Why -IGZ4 has a new user search endpoint with a different response shape. Owner updates use a dedicated endpoint. Identity is username-based. - -#### How to merge -Wrap both `applyChanges()` and `generateSuggestionList()` in `VITE_FEDERATION` conditionals: - -```js -const applyChanges = () => { - if (newOwnerId) { - const apiCall = import.meta.env.VITE_FEDERATION === 'true' - ? projectsIguazioApi.updateProjectOwner(projectId, newOwnerId) - : projectsIguazioApi.editProject(projectId, { data: { ... } }) - apiCall.then(changeOwnerCallback).catch(...).finally(handleOnClose) - } -} - -const generateSuggestionList = async (memberName, resolve) => { - if (import.meta.env.VITE_FEDERATION === 'true') { - const response = await projectsIguazioApi.searchUsersMetadata(memberName) - const users = response.data.items || [] - formattedUsers = users.map(user => ({ ..., id: user.username })) - } else { - const response = await projectsIguazioApi.getScrubbedUsers({ params: { ... } }) - const users = response.data.data - formattedUsers = users.map(user => ({ ..., id: user.id })) - } -} -``` - ---- - -### 12. Project Authorization Utility - -**Files:** `src/utils/projectAuth.util.js` (new), `src/components/Workflow/workflow.util.js` - -#### What changed - -**`projectAuth.util.js`** — new utility wrapping the IGZ4 policies endpoint: -```js -// IGZ4 only: checks if the active user has write access to a project -export const checkProjectWriteAccess = async projectName => { - // calls GET /v1/authorization/projects/{name}/policies - // returns true if active user is owner, admin, or security admin -} -``` - -**`workflow.util.js`** — replaced two-step IGZ3 permission check with single call: -```diff -// IGZ3: try owner visibility, fall back to workflows update authorization -- await projectsIguazioApi.getProjectOwnerVisibility(projectName) -- // catch → try getProjectWorkflowsUpdateAuthorization(projectName) - -// IGZ4: single call via new utility -+ const hasAccess = await checkProjectWriteAccess(projectName) -``` - -Both `fetchMissingProjectsPermissions()` and `fetchMissingProjectPermission()` were updated. - -#### Why -IGZ4's new auth model provides a single policies endpoint that contains all permission information. The old two-endpoint chain (`getProjectOwnerVisibility` + `getProjectWorkflowsUpdateAuthorization`) is gone. The new utility centralizes this logic. - -#### How to merge -`projectAuth.util.js` currently contains only IGZ4 logic. Two options: - -**Option A** — Add IGZ3 fallback inside `projectAuth.util.js`: -```js -export const checkProjectWriteAccess = async projectName => { - if (import.meta.env.VITE_FEDERATION === 'true') { - // IGZ4: GET /v1/authorization/projects/{name}/policies - } else { - // IGZ3: try getProjectOwnerVisibility, fall back to getProjectWorkflowsUpdateAuthorization - return projectsIguazioApi - .getProjectOwnerVisibility(projectName) - .then(() => true) - .catch(() => projectsIguazioApi - .getProjectWorkflowsUpdateAuthorization(projectName) - .then(() => true) - .catch(() => false) - ) - } -} -``` -This keeps `workflow.util.js` unchanged after merge. - -**Option B** — Branch in `workflow.util.js` directly, keeping `projectAuth.util.js` IGZ4-only. - -Option A is cleaner — it isolates the platform-specific logic in the auth utility. - ---- - -### 13. Module Federation Build - -**Files:** `vite.config.mjs`, `config/loadDevProxyConfig.js`, `src/utils/nuclio.remotes.utils.js`, `scripts/previewLocalBuildMF.mjs` - -#### What changed - -**`vite.config.mjs`** — three changes: -1. Added `@module-federation/vite` plugin, conditionally enabled: - ```js - const federationPlugin = env.VITE_FEDERATION === 'true' - ? federation({ filename: 'remoteEntry.js', name: 'mlrun', - exposes: { './loadRemoteConfig': './src/loadRemoteConfig.js', './app': './src/main.jsx' }, - shared: { react: { singleton: true }, 'react-dom': { singleton: true } } - }) - : null - ``` -2. Dev proxy config moved to `loadDevProxyConfig.js` (replaces hardcoded proxy object): - ```diff - - proxy: { - - '/api': env.VITE_MLRUN_API_URL ? { target: ..., headers: { 'x-v3io-session-key': ... } } : undefined, - - '/nuclio': ..., - - '/iguazio': ..., - - '/function-catalog': ... - - } - + proxy: { ...mlrunProxyConfig(env) } - ``` -3. Added `build.target: 'esnext'` (required for top-level `await` in MF entry points). - -**`config/loadDevProxyConfig.js`** — reads proxy configuration from a DRC (Dev Remote Config) file, allowing dynamic proxy targets without rebuilding. Falls back to env vars. - -**`src/utils/nuclio.remotes.utils.js`** — utilities for loading Nuclio as a Module Federation remote at runtime. - -#### Why -Module Federation requires the MF Vite plugin to expose `remoteEntry.js`. The `build.target: 'esnext'` is needed because MF entry points use top-level `await`. Proxy config was externalized to support the DRC (Dev Remote Config) tooling used in IGZ4 development. - -#### How to merge -**SAFE TO MERGE AS-IS.** The federation plugin is only instantiated when `VITE_FEDERATION=true`. When `false`, `federationPlugin` is `null` and Vite ignores it. The proxy config now comes from `loadDevProxyConfig.js` which falls back to env vars — same behavior as before for IGZ3 dev. - ---- - -### 14. Bug Fixes - -Various files with pure bug fixes and safe improvements that require no conditional logic. - -#### A. `isDetailsTabExists` guard — 5 components - -`src/elements/JobsTable/JobsTable.jsx`, `src/elements/WorkflowsTable/WorkflowsTable.jsx`, `src/components/FunctionsPageOld/FunctionsOld.jsx`, `src/components/FeatureStore/FeatureSets/FeatureSets.jsx`, `src/components/FeatureStore/FeatureVectors/FeatureVectors.jsx` - -All five had the same race condition: `isDetailsTabExists()` was called when a URL param was set but before the selected item was loaded, causing wrong tab redirects. Fix: added `!isEmpty(selectedXxx)` guard. - -```diff -- if (params.jobId && pageData.details.menu.length > 0) { -+ if (params.jobId && !isEmpty(selectedJob) && pageData.details.menu.length > 0) { - isDetailsTabExists(params.tab, pageData.details.menu, navigate, location) - } -``` - -#### B. `usePagination.hook.js` — FORCE_REFRESH support - -Added a `FORCE_REFRESH` URL search param that forces a content re-fetch even when the `BE_PAGE` number hasn't changed. The param is consumed and deleted after use (single-use trigger). Useful for explicit refresh actions after mutations. - -```js -// Forces re-fetch regardless of bePage -if (!bePage && !forceRefreshData.isForce) return -if (lastRequestedPageRef.current === bePage && !forceRefreshData.isForce) return -``` - -#### C. `DetailsInputs.jsx` — nested inputs + refactor - -Input processing was refactored from an inline `useEffect` body into a `getInputsContent` useCallback. More importantly, it now handles inputs where the value is a nested object (recursive call), which previously would throw on `.startsWith()`: - -```diff -+ if (inputPath && typeof inputPath === 'object') { -+ getInputsContent(inputPath) -+ return -+ } -``` - -#### D. `DetailsPipeline.jsx` — loading state + error step fix - -1. Loading state now comes from `artifactsStore.pipelines.loading` instead of `functionsStore.funcLoading` — the pipeline data is fetched as an artifact, not a function. -2. `addVisualFramesForGroups` renamed to `addVisualFramesForFunctions` (matches actual semantics). -3. Error steps with a `base_step` now inherit the base step's function: - ```diff - + } else if (step.kind === ERROR_STEP_KIND && !step.function && step.base_step) { - + stepData.function = steps[step.base_step]?.function || '' - + } - ``` - -#### E. `generateTemplatesCategories.js` — category deduplication - -1. Added `categoryMap` to normalize category names (e.g., `genai` → `GenAI`). -2. Category deduplication is now case-insensitive (previously two entries with different cases could coexist). - -```diff -+ const categoryMap = { genai: 'GenAI' } -+ categories: template.metadata?.categories?.map(cat => categoryMap[cat.toLowerCase()] || cat) - -// Deduplication: -- if (!hubFunctionsCategories.includes(category)) { -+ const isDuplicate = hubFunctionsCategories.some( -+ existing => existing.toLowerCase() === category.toLowerCase() -+ ) -+ if (!isDuplicate) { -``` - -#### F. Other safe fixes - -| File | Fix | -|------|-----| -| `src/hooks/nuclioMode.hook.js` | Optional chaining on `window?.mlrunConfig` | -| `src/utils/getState.js` | Added `standby`, `scaledToZero`, `initialized` states | -| `src/api/jobs-api.js` | Stream via axios adapter instead of fetch | -| `src/utils/getJobLogs.util.js` | `res.body` → `res.data` (pairs with jobs-api.js) | -| `src/reducers/artifactsReducer.js` | Error message callback | -| `src/reducers/functionReducer.js` | `return thunkAPI.rejectWithValue` | -| `src/components/Datasets/datasets.util.jsx` | Optional chaining `artifact_limits?.max_download_size` | -| `src/common/Download/Download.jsx` | Optional chaining | -| `src/utils/getArtifactPreview.jsx` | Optional chaining | -| `src/components/FunctionsPage/Functions.jsx` | `!isEmpty(selectedFunction)` guard | -| `src/common/DatePicker/DatePicker.jsx` | `dateTo` null safety | -| `src/common/ReactFlow/mlReactFlow.util.js` | Rename `addVisualFramesForGroups` → `addVisualFramesForFunctions`; margin 16→34 | -| `src/utils/getNoDataMessage.js` | Added filter message, removed trailing periods | -| `src/components/ModelsPage/RealTimePipelines/RealTimePipelinesCounters.jsx` | Tooltip wrappers on stats cards | -| `src/components/Details/DetailsTabsContent/DetailsTabsContent.jsx` | `handleCancel` forwarded to `DetailsLLMPrompts`; `refresh` prop on `DetailsModelEndpoints` | diff --git a/nginx/nginx.conf.mf.tmpl b/nginx/nginx.conf.mf.tmpl new file mode 100644 index 0000000000..7989d3eeba --- /dev/null +++ b/nginx/nginx.conf.mf.tmpl @@ -0,0 +1,30 @@ +server { + listen 8090; + server_name localhost; + + root /usr/share/nginx/html; + + # Basic Gzip as per standard defaults + gzip on; + + # CRITICAL: Allow the Global Host (IGZ) to fetch these assets + add_header Access-Control-Allow-Origin *; + add_header Access-Control-Allow-Methods 'GET, OPTIONS'; + add_header Access-Control-Allow-Headers 'Content-Type, Authorization'; + + location / { + # Show landing page when accessing the remote directly + index landing.html; + try_files $uri $uri/ /landing.html; + + # Ensure remote assets are always fresh + if ($request_filename ~* .*\.(?:js|json)$ ) { + add_header Cache-Control "no-cache, no-store, must-revalidate"; + } + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} diff --git a/nginx/nginx.conf.tmpl b/nginx/nginx.conf.tmpl index 7989d3eeba..4bdb86a342 100644 --- a/nginx/nginx.conf.tmpl +++ b/nginx/nginx.conf.tmpl @@ -1,30 +1,47 @@ +map $http_x_v3io_session_key $v3io_session_key { + default $http_x_v3io_session_key; + "" ${MLRUN_V3IO_ACCESS_KEY}; +} + server { - listen 8090; - server_name localhost; - - root /usr/share/nginx/html; - - # Basic Gzip as per standard defaults - gzip on; - - # CRITICAL: Allow the Global Host (IGZ) to fetch these assets - add_header Access-Control-Allow-Origin *; - add_header Access-Control-Allow-Methods 'GET, OPTIONS'; - add_header Access-Control-Allow-Headers 'Content-Type, Authorization'; - - location / { - # Show landing page when accessing the remote directly - index landing.html; - try_files $uri $uri/ /landing.html; - - # Ensure remote assets are always fresh - if ($request_filename ~* .*\.(?:js|json)$ ) { - add_header Cache-Control "no-cache, no-store, must-revalidate"; - } - } - - error_page 500 502 503 504 /50x.html; - location = /50x.html { - root /usr/share/nginx/html; - } + + listen 8090; + + include resolvers.conf; + + # https://raw.githubusercontent.com + set $function_catalog ${MLRUN_FUNCTION_CATALOG_URL}; + location /function-catalog { + rewrite /function-catalog/(.*) ${MLRUN_FUNCTION_CATALOG_PATH}/$1 break; + proxy_pass $function_catalog; + } + + location /mlrun { + rewrite ^/mlrun(/|$)(.*) /$2 last; + } + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri $uri/ /index.html; + } + + set $backend ${MLRUN_API_PROXY_URL}; + location /api { + proxy_set_header x-v3io-session-key $v3io_session_key; + proxy_pass $backend; + } + + set $nuclio_backend ${MLRUN_NUCLIO_API_URL}; + location /nuclio { + rewrite ^/nuclio(/|$)(.*) /$2 break; + proxy_pass $nuclio_backend; + } + + error_page 500 502 503 504 /50x.html; + + location = /50x.html { + root /usr/share/nginx/html; + } + } diff --git a/nginx/run_nginx b/nginx/run_nginx index f6e1ae65af..b9d3367b4a 100755 --- a/nginx/run_nginx +++ b/nginx/run_nginx @@ -1,7 +1,23 @@ #!/bin/sh -# Simply copy the template as the final config -cp /etc/nginx/conf.d/nginx.conf.tmpl /etc/nginx/conf.d/nginx.conf +# update nginx configuration with env +envsubst '${MLRUN_API_PROXY_URL} \ + ${MLRUN_BETA_MODE} \ + ${MLRUN_FUNCTION_CATALOG_URL} \ + ${MLRUN_FUNCTION_CATALOG_PATH} \ + ${MLRUN_NUCLIO_API_URL} \ + ${MLRUN_NUCLIO_MODE} \ + ${MLRUN_NUCLIO_UI_URL} \ + ${MLRUN_V3IO_ACCESS_KEY}' \ +< /etc/nginx/conf.d/nginx.conf.tmpl \ +> /etc/nginx/conf.d/nginx.conf -# Start Nginx in foreground -exec nginx -g 'daemon off;' +# update configuration with env +envsubst '${MLRUN_BETA_MODE} \ + ${MLRUN_NUCLIO_MODE} \ + ${MLRUN_NUCLIO_UI_URL}' \ +< /usr/share/nginx/html/config.json.tmpl \ +> /usr/share/nginx/html/config.json + +# start nginx in the foreground +nginx -g 'daemon off;' diff --git a/nginx/run_nginx.mf b/nginx/run_nginx.mf new file mode 100644 index 0000000000..f6e1ae65af --- /dev/null +++ b/nginx/run_nginx.mf @@ -0,0 +1,7 @@ +#!/bin/sh + +# Simply copy the template as the final config +cp /etc/nginx/conf.d/nginx.conf.tmpl /etc/nginx/conf.d/nginx.conf + +# Start Nginx in foreground +exec nginx -g 'daemon off;' diff --git a/package-lock.json b/package-lock.json index 69acc97f78..49a30d9ce3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -171,23 +171,23 @@ } }, "node_modules/@asamuzakjp/css-color": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.1.2.tgz", - "integrity": "sha512-NfBUvBaYgKIuq6E/RBLY1m0IohzNHAYyaJGuTK79Z23uNwmz2jl1mPsC5ZxCCxylinKhT1Amn5oNTlx1wN8cQg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.1.1.tgz", + "integrity": "sha512-B0Hv6G3gWGMn0xKJ0txEi/jM5iFpT3MfDxmhZFb4W047GvytCf1DHQ1D69W3zHI4yWe2aTZAA0JnbMZ7Xc8DuQ==", "dev": true, "license": "MIT", "dependencies": { - "@csstools/css-calc": "^3.0.0", - "@csstools/css-color-parser": "^4.0.1", - "@csstools/css-parser-algorithms": "^4.0.0", - "@csstools/css-tokenizer": "^4.0.0", - "lru-cache": "^11.2.5" + "@csstools/css-calc": "^2.1.4", + "@csstools/css-color-parser": "^3.1.0", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "lru-cache": "^11.2.4" } }, "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { - "version": "11.2.6", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", - "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", + "version": "11.2.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.5.tgz", + "integrity": "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -195,9 +195,9 @@ } }, "node_modules/@asamuzakjp/dom-selector": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.8.1.tgz", - "integrity": "sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==", + "version": "6.7.7", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.7.7.tgz", + "integrity": "sha512-8CO/UQ4tzDd7ula+/CVimJIVWez99UJlbMyIgk8xOnhAVPKLnBZmUFYVgugS441v2ZqUq5EnSh6B0Ua0liSFAA==", "dev": true, "license": "MIT", "dependencies": { @@ -205,13 +205,13 @@ "bidi-js": "^1.0.3", "css-tree": "^3.1.0", "is-potential-custom-element-name": "^1.0.1", - "lru-cache": "^11.2.6" + "lru-cache": "^11.2.5" } }, "node_modules/@asamuzakjp/dom-selector/node_modules/lru-cache": { - "version": "11.2.6", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", - "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", + "version": "11.2.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.5.tgz", + "integrity": "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -321,9 +321,9 @@ } }, "node_modules/@babel/generator": { - "version": "7.29.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", - "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.0.tgz", + "integrity": "sha512-vSH118/wwM/pLR38g/Sgk05sNtro6TlTJKuiMXDaZqPUfjTFcudpCOt00IhOfj+1BFAX+UFAlzCU+6WXr3GLFQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2524,9 +2524,9 @@ } }, "node_modules/@csstools/color-helpers": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.1.tgz", - "integrity": "sha512-NmXRccUJMk2AWA5A7e5a//3bCIMyOu2hAtdRYrhPPHjDxINuCwX1w6rnIZ4xjLcp0ayv6h8Pc3X0eJUGiAAXHQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", + "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", "dev": true, "funding": [ { @@ -2540,13 +2540,13 @@ ], "license": "MIT-0", "engines": { - "node": ">=20.19.0" + "node": ">=18" } }, "node_modules/@csstools/css-calc": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.1.1.tgz", - "integrity": "sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", "dev": true, "funding": [ { @@ -2560,17 +2560,17 @@ ], "license": "MIT", "engines": { - "node": ">=20.19.0" + "node": ">=18" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^4.0.0", - "@csstools/css-tokenizer": "^4.0.0" + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" } }, "node_modules/@csstools/css-color-parser": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.0.1.tgz", - "integrity": "sha512-vYwO15eRBEkeF6xjAno/KQ61HacNhfQuuU/eGwH67DplL0zD5ZixUa563phQvUelA07yDczIXdtmYojCphKJcw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", + "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", "dev": true, "funding": [ { @@ -2584,21 +2584,21 @@ ], "license": "MIT", "dependencies": { - "@csstools/color-helpers": "^6.0.1", - "@csstools/css-calc": "^3.0.0" + "@csstools/color-helpers": "^5.1.0", + "@csstools/css-calc": "^2.1.4" }, "engines": { - "node": ">=20.19.0" + "node": ">=18" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^4.0.0", - "@csstools/css-tokenizer": "^4.0.0" + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" } }, "node_modules/@csstools/css-parser-algorithms": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", - "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", "dev": true, "funding": [ { @@ -2612,16 +2612,16 @@ ], "license": "MIT", "engines": { - "node": ">=20.19.0" + "node": ">=18" }, "peerDependencies": { - "@csstools/css-tokenizer": "^4.0.0" + "@csstools/css-tokenizer": "^3.0.4" } }, "node_modules/@csstools/css-syntax-patches-for-csstree": { - "version": "1.0.27", - "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.27.tgz", - "integrity": "sha512-sxP33Jwg1bviSUXAV43cVYdmjt2TLnLXNqCWl9xmxHawWVjGz/kEbdkr7F9pxJNBN2Mh+dq0crgItbW6tQvyow==", + "version": "1.0.26", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.26.tgz", + "integrity": "sha512-6boXK0KkzT5u5xOgF6TKB+CLq9SOpEGmkZw0g5n9/7yg85wab3UzSxB8TxhLJ31L4SGJ6BCFRw/iftTha1CJXA==", "dev": true, "funding": [ { @@ -2636,9 +2636,9 @@ "license": "MIT-0" }, "node_modules/@csstools/css-tokenizer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", - "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", "dev": true, "funding": [ { @@ -2652,7 +2652,7 @@ ], "license": "MIT", "engines": { - "node": ">=20.19.0" + "node": ">=18" } }, "node_modules/@csstools/normalize.css": { @@ -4880,38 +4880,13 @@ } }, "node_modules/@cucumber/ci-environment": { -<<<<<<< HEAD - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@cucumber/ci-environment/-/ci-environment-12.0.0.tgz", - "integrity": "sha512-SqCEnbCNl3zCXCFpqGUuoaSNhLC0jLw4tKeFcAxTw9MD/QRlJjeAC/fyvVLFuXuSq0OunJlFfxLu+Z3HE+oLPg==", -======= "version": "13.0.0", "resolved": "https://registry.npmjs.org/@cucumber/ci-environment/-/ci-environment-13.0.0.tgz", "integrity": "sha512-cs+3NzfNkGbcmHPddjEv4TKFiBpZRQ6WJEEufB9mw+ExS22V/4R/zpDSEG+fsJ/iSNCd6A2sATdY8PFOyY3YnA==", ->>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "dev": true, "license": "MIT" }, "node_modules/@cucumber/cucumber": { -<<<<<<< HEAD - "version": "12.6.0", - "resolved": "https://registry.npmjs.org/@cucumber/cucumber/-/cucumber-12.6.0.tgz", - "integrity": "sha512-z6XKBIcUnJebnR3W8+K7Q2jJKB+pKpoD1l3CygEa9ufq/aeGuS5LAlllNxrod8loepLJhNmp8J8aengGbkL4cg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@cucumber/ci-environment": "12.0.0", - "@cucumber/cucumber-expressions": "18.1.0", - "@cucumber/gherkin": "37.0.1", - "@cucumber/gherkin-streams": "6.0.0", - "@cucumber/gherkin-utils": "10.0.0", - "@cucumber/html-formatter": "22.3.0", - "@cucumber/junit-xml-formatter": "0.9.0", - "@cucumber/message-streams": "4.0.1", - "@cucumber/messages": "31.2.0", - "@cucumber/pretty-formatter": "1.0.1", - "@cucumber/tag-expressions": "8.1.0", -======= "version": "12.7.0", "resolved": "https://registry.npmjs.org/@cucumber/cucumber/-/cucumber-12.7.0.tgz", "integrity": "sha512-7A/9CJpJDxv1SQ7hAZU0zPn2yRxx6XMR+LO4T94Enm3cYNWsEEj+RGX38NLX4INT+H6w5raX3Csb/qs4vUBsOA==", @@ -4929,7 +4904,6 @@ "@cucumber/messages": "32.0.1", "@cucumber/pretty-formatter": "1.0.1", "@cucumber/tag-expressions": "9.1.0", ->>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "assertion-error-formatter": "^3.0.0", "capital-case": "^1.0.4", "chalk": "^4.1.2", @@ -4952,11 +4926,7 @@ "mz": "^2.7.0", "progress": "^2.0.3", "read-package-up": "^12.0.0", -<<<<<<< HEAD - "semver": "7.7.3", -======= "semver": "7.7.4", ->>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "string-argv": "0.3.1", "supports-color": "^8.1.1", "type-fest": "^4.41.0", @@ -4975,15 +4945,9 @@ } }, "node_modules/@cucumber/cucumber-expressions": { -<<<<<<< HEAD - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/@cucumber/cucumber-expressions/-/cucumber-expressions-18.1.0.tgz", - "integrity": "sha512-9yc+wForrn15FaqLWNjYb19iQ/gPXhcq1kc4X1Ex1lR7NcJpa5pGnCow3bc1HERVM5IoYH+gwwrcJogSMsf+Vw==", -======= "version": "19.0.0", "resolved": "https://registry.npmjs.org/@cucumber/cucumber-expressions/-/cucumber-expressions-19.0.0.tgz", "integrity": "sha512-4FKoOQh2Uf6F6/Ln+1OxuK8LkTg6PyAqekhf2Ix8zqV2M54sH+m7XNJNLhOFOAW/t9nxzRbw2CcvXbCLjcvHZg==", ->>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "dev": true, "license": "MIT", "dependencies": { @@ -5000,29 +4964,6 @@ "node": ">=20" } }, -<<<<<<< HEAD - "node_modules/@cucumber/cucumber/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@cucumber/gherkin": { - "version": "37.0.1", - "resolved": "https://registry.npmjs.org/@cucumber/gherkin/-/gherkin-37.0.1.tgz", - "integrity": "sha512-VmX+PKa9vqKZiycZoQKYlCsA0N7gAfiOfrcHSjK+suEVUwvKEH2sjO47NznrFFLmVWYTRmw3DLHQnpBAznkYEA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@cucumber/messages": ">=31.0.0 <32" -======= "node_modules/@cucumber/gherkin": { "version": "38.0.0", "resolved": "https://registry.npmjs.org/@cucumber/gherkin/-/gherkin-38.0.0.tgz", @@ -5031,7 +4972,6 @@ "license": "MIT", "dependencies": { "@cucumber/messages": ">=31.0.0 <33" ->>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) } }, "node_modules/@cucumber/gherkin-streams": { @@ -5064,18 +5004,6 @@ } }, "node_modules/@cucumber/gherkin-utils": { -<<<<<<< HEAD - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@cucumber/gherkin-utils/-/gherkin-utils-10.0.0.tgz", - "integrity": "sha512-BcujlDT343GXXNrMPl3ws6Il3zs8dQw3Yp/d3HnOJF8i2snGGgiapoTbko7MdvAt7ivDL7SDo+e1d5Cnpl3llA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@cucumber/gherkin": "^34.0.0", - "@cucumber/messages": "^29.0.0", - "@teppeis/multimaps": "3.0.0", - "commander": "14.0.0", -======= "version": "11.0.0", "resolved": "https://registry.npmjs.org/@cucumber/gherkin-utils/-/gherkin-utils-11.0.0.tgz", "integrity": "sha512-LJ+s4+TepHTgdKWDR4zbPyT7rQjmYIcukTwNbwNwgqr6i8Gjcmzf6NmtbYDA19m1ZFg6kWbFsmHnj37ZuX+kZA==", @@ -5086,97 +5014,26 @@ "@cucumber/messages": "^32.0.0", "@teppeis/multimaps": "3.0.0", "commander": "14.0.2", ->>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "source-map-support": "^0.5.21" }, "bin": { "gherkin-utils": "bin/gherkin-utils" } }, - "node_modules/@cucumber/gherkin-utils/node_modules/@cucumber/gherkin": { - "version": "34.0.0", - "resolved": "https://registry.npmjs.org/@cucumber/gherkin/-/gherkin-34.0.0.tgz", - "integrity": "sha512-659CCFsrsyvuBi/Eix1fnhSheMnojSfnBcqJ3IMPNawx7JlrNJDcXYSSdxcUw3n/nG05P+ptCjmiZY3i14p+tA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@cucumber/messages": ">=19.1.4 <29" - } - }, - "node_modules/@cucumber/gherkin-utils/node_modules/@cucumber/gherkin/node_modules/@cucumber/messages": { - "version": "28.1.0", - "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-28.1.0.tgz", - "integrity": "sha512-2LzZtOwYKNlCuNf31ajkrekoy2M4z0Z1QGiPH40n4gf5t8VOUFb7m1ojtR4LmGvZxBGvJZP8voOmRqDWzBzYKA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/uuid": "10.0.0", - "class-transformer": "0.5.1", - "reflect-metadata": "0.2.2", - "uuid": "11.1.0" - } - }, - "node_modules/@cucumber/gherkin-utils/node_modules/@cucumber/messages": { - "version": "29.0.1", - "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-29.0.1.tgz", - "integrity": "sha512-aAvIYfQD6/aBdF8KFQChC3CQ1Q+GX9orlR6GurGiX6oqaCnBkxA4WU3OQUVepDynEFrPayerqKRFcAMhdcXReQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "class-transformer": "0.5.1", - "reflect-metadata": "0.2.2" - } - }, - "node_modules/@cucumber/gherkin-utils/node_modules/@types/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", - "dev": true, - "license": "MIT" - }, "node_modules/@cucumber/gherkin-utils/node_modules/commander": { -<<<<<<< HEAD - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.0.tgz", - "integrity": "sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==", -======= "version": "14.0.2", "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", ->>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "dev": true, "license": "MIT", "engines": { "node": ">=20" -<<<<<<< HEAD - } - }, - "node_modules/@cucumber/gherkin-utils/node_modules/uuid": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", - "dev": true, - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/esm/bin/uuid" - } - }, - "node_modules/@cucumber/html-formatter": { - "version": "22.3.0", - "resolved": "https://registry.npmjs.org/@cucumber/html-formatter/-/html-formatter-22.3.0.tgz", - "integrity": "sha512-0s3G7kznCRDiiesQ4K0yBdswGqU9E0j2AWUug41NpedBzhaY+Hn192ANRF597GZtuWrCjE53aFb3fOyOsT8B+g==", -======= } }, "node_modules/@cucumber/html-formatter": { "version": "23.0.0", "resolved": "https://registry.npmjs.org/@cucumber/html-formatter/-/html-formatter-23.0.0.tgz", "integrity": "sha512-WwcRzdM8Ixy4e53j+Frm3fKM5rNuIyWUfy4HajEN+Xk/YcjA6yW0ACGTFDReB++VDZz/iUtwYdTlPRY36NbqJg==", ->>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "dev": true, "license": "MIT", "peerDependencies": { @@ -5210,15 +5067,9 @@ } }, "node_modules/@cucumber/messages": { -<<<<<<< HEAD - "version": "31.2.0", - "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-31.2.0.tgz", - "integrity": "sha512-3urzBNCwmU/YKrKR0b3XdioFcOFNuxlLwEImsxeP8rXnweLs+Ky04QURcbKpFom3T6a6v9zVioLCfHUuSQ72pg==", -======= "version": "32.0.1", "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-32.0.1.tgz", "integrity": "sha512-1OSoW+GQvFUNAl6tdP2CTBexTXMNJF0094goVUcvugtQeXtJ0K8sCP0xbq7GGoiezs/eJAAOD03+zAPT64orHQ==", ->>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "dev": true, "license": "MIT", "dependencies": { @@ -5243,8 +5094,6 @@ "@cucumber/messages": "*" } }, -<<<<<<< HEAD -======= "node_modules/@cucumber/pretty-formatter/node_modules/ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", @@ -5258,7 +5107,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, ->>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "node_modules/@cucumber/query": { "version": "14.7.0", "resolved": "https://registry.npmjs.org/@cucumber/query/-/query-14.7.0.tgz", @@ -5274,15 +5122,9 @@ } }, "node_modules/@cucumber/tag-expressions": { -<<<<<<< HEAD - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@cucumber/tag-expressions/-/tag-expressions-8.1.0.tgz", - "integrity": "sha512-UFeOVUyc711/E7VHjThxMwg3jbGod9TlbM1gxNixX/AGDKg82Eha4cE0tKki3GGUs7uB2NyI+hQAuhB8rL2h5A==", -======= "version": "9.1.0", "resolved": "https://registry.npmjs.org/@cucumber/tag-expressions/-/tag-expressions-9.1.0.tgz", "integrity": "sha512-bvHjcRFZ+J1TqIa9eFNO1wGHqwx4V9ZKV3hYgkuK/VahHx73uiP4rKV3JVrvWSMrwrFvJG6C8aEwnCWSvbyFdQ==", ->>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "dev": true, "license": "MIT" }, @@ -5878,9 +5720,9 @@ } }, "node_modules/@exodus/bytes": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.14.1.tgz", - "integrity": "sha512-OhkBFWI6GcRMUroChZiopRiSp2iAMvEBK47NhJooDqz1RERO4QuZIZnjP63TXX8GAiLABkYmX+fuQsdJ1dd2QQ==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.11.0.tgz", + "integrity": "sha512-wO3vd8nsEHdumsXrjGO/v4p6irbg7hy9kvIeR6i2AwylZSk4HJdWgL0FNaVquW1+AweJcdvU1IEpuIWk/WaPnA==", "dev": true, "license": "MIT", "engines": { @@ -7051,9 +6893,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", - "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz", + "integrity": "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==", "cpu": [ "arm" ], @@ -7064,9 +6906,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", - "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz", + "integrity": "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==", "cpu": [ "arm64" ], @@ -7077,9 +6919,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", - "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz", + "integrity": "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==", "cpu": [ "arm64" ], @@ -7090,9 +6932,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", - "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz", + "integrity": "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==", "cpu": [ "x64" ], @@ -7103,9 +6945,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", - "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz", + "integrity": "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==", "cpu": [ "arm64" ], @@ -7116,9 +6958,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", - "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz", + "integrity": "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==", "cpu": [ "x64" ], @@ -7129,9 +6971,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", - "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz", + "integrity": "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==", "cpu": [ "arm" ], @@ -7142,9 +6984,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", - "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz", + "integrity": "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==", "cpu": [ "arm" ], @@ -7155,9 +6997,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", - "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz", + "integrity": "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==", "cpu": [ "arm64" ], @@ -7168,9 +7010,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", - "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz", + "integrity": "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==", "cpu": [ "arm64" ], @@ -7181,9 +7023,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", - "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz", + "integrity": "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==", "cpu": [ "loong64" ], @@ -7194,9 +7036,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", - "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz", + "integrity": "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==", "cpu": [ "loong64" ], @@ -7207,9 +7049,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", - "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz", + "integrity": "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==", "cpu": [ "ppc64" ], @@ -7220,9 +7062,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", - "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz", + "integrity": "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==", "cpu": [ "ppc64" ], @@ -7233,9 +7075,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", - "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz", + "integrity": "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==", "cpu": [ "riscv64" ], @@ -7246,9 +7088,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", - "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz", + "integrity": "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==", "cpu": [ "riscv64" ], @@ -7259,9 +7101,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", - "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz", + "integrity": "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==", "cpu": [ "s390x" ], @@ -7272,9 +7114,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", - "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz", + "integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==", "cpu": [ "x64" ], @@ -7285,9 +7127,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", - "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz", + "integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==", "cpu": [ "x64" ], @@ -7298,9 +7140,9 @@ ] }, "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", - "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz", + "integrity": "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==", "cpu": [ "x64" ], @@ -7311,9 +7153,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", - "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz", + "integrity": "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==", "cpu": [ "arm64" ], @@ -7324,9 +7166,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", - "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz", + "integrity": "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==", "cpu": [ "arm64" ], @@ -7337,9 +7179,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", - "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz", + "integrity": "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==", "cpu": [ "ia32" ], @@ -7350,9 +7192,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", - "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz", + "integrity": "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==", "cpu": [ "x64" ], @@ -7363,9 +7205,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", - "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz", + "integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==", "cpu": [ "x64" ], @@ -7383,9 +7225,9 @@ "license": "MIT" }, "node_modules/@sinclair/typebox": { - "version": "0.27.10", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", - "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", "dev": true, "license": "MIT" }, @@ -7676,14 +7518,14 @@ } }, "node_modules/@storybook/core": { - "version": "8.6.17", - "resolved": "https://registry.npmjs.org/@storybook/core/-/core-8.6.17.tgz", - "integrity": "sha512-lndZDYIvUddWk54HmgYwE4h2B0JtWt8ztIRAzHRt6ReZZ9QQbmM5b85Qpa+ng4dyQEKc2JAtYD3Du7RRFcpHlw==", + "version": "8.6.15", + "resolved": "https://registry.npmjs.org/@storybook/core/-/core-8.6.15.tgz", + "integrity": "sha512-VFpKcphNurJpSC4fpUfKL3GTXVoL53oytghGR30QIw5jKWwaT50HVbTyb41BLOUuZjmMhUQA8weiQEew6RX0gw==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@storybook/theming": "8.6.17", + "@storybook/theming": "8.6.15", "better-opn": "^3.0.2", "browser-assert": "^1.2.1", "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0", @@ -7708,21 +7550,6 @@ } } }, - "node_modules/@storybook/core/node_modules/@storybook/theming": { - "version": "8.6.17", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-8.6.17.tgz", - "integrity": "sha512-IttFvRqozpuzN5MlQEWGOzUA2rZg86688Dyv1d+bjpYcFHtY1X4XyTCGwv1BPTaTsB959oM8R2yoNYWQkABbBA==", - "dev": true, - "license": "MIT", - "peer": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" - } - }, "node_modules/@storybook/csf-plugin": { "version": "8.6.14", "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-8.6.14.tgz", @@ -8520,6 +8347,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/@types/aria-query": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", @@ -8987,17 +8824,10 @@ "license": "MIT" }, "node_modules/@types/node": { -<<<<<<< HEAD - "version": "25.2.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.3.tgz", - "integrity": "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==", - "dev": true, -======= "version": "25.2.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.0.tgz", "integrity": "sha512-DZ8VwRFUNzuqJ5khrvwMXHmvPe+zGayJhr2CDNiKB1WBE1ST8Djl00D0IC4vvNmHMdj6DlbYRIaFE7WHjlDl5w==", "devOptional": true, ->>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "license": "MIT", "dependencies": { "undici-types": "~7.16.0" @@ -9018,9 +8848,9 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", - "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "version": "19.2.10", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.10.tgz", + "integrity": "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw==", "license": "MIT", "dependencies": { "csstype": "^3.2.2" @@ -9281,6 +9111,19 @@ "node": ">=18.20.0" } }, + "node_modules/@wdio/logger/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, "node_modules/@wdio/logger/node_modules/chalk": { "version": "5.6.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", @@ -9294,6 +9137,22 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/@wdio/logger/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/@webassemblyjs/ast": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", @@ -9487,9 +9346,9 @@ "peer": true }, "node_modules/@zip.js/zip.js": { - "version": "2.8.20", - "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.8.20.tgz", - "integrity": "sha512-oJzVhK9gnSKD++WLG37QEgeTgm5W8XUYmNv0EhOxytSr85vXn9EMpOoKNTK3yWDLa55Z0MovKW/6RNeh9OUmnA==", + "version": "2.8.16", + "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.8.16.tgz", + "integrity": "sha512-kCjaXh50GCf9afcof6ekjXPKR//rBVIxNHJLSUaM3VAET2F0+hymgrK1GpInRIIFUpt+wsnUfgx2+bbrmc+7Tw==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -9613,9 +9472,9 @@ } }, "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", "peer": true, @@ -9668,13 +9527,15 @@ } }, "node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" @@ -10198,25 +10059,19 @@ } }, "node_modules/babel-plugin-inline-react-svg/node_modules/resolve": { - "version": "2.0.0-next.6", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.6.tgz", - "integrity": "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==", + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "dev": true, "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "is-core-module": "^2.16.1", - "node-exports-info": "^1.6.0", - "object-keys": "^1.1.1", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, - "engines": { - "node": ">= 0.4" - }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -10521,9 +10376,9 @@ } }, "node_modules/basic-ftp": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.2.0.tgz", - "integrity": "sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.1.0.tgz", + "integrity": "sha512-RkaJzeJKDbaDWTIPiJwubyljaEPwpVWkm9Rt5h9Nd6h7tEXTJ3VB4qxdZBioV7JO5yLUaOKwz7vDOzlncUsegw==", "dev": true, "license": "MIT", "engines": { @@ -10882,9 +10737,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001770", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001770.tgz", - "integrity": "sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw==", + "version": "1.0.30001767", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001767.tgz", + "integrity": "sha512-34+zUAMhSH+r+9eKmYG+k2Rpt8XttfE4yXAjoZvkAPs15xcYQhyBYdalJ65BzivAvGRMViEjy6oKr/S91loekQ==", "dev": true, "funding": [ { @@ -10949,21 +10804,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chalk/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/chalk/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -11182,25 +11022,21 @@ "wrap-ansi": "^7.0.0" } }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/clone-deep": { @@ -11859,9 +11695,9 @@ } }, "node_modules/cssstyle/node_modules/lru-cache": { - "version": "11.2.6", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", - "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", + "version": "11.2.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.5.tgz", + "integrity": "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -13462,25 +13298,19 @@ } }, "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.6", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.6.tgz", - "integrity": "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==", + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "dev": true, "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "is-core-module": "^2.16.1", - "node-exports-info": "^1.6.0", - "object-keys": "^1.1.1", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, - "engines": { - "node": ">= 0.4" - }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -14256,16 +14086,9 @@ } }, "node_modules/flatted": { -<<<<<<< HEAD - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", - "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", - "dev": true, -======= "version": "3.3.3", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", ->>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "license": "ISC" }, "node_modules/follow-redirects": { @@ -14381,7 +14204,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "license": "ISC", "dependencies": { @@ -14755,20 +14578,6 @@ } }, "node_modules/glob": { -<<<<<<< HEAD - "version": "13.0.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.4.tgz", - "integrity": "sha512-KACie1EOs9BIOMtenFaxwmYODWA3/fTfGSUnLhMJpXRntu1g+uL/Xvub5f8SCTppvo9q62Qy4LeOoUiaL54G5A==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "minimatch": "^10.2.1", - "minipass": "^7.1.2", - "path-scurry": "^2.0.0" - }, - "engines": { - "node": "20 || >=22" -======= "version": "13.0.6", "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", @@ -14781,7 +14590,6 @@ }, "engines": { "node": "18 || 20 || >=22" ->>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -15135,27 +14943,11 @@ } }, "node_modules/hosted-git-info": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.2.tgz", - "integrity": "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==", + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^11.1.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "11.2.6", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", - "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" - } + "license": "ISC" }, "node_modules/html-encoding-sniffer": { "version": "6.0.0", @@ -15450,9 +15242,9 @@ } }, "node_modules/immutable": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.3.tgz", - "integrity": "sha512-AUY/VyX0E5XlibOmWt10uabJzam1zlYjwiEgQSDc5+UIkFNaF9WM0JxXKaNMGf+F/ffUF+7kRKXM9A7C0xXqMg==", + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz", + "integrity": "sha512-15gZoQ38eYjEjxkorfbcgBKBL6R7T459OuK+CpcWt7O3KF4uPCx2tD0uFETlUDIyo+1789crbMhTvQBSR5yBMg==", "dev": true, "license": "MIT", "engines": { @@ -17029,10 +16821,6 @@ "version": "3.7.2", "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz", "integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==", -<<<<<<< HEAD - "dev": true, -======= ->>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "license": "MIT", "engines": { "node": ">=12" @@ -17693,35 +17481,6 @@ "semver": "bin/semver" } }, - "node_modules/node-exports-info": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.0.tgz", - "integrity": "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "array.prototype.flatmap": "^1.3.3", - "es-errors": "^1.3.0", - "object.entries": "^1.1.9", - "semver": "^6.3.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/node-exports-info/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -17803,18 +17562,26 @@ } }, "node_modules/normalize-package-data": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-8.0.0.tgz", - "integrity": "sha512-RWk+PI433eESQ7ounYxIp67CYuVsS1uYSonX3kA6ps/3LWfjVQa/ptEg6Y3T6uAMq1mWpX9PQ+qx+QaHpsc7gQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "hosted-git-info": "^9.0.0", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" } }, "node_modules/normalize-path": { @@ -18318,15 +18085,9 @@ "license": "MIT" }, "node_modules/path-scurry": { -<<<<<<< HEAD - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", - "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", -======= "version": "2.0.2", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", ->>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -18334,11 +18095,7 @@ "minipass": "^7.1.2" }, "engines": { -<<<<<<< HEAD - "node": "20 || >=22" -======= "node": "18 || 20 || >=22" ->>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -18409,9 +18166,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "license": "MIT", "engines": { "node": ">=8.6" @@ -20368,6 +20125,20 @@ "node": ">=8" } }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/prismjs": { "version": "1.30.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", @@ -20704,16 +20475,6 @@ "node": ">=14" } }, - "node_modules/react-dev-utils/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/react-dev-utils/node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -20820,19 +20581,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/react-dev-utils/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/react-dom": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", @@ -20945,9 +20693,9 @@ } }, "node_modules/react-resizable-panels": { - "version": "4.6.4", - "resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-4.6.4.tgz", - "integrity": "sha512-E7Szs1xyaMZ7xOI2gG4TECNz4r/gmpV1AsXyZRnER6OQnfFf9uclFmrHHZR3h/iF8vQS+nQ1LKyZv9bzwGxPSg==", + "version": "4.5.9", + "resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-4.5.9.tgz", + "integrity": "sha512-7l0w2TxYq032F2o6PnfxDbDEKzi1lohDw3BKx4OBkh6uu7uh+Gj1C0Ubpv0/fOO2bRvo+IIQMOoFE0l2LgpeAg==", "license": "MIT", "peerDependencies": { "react": "^18.0.0 || ^19.0.0", @@ -21054,8 +20802,6 @@ "version": "12.0.0", "resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-12.0.0.tgz", "integrity": "sha512-Q5hMVBYur/eQNWDdbF4/Wqqr9Bjvtrw2kjGxxBbKLbx8bVCL8gcArjTy8zDUuLGQicftpMuU0riQNcAsbtOVsw==", -<<<<<<< HEAD -======= "dev": true, "license": "MIT", "dependencies": { @@ -21179,55 +20925,16 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", ->>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "dev": true, "license": "MIT", "dependencies": { - "find-up-simple": "^1.0.1", - "read-pkg": "^10.0.0", - "type-fest": "^5.2.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-package-up/node_modules/type-fest": { - "version": "5.4.4", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.4.4.tgz", - "integrity": "sha512-JnTrzGu+zPV3aXIUhnyWJj4z/wigMsdYajGLIYakqyOW1nPllzXEJee0QQbHj+CTIQtXGlAjuK0UY+2xTyjVAw==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "dependencies": { - "tagged-tag": "^1.0.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-10.1.0.tgz", - "integrity": "sha512-I8g2lArQiP78ll51UeMZojewtYgIRCKCWqZEgOO8c/uefTI+XDXvCSXu3+YNUaTNvZzobrL5+SqHjBrByRRTdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/normalize-package-data": "^2.4.4", - "normalize-package-data": "^8.0.0", - "parse-json": "^8.3.0", - "type-fest": "^5.4.4", - "unicorn-magic": "^0.4.0" + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" }, "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/read-pkg-up": { @@ -21248,62 +20955,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/read-pkg-up/node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true, - "license": "ISC" - }, - "node_modules/read-pkg-up/node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/read-pkg-up/node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up/node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, "node_modules/read-pkg-up/node_modules/type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", @@ -21314,51 +20965,14 @@ "node": ">=8" } }, - "node_modules/read-pkg/node_modules/parse-json": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", - "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.26.2", - "index-to-position": "^1.1.0", - "type-fest": "^4.39.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg/node_modules/parse-json/node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/read-pkg/node_modules/type-fest": { - "version": "5.4.4", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.4.4.tgz", - "integrity": "sha512-JnTrzGu+zPV3aXIUhnyWJj4z/wigMsdYajGLIYakqyOW1nPllzXEJee0QQbHj+CTIQtXGlAjuK0UY+2xTyjVAw==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", "dev": true, "license": "(MIT OR CC0-1.0)", - "dependencies": { - "tagged-tag": "^1.0.0" - }, "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/readable-stream": { @@ -21892,7 +21506,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "license": "ISC", "dependencies": { @@ -21911,16 +21525,9 @@ } }, "node_modules/rollup": { -<<<<<<< HEAD - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", - "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", - "dev": true, -======= "version": "4.57.1", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz", "integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==", ->>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "license": "MIT", "dependencies": { "@types/estree": "1.0.8" @@ -21933,31 +21540,31 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.59.0", - "@rollup/rollup-android-arm64": "4.59.0", - "@rollup/rollup-darwin-arm64": "4.59.0", - "@rollup/rollup-darwin-x64": "4.59.0", - "@rollup/rollup-freebsd-arm64": "4.59.0", - "@rollup/rollup-freebsd-x64": "4.59.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", - "@rollup/rollup-linux-arm-musleabihf": "4.59.0", - "@rollup/rollup-linux-arm64-gnu": "4.59.0", - "@rollup/rollup-linux-arm64-musl": "4.59.0", - "@rollup/rollup-linux-loong64-gnu": "4.59.0", - "@rollup/rollup-linux-loong64-musl": "4.59.0", - "@rollup/rollup-linux-ppc64-gnu": "4.59.0", - "@rollup/rollup-linux-ppc64-musl": "4.59.0", - "@rollup/rollup-linux-riscv64-gnu": "4.59.0", - "@rollup/rollup-linux-riscv64-musl": "4.59.0", - "@rollup/rollup-linux-s390x-gnu": "4.59.0", - "@rollup/rollup-linux-x64-gnu": "4.59.0", - "@rollup/rollup-linux-x64-musl": "4.59.0", - "@rollup/rollup-openbsd-x64": "4.59.0", - "@rollup/rollup-openharmony-arm64": "4.59.0", - "@rollup/rollup-win32-arm64-msvc": "4.59.0", - "@rollup/rollup-win32-ia32-msvc": "4.59.0", - "@rollup/rollup-win32-x64-gnu": "4.59.0", - "@rollup/rollup-win32-x64-msvc": "4.59.0", + "@rollup/rollup-android-arm-eabi": "4.57.1", + "@rollup/rollup-android-arm64": "4.57.1", + "@rollup/rollup-darwin-arm64": "4.57.1", + "@rollup/rollup-darwin-x64": "4.57.1", + "@rollup/rollup-freebsd-arm64": "4.57.1", + "@rollup/rollup-freebsd-x64": "4.57.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", + "@rollup/rollup-linux-arm-musleabihf": "4.57.1", + "@rollup/rollup-linux-arm64-gnu": "4.57.1", + "@rollup/rollup-linux-arm64-musl": "4.57.1", + "@rollup/rollup-linux-loong64-gnu": "4.57.1", + "@rollup/rollup-linux-loong64-musl": "4.57.1", + "@rollup/rollup-linux-ppc64-gnu": "4.57.1", + "@rollup/rollup-linux-ppc64-musl": "4.57.1", + "@rollup/rollup-linux-riscv64-gnu": "4.57.1", + "@rollup/rollup-linux-riscv64-musl": "4.57.1", + "@rollup/rollup-linux-s390x-gnu": "4.57.1", + "@rollup/rollup-linux-x64-gnu": "4.57.1", + "@rollup/rollup-linux-x64-musl": "4.57.1", + "@rollup/rollup-openbsd-x64": "4.57.1", + "@rollup/rollup-openharmony-arm64": "4.57.1", + "@rollup/rollup-win32-arm64-msvc": "4.57.1", + "@rollup/rollup-win32-ia32-msvc": "4.57.1", + "@rollup/rollup-win32-x64-gnu": "4.57.1", + "@rollup/rollup-win32-x64-msvc": "4.57.1", "fsevents": "~2.3.2" } }, @@ -22198,17 +21805,10 @@ } }, "node_modules/sass/node_modules/immutable": { -<<<<<<< HEAD - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.5.tgz", - "integrity": "sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==", - "dev": true, -======= "version": "5.1.4", "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz", "integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==", "devOptional": true, ->>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "license": "MIT" }, "node_modules/sass/node_modules/readdirp": { @@ -22225,16 +21825,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/sax": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.5.0.tgz", - "integrity": "sha512-21IYA3Q5cQf089Z6tgaUTr7lDAyzoTPx5HRtbhsME8Udispad8dC/+sziTNugOEx54ilvatQ9YCzl4KQLPcRHA==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=11.0.0" - } - }, "node_modules/saxes": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", @@ -22309,6 +21899,16 @@ "node": ">= 20.0.0" } }, + "node_modules/selenium-webdriver/node_modules/tmp": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, "node_modules/semver": { "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", @@ -22591,16 +22191,6 @@ "dev": true, "license": "ISC" }, -<<<<<<< HEAD - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, -======= ->>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "node_modules/simple-update-notifier": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", @@ -22648,22 +22238,6 @@ "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", @@ -22902,14 +22476,14 @@ } }, "node_modules/storybook": { - "version": "8.6.17", - "resolved": "https://registry.npmjs.org/storybook/-/storybook-8.6.17.tgz", - "integrity": "sha512-krR/l680A6qVnkGiK9p8jY0ucX3+kFCs2f4zw+S3w2Cdq8EiM/tFebPcX2V4S3z2UsO0v0dwAJOJNpzbFPdmVg==", + "version": "8.6.15", + "resolved": "https://registry.npmjs.org/storybook/-/storybook-8.6.15.tgz", + "integrity": "sha512-Ob7DMlwWx8s7dMvcQ3xPc02TvUeralb+xX3oaPRk9wY9Hc6M1IBC/7cEoITkSmRS2v38DHubC+mtEKNc1u2gQg==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@storybook/core": "8.6.17" + "@storybook/core": "8.6.15" }, "bin": { "getstorybook": "bin/index.cjs", @@ -23016,30 +22590,6 @@ "node": ">=8" } }, -<<<<<<< HEAD - "node_modules/string-width/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, -======= ->>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "node_modules/string.prototype.matchall": { "version": "4.0.12", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", @@ -23139,21 +22689,6 @@ } }, "node_modules/strip-ansi": { -<<<<<<< HEAD - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" -======= "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", @@ -23163,20 +22698,15 @@ }, "engines": { "node": ">=8" ->>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) } }, "node_modules/strip-ansi/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "dev": true, + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "node": ">=8" } }, "node_modules/strip-bom": { @@ -23379,16 +22909,6 @@ "node": ">=4" } }, - "node_modules/stylelint/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/stylelint/node_modules/autoprefixer": { "version": "9.8.8", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.8.tgz", @@ -23481,18 +23001,12 @@ "node": ">=4" } }, - "node_modules/stylelint/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/stylelint/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } + "license": "ISC" }, "node_modules/stylelint/node_modules/write-file-atomic": { "version": "3.0.3", @@ -23589,18 +23103,18 @@ "dev": true }, "node_modules/svgo": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.2.tgz", - "integrity": "sha512-TyzE4NVGLUFy+H/Uy4N6c3G0HEeprsVfge6Lmq+0FdQQ/zqoVYB62IsBZORsiL+o96s6ff/V6/3UQo/C0cgCAA==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", "dev": true, "license": "MIT", "dependencies": { + "@trysound/sax": "0.2.0", "commander": "^7.2.0", "css-select": "^4.1.3", "css-tree": "^1.1.3", "csso": "^4.2.0", "picocolors": "^1.0.0", - "sax": "^1.5.0", "stable": "^0.1.8" }, "bin": { @@ -23682,9 +23196,9 @@ } }, "node_modules/table/node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", "dependencies": { @@ -23698,16 +23212,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/table/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/table/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -23715,22 +23219,6 @@ "dev": true, "license": "MIT" }, -<<<<<<< HEAD - "node_modules/table/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, -======= ->>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "node_modules/tagged-tag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz", @@ -23897,9 +23385,9 @@ } }, "node_modules/terser-webpack-plugin/node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", "peer": true, @@ -24014,7 +23502,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "license": "ISC", "dependencies": { @@ -24131,9 +23619,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", "engines": { "node": ">=12" @@ -24153,38 +23641,25 @@ } }, "node_modules/tldts": { - "version": "7.0.23", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.23.tgz", - "integrity": "sha512-ASdhgQIBSay0R/eXggAkQ53G4nTJqTXqC2kbaBbdDwM7SkjyZyO0OaaN1/FH7U/yCeqOHDwFO5j8+Os/IS1dXw==", + "version": "7.0.21", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.21.tgz", + "integrity": "sha512-Plu6V8fF/XU6d2k8jPtlQf5F4Xx2hAin4r2C2ca7wR8NK5MbRTo9huLUWRe28f3Uk8bYZfg74tit/dSjc18xnw==", "dev": true, "license": "MIT", "dependencies": { - "tldts-core": "^7.0.23" + "tldts-core": "^7.0.21" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "7.0.23", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.23.tgz", - "integrity": "sha512-0g9vrtDQLrNIiCj22HSe9d4mLVG3g5ph5DZ8zCKBr4OtrspmNB6ss7hVyzArAeE88ceZocIEGkyW1Ime7fxPtQ==", + "version": "7.0.21", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.21.tgz", + "integrity": "sha512-oVOMdHvgjqyzUZH1rOESgJP1uNe2bVrfK0jUHHmiM2rpEiRbf3j4BrsIc6JigJRbHGanQwuZv/R+LTcHsw+bLA==", "dev": true, "license": "MIT" }, -<<<<<<< HEAD - "node_modules/tmp": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", - "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.14" - } - }, -======= ->>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -25085,9 +24560,9 @@ } }, "node_modules/vite-plugin-eslint/node_modules/rollup": { - "version": "2.80.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.80.0.tgz", - "integrity": "sha512-cIFJOD1DESzpjOBl763Kp1AH7UE/0fcdHe6rZXUdQ9c50uvgigvW97u3IcSeBwOkgqL/PXPBktBCh0KEu5L8XQ==", + "version": "2.79.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", + "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", "dev": true, "license": "MIT", "bin": { @@ -25139,9 +24614,9 @@ } }, "node_modules/vite-plugin-svgr/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { @@ -25259,9 +24734,9 @@ } }, "node_modules/vitest/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { @@ -25320,9 +24795,9 @@ } }, "node_modules/webpack": { - "version": "5.105.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.2.tgz", - "integrity": "sha512-dRXm0a2qcHPUBEzVk8uph0xWSjV/xZxenQQbLwnwP7caQCYpqG1qddwlyEkIDkYn0K8tvmcrZ+bOrzoQ3HxCDw==", + "version": "5.105.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.0.tgz", + "integrity": "sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==", "dev": true, "license": "MIT", "peer": true, @@ -25370,9 +24845,9 @@ } }, "node_modules/webpack-sources": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.4.tgz", - "integrity": "sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", + "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", "dev": true, "license": "MIT", "peer": true, @@ -25416,9 +24891,9 @@ } }, "node_modules/webpack/node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", "peer": true, @@ -25661,62 +25136,6 @@ "node": ">=0.10.0" } }, -<<<<<<< HEAD - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, -======= ->>>>>>> 41fbb330d ([FEAT] IGZ4 Module Federation integration — merge feature/ig4 into development baseline) "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -25738,6 +25157,13 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, "node_modules/ws": { "version": "8.19.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", diff --git a/package.json b/package.json index 9a23fc4f27..b776b581b9 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,8 @@ "preview": "vite preview", "preinstall": "npx force-resolutions", "test:coverage": "npm run test -- --coverage --watchAll=false", - "docker": "docker buildx build --platform linux/amd64 -t ${MLRUN_DOCKER_REGISTRY}${MLRUN_DOCKER_REPO:-mlrun}/mlrun-ui:${MLRUN_DOCKER_TAG:-latest} --build-arg COMMIT_HASH=\"`git rev-parse --short HEAD`\" --build-arg DATE=\"`date -u`\" --build-arg IS_MF=${npm_config_IS_MF:-false} -f Dockerfile .", + "docker": "docker build -t ${MLRUN_DOCKER_REGISTRY}${MLRUN_DOCKER_REPO:-mlrun}/mlrun-ui:${MLRUN_DOCKER_TAG:-latest} --build-arg COMMIT_HASH=\"`git rev-parse --short HEAD`\" --build-arg DATE=\"`date -u`\" -f Dockerfile .", + "docker:mf": "docker buildx build --platform linux/amd64 -t ${MLRUN_DOCKER_REGISTRY}${MLRUN_DOCKER_REPO:-mlrun}/mlrun-ui:${MLRUN_DOCKER_TAG:-latest} --build-arg COMMIT_HASH=\"`git rev-parse --short HEAD`\" --build-arg DATE=\"`date -u`\" -f Dockerfile.mf .", "generate-rn": "./generate-release-notes.js ${MLRUN_OLD_VERSION} ${MLRUN_VERSION} ${MLRUN_RELEASE_BRANCH} ${MLRUN_RELEASE_TYPE}", "storybook": "start-storybook -p 6006", "build-storybook": "build-storybook", diff --git a/public/config.json b/public/config.json index 06bc4b8e3a..cc2b881b35 100644 --- a/public/config.json +++ b/public/config.json @@ -1,6 +1,6 @@ { "betaMode": "enabled", "nuclioMode": "enabled", - "nuclioUiUrl": "http://localhost:4000", + "nuclioUiUrl": "http://localhost:8070", "nuclioRemoteEntryUrl": "http://localhost:5189" } diff --git a/src/App.jsx b/src/App.jsx index a307895dab..2badccb840 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -63,7 +63,8 @@ import { JOBS_MONITORING_JOBS_TAB, JOBS_MONITORING_WORKFLOWS_TAB, JOBS_MONITORING_SCHEDULED_TAB, - INACTIVE_JOBS_TAB + INACTIVE_JOBS_TAB, + IS_MF_MODE } from './constants' import 'reactflow/dist/style.css' @@ -71,74 +72,116 @@ import 'igz-controls/index.css' import './scss/main.scss' import RemoteNuclioRouteWrapper from './components/RemoteNuclio/RemoteNuclioRouteWrapper' -const Page = lazyRetry(() => import('./layout/Page/Page')) -const Datasets = lazyRetry(() => import('./components/Datasets/Datasets')) -const FeatureStore = lazyRetry(() => import('./components/FeatureStore/FeatureStore')) -const Files = lazyRetry(() => import('./components/Files/Files')) -const FunctionsOld = lazyRetry(() => import('./components/FunctionsPageOld/FunctionsOld')) // todo [functionsWithPagination] delete FunctionsOld and other related logic in 1.9.0 -const Functions = lazyRetry(() => import('./components/FunctionsPage/Functions')) -const Jobs = lazyRetry(() => import('./components/Jobs/Jobs')) -const MonitorJobs = lazyRetry(() => import('./components/Jobs/MonitorJobs/MonitorJobs')) +const Page = lazyRetry(() => import('./layout/Page/Page'), 'Page') +const Datasets = lazyRetry(() => import('./components/Datasets/Datasets'), 'Datasets') +const FeatureStore = lazyRetry( + () => import('./components/FeatureStore/FeatureStore'), + 'FeatureStore' +) +const Files = lazyRetry(() => import('./components/Files/Files'), 'Files') +const FunctionsOld = lazyRetry( + () => import('./components/FunctionsPageOld/FunctionsOld'), + 'FunctionsOld' +) // todo [functionsWithPagination] delete FunctionsOld and other related logic in 1.9.0 +const Functions = lazyRetry(() => import('./components/FunctionsPage/Functions'), 'Functions') +const Jobs = lazyRetry(() => import('./components/Jobs/Jobs'), 'Jobs') +const MonitorJobs = lazyRetry( + () => import('./components/Jobs/MonitorJobs/MonitorJobs'), + 'MonitorJobs' +) const MonitorWorkflows = lazyRetry( - () => import('./components/Jobs/MonitorWorkflows/MonitorWorkflows') + () => import('./components/Jobs/MonitorWorkflows/MonitorWorkflows'), + 'MonitorWorkflows' +) +const ScheduledJobs = lazyRetry( + () => import('./components/Jobs/ScheduledJobs/ScheduledJobs'), + 'ScheduledJobs' ) -const ScheduledJobs = lazyRetry(() => import('./components/Jobs/ScheduledJobs/ScheduledJobs')) -const Models = lazyRetry(() => import('./components/ModelsPage/Models/Models')) +const Models = lazyRetry(() => import('./components/ModelsPage/Models/Models'), 'Models') const RealTimePipelines = lazyRetry( - () => import('./components/ModelsPage/RealTimePipelines/RealTimePipelines') + () => import('./components/ModelsPage/RealTimePipelines/RealTimePipelines'), + 'RealTimePipelines' ) const ModelEndpoints = lazyRetry( - () => import('./components/ModelsPage/ModelEndpoints/ModelEndpoints') + () => import('./components/ModelsPage/ModelEndpoints/ModelEndpoints'), + 'ModelEndpoints' +) +const ModelsPage = lazyRetry(() => import('./components/ModelsPage/ModelsPage'), 'ModelsPage') +const Projects = lazyRetry(() => import('./components/ProjectsPage/Projects'), 'Projects') +const ProjectMonitor = lazyRetry( + () => import('./components/Project/ProjectMonitor'), + 'ProjectMonitor' ) -const ModelsPage = lazyRetry(() => import('./components/ModelsPage/ModelsPage')) -const Projects = lazyRetry(() => import('./components/ProjectsPage/Projects')) -const ProjectMonitor = lazyRetry(() => import('./components/Project/ProjectMonitor')) const ConsumerGroupsWrapper = lazyRetry( - () => import('./components/ConsumerGroupsWrapper/ConsumerGroupsWrapper') + () => import('./components/ConsumerGroupsWrapper/ConsumerGroupsWrapper'), + 'ConsumerGroupsWrapper' +) +const ConsumerGroup = lazyRetry( + () => import('./components/ConsumerGroup/ConsumerGroup'), + 'ConsumerGroup' +) +const ConsumerGroups = lazyRetry( + () => import('./components/ConsumerGroups/ConsumerGroups'), + 'ConsumerGroups' ) -const ConsumerGroup = lazyRetry(() => import('./components/ConsumerGroup/ConsumerGroup')) -const ConsumerGroups = lazyRetry(() => import('./components/ConsumerGroups/ConsumerGroups')) const ProjectOverview = lazyRetry( - () => import('./components/Project/ProjectOverview/ProjectOverview') + () => import('./components/Project/ProjectOverview/ProjectOverview'), + 'ProjectOverview' +) +const ProjectSettings = lazyRetry( + () => import('./components/ProjectSettings/ProjectSettings'), + 'ProjectSettings' ) -const ProjectSettings = lazyRetry(() => import('./components/ProjectSettings/ProjectSettings')) const AddToFeatureVectorPage = lazyRetry( - () => import('./components/AddToFeatureVectorPage/AddToFeatureVectorPage') + () => import('./components/AddToFeatureVectorPage/AddToFeatureVectorPage'), + 'AddToFeatureVectorPage' +) +const FeatureSets = lazyRetry( + () => import('./components/FeatureStore/FeatureSets/FeatureSets'), + 'FeatureSets' ) -const FeatureSets = lazyRetry(() => import('./components/FeatureStore/FeatureSets/FeatureSets')) -const Features = lazyRetry(() => import('./components/FeatureStore/Features/Features')) +const Features = lazyRetry(() => import('./components/FeatureStore/Features/Features'), 'Features') const FeatureVectors = lazyRetry( - () => import('./components/FeatureStore/FeatureVectors/FeatureVectors') + () => import('./components/FeatureStore/FeatureVectors/FeatureVectors'), + 'FeatureVectors' ) const ProjectsJobsMonitoring = lazyRetry( - () => import('./components/ProjectsJobsMonitoring/ProjectsJobsMonitoring') + () => import('./components/ProjectsJobsMonitoring/ProjectsJobsMonitoring'), + 'ProjectsJobsMonitoring' ) -const ProjectsAlerts = lazyRetry(() => import('./components/Alerts/Alerts')) +const ProjectsAlerts = lazyRetry(() => import('./components/Alerts/Alerts'), 'ProjectsAlerts') const JobsMonitoring = lazyRetry( - () => import('./components/ProjectsJobsMonitoring/JobsMonitoring/JobsMonitoring') + () => import('./components/ProjectsJobsMonitoring/JobsMonitoring/JobsMonitoring'), + 'JobsMonitoring' ) const ScheduledMonitoring = lazyRetry( - () => import('./components/ProjectsJobsMonitoring/ScheduledMonitoring/ScheduledMonitoring') + () => import('./components/ProjectsJobsMonitoring/ScheduledMonitoring/ScheduledMonitoring'), + 'ScheduledMonitoring' ) const WorkflowsMonitoring = lazyRetry( - () => import('./components/ProjectsJobsMonitoring/WorkflowsMonitoring/WorkflowsMonitoring') + () => import('./components/ProjectsJobsMonitoring/WorkflowsMonitoring/WorkflowsMonitoring'), + 'WorkflowsMonitoring' ) -const Documents = lazyRetry(() => import('./components/Documents/Documents')) -const LLMPrompts = lazyRetry(() => import('./components/LLMPrompts/LLMPrompts')) +const Documents = lazyRetry(() => import('./components/Documents/Documents'), 'Documents') +const LLMPrompts = lazyRetry(() => import('./components/LLMPrompts/LLMPrompts'), 'LLMPrompts') const ApplicationMetrics = lazyRetry( - () => import('./components/ApplicationMetrics/ApplicationMetrics') + () => import('./components/ApplicationMetrics/ApplicationMetrics'), + 'ApplicationMetrics' ) const MonitoringApplicationsPage = lazyRetry( - () => import('./components/MonitoringApplicationsPage/MonitoringApplicationsPage') + () => import('./components/MonitoringApplicationsPage/MonitoringApplicationsPage'), + 'MonitoringApplicationsPage' ) const MonitoringApplications = lazyRetry( () => - import('./components/MonitoringApplicationsPage/MonitoringApplications/MonitoringApplications') + import('./components/MonitoringApplicationsPage/MonitoringApplications/MonitoringApplications'), + 'MonitoringApplications' ) const MonitoringApplication = lazyRetry( () => - import('./components/MonitoringApplicationsPage/MonitoringApplications/MonitoringApplication/MonitoringApplication') + import('./components/MonitoringApplicationsPage/MonitoringApplications/MonitoringApplication/MonitoringApplication'), + 'MonitoringApplication' ) const App = () => { @@ -159,12 +202,14 @@ const App = () => { <> }> } /> - - } /> - } /> - } /> - } /> - + {IS_MF_MODE && ( + + } /> + } /> + } /> + } /> + + )} }> {[ `${JOBS_MONITORING_JOBS_TAB}/:jobName/:jobId/:tab`, diff --git a/src/api/projects-iguazio-api.js b/src/api/projects-iguazio-api.js index 04d9e3f79d..2648eaf8fe 100644 --- a/src/api/projects-iguazio-api.js +++ b/src/api/projects-iguazio-api.js @@ -19,7 +19,36 @@ such restriction. */ import { iguazioHttpClient } from '../httpClient' -const projectsIguazioApi = { +const igz3Api = { + editProject: (projectId, data) => iguazioHttpClient.put(`/projects/${projectId}`, data), + getProjectJob: jobId => iguazioHttpClient.get(`/jobs/${jobId}`), + getProjects: config => iguazioHttpClient.get('/projects', config), + getProjectMembers: projectId => + iguazioHttpClient.get(`/projects/${projectId}`, { + params: { + include: + 'project_authorization_roles.principal_users,project_authorization_roles.principal_user_groups' + } + }), + getProjectMembersVisibility: project => + iguazioHttpClient.get(`/projects/__name__/${project}/authorization`, { + params: { action: 'authorization/roles', sub_resource: 'authorization/roles' } + }), + getProjectOwnerVisibility: project => + iguazioHttpClient.get(`/projects/__name__/${project}/authorization`, { + params: { action: 'update', sub_resource: 'authorization/owner' } + }), + getProjectWorkflowsUpdateAuthorization: project => + iguazioHttpClient.get(`/projects/__name__/${project}/authorization`, { + params: { action: 'update', sub_resource: 'workflow' } + }), + updateProjectMembers: data => iguazioHttpClient.post('/async_transactions', data), + getScrubbedUsers: config => iguazioHttpClient.get('/scrubbed_users', config), + getScrubbedUserGroups: config => iguazioHttpClient.get('/scrubbed_user_groups', config), + getActiveUser: () => iguazioHttpClient.get('/self') +} + +const igz4Api = { updateProjectOwner: (projectName, owner) => iguazioHttpClient.put(`/v1/authorization/projects/${projectName}/owner`, { owner, @@ -37,4 +66,4 @@ const projectsIguazioApi = { iguazioHttpClient.get('/v1/authentication/self', { params: { format: 'full' } }) } -export default projectsIguazioApi +export default import.meta.env.VITE_FEDERATION === 'true' ? igz4Api : igz3Api diff --git a/src/common/ActionsMenu/ActionsMenu.jsx b/src/common/ActionsMenu/ActionsMenu.jsx deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/common/Breadcrumbs/breadcrumbs.util.js b/src/common/Breadcrumbs/breadcrumbs.util.js index 593c890c68..03620e6c90 100644 --- a/src/common/Breadcrumbs/breadcrumbs.util.js +++ b/src/common/Breadcrumbs/breadcrumbs.util.js @@ -35,8 +35,10 @@ import { PROJECTS_PAGE_PATH, MONITORING_APP_PAGE, DOCUMENTS_PAGE, - LLM_PROMPTS_PAGE + LLM_PROMPTS_PAGE, + IS_MF_MODE } from '../../constants' +import { generateNuclioLink } from '../../utils' export const generateMlrunScreens = params => params.projectName @@ -59,8 +61,20 @@ export const generateMlrunScreens = params => { label: 'Monitoring app', id: MONITORING_APP_PAGE }, { label: 'Jobs and workflows', id: 'jobs' }, { label: 'ML functions', id: 'functions' }, - { label: 'Real-time functions', id: 'real-time-functions' }, - { label: 'API gateways', id: 'api-gateways' }, + IS_MF_MODE + ? { label: 'Real-time functions', id: 'real-time-functions' } + : { + label: 'Real-time functions', + id: 'Real-time functions', + link: generateNuclioLink(`/projects/${params.projectName}/functions`) + }, + IS_MF_MODE + ? { label: 'API gateways', id: 'api-gateways' } + : { + label: 'API gateways', + id: 'API gateways', + link: generateNuclioLink(`/projects/${params.projectName}/api-gateways`) + }, { label: 'Alerts', id: ALERTS_PAGE_PATH, diff --git a/src/components/Details/details.util.js b/src/components/Details/details.util.js index 5a67137aa3..982ce421d5 100644 --- a/src/components/Details/details.util.js +++ b/src/components/Details/details.util.js @@ -40,7 +40,9 @@ import { FUNCTION_TYPE_LOCAL, MODEL_ENDPOINTS_TAB, MODELS_TAB, - TAG_LATEST + TAG_LATEST, + IS_MF_MODE, + NUCLIO_FUNCTIONS_PATH } from '../../constants' import { generateLinkPath, generateNuclioLink, parseUri } from '../../utils' import { getFunctionImage } from '../FunctionsPage/functions.util' @@ -315,8 +317,9 @@ export const generateRealTimePipelinesContent = selectedItem => { status: selectedItem.state.value, className: selectedItem.state.className, link: generateNuclioLink( - `/projects/${selectedItem.project}/real-time-functions/${nuclioFunctionName}` - ) + `/projects/${selectedItem.project}/${NUCLIO_FUNCTIONS_PATH}/${nuclioFunctionName}` + ), + linkIsExternal: !IS_MF_MODE }, childFunction: { value: selectedItem.childFunctions ?? selectedItem.function_refs ?? [], diff --git a/src/components/JobWizard/JobWizard.jsx b/src/components/JobWizard/JobWizard.jsx index ef83e7f5cc..2afb533317 100644 --- a/src/components/JobWizard/JobWizard.jsx +++ b/src/components/JobWizard/JobWizard.jsx @@ -54,7 +54,8 @@ import { PARAMETERS_STEP, RESOURCES_STEP, RUN_DETAILS_STEP, - SCHEDULE_TAB + SCHEDULE_TAB, + IS_MF_MODE } from '../../constants' import { generateJobRequestData, @@ -363,9 +364,13 @@ const JobWizard = ({ mode, true ) + const credentials = IS_MF_MODE ? null : jobRequestData.function?.metadata?.credentials + if (!IS_MF_MODE) delete jobRequestData.function.metadata + dispatch( editJob({ postData: { + ...(credentials && { credentials }), scheduled_object: jobRequestData, cron_trigger: jobRequestData.schedule }, diff --git a/src/components/JobWizard/JobWizard.util.js b/src/components/JobWizard/JobWizard.util.js index 0e6bb67f5c..1f2b7aa3d1 100644 --- a/src/components/JobWizard/JobWizard.util.js +++ b/src/components/JobWizard/JobWizard.util.js @@ -42,8 +42,11 @@ import { EXISTING_IMAGE_SOURCE, FUNCTION_DEFAULT_HANDLER, HYPERPARAMETER_STRATEGY_STEP, + IS_MF_MODE, + JOB_DEFAULT_OUTPUT_PATH, LIST_TUNING_STRATEGY, MAX_SELECTOR_CRITERIA, + PANEL_DEFAULT_ACCESS_KEY, PANEL_RERUN_MODE, PARAMETERS_FROM_FILE_VALUE, PARAMETERS_FROM_UI_VALUE, @@ -165,8 +168,12 @@ export const generateJobWizardData = ( }, [ADVANCED_STEP]: { inputPath: null, - outputPath: currentProject?.spec?.artifact_path || frontendSpec.default_artifact_path, - apiTokenInput: 'default', + outputPath: IS_MF_MODE + ? currentProject?.spec?.artifact_path || frontendSpec.default_artifact_path + : currentProject?.spec?.artifact_path || + (frontendSpec.ce?.version && frontendSpec.default_artifact_path) || + JOB_DEFAULT_OUTPUT_PATH, + ...(IS_MF_MODE ? { apiTokenInput: 'default' } : { accessKey: true, accessKeyInput: '' }), environmentVariablesTable: parseEnvironmentVariables(environmentVariables) // secretSourcesTable - currently not shown // secretSourcesTable: [] @@ -284,7 +291,16 @@ export const generateJobWizardDefaultData = ( [ADVANCED_STEP]: { inputPath: defaultData.task.spec.input_path, outputPath: defaultData.task.spec.output_path, - apiTokenInput: defaultData.task.spec?.auth?.token_name ?? '', + ...(IS_MF_MODE + ? { apiTokenInput: defaultData.task.spec?.auth?.token_name ?? '' } + : { + accessKey: + defaultData.function?.metadata?.credentials?.access_key === PANEL_DEFAULT_ACCESS_KEY, + accessKeyInput: + defaultData.function?.metadata?.credentials?.access_key === PANEL_DEFAULT_ACCESS_KEY + ? '' + : defaultData.function?.metadata?.credentials?.access_key + }), environmentVariablesTable: parseEnvironmentVariables(defaultData.function?.spec?.env ?? []) // secretSourcesTable - currently not shown // secretSourcesTable: parseSecretSources(defaultData.task.spec.secret_sources) @@ -1014,9 +1030,10 @@ export const generateJobRequestData = ( labels: convertChipsData(labels) }, spec: { - ...(formData[ADVANCED_STEP].apiTokenInput && { - auth: { token_name: formData[ADVANCED_STEP].apiTokenInput } - }), + ...(IS_MF_MODE && + formData[ADVANCED_STEP].apiTokenInput && { + auth: { token_name: formData[ADVANCED_STEP].apiTokenInput } + }), inputs: generateDataInputs(formData[DATA_INPUTS_STEP].dataInputsTable), parameters: generateParameters(formData[PARAMETERS_STEP].parametersTable), // secretSourcesTable - currently not shown @@ -1036,6 +1053,15 @@ export const generateJobRequestData = ( } }, function: { + ...(!IS_MF_MODE && { + metadata: { + credentials: { + access_key: formData[ADVANCED_STEP].accessKey + ? PANEL_DEFAULT_ACCESS_KEY + : formData[ADVANCED_STEP].accessKeyInput + } + } + }), spec: { image: formData[RUN_DETAILS_STEP].image?.imageSource === EXISTING_IMAGE_SOURCE diff --git a/src/components/JobWizard/JobWizardSteps/JobWizardAdvanced/JobWizardAdvanced.jsx b/src/components/JobWizard/JobWizardSteps/JobWizardAdvanced/JobWizardAdvanced.jsx index 6b59fa9d4b..ea00869ac6 100644 --- a/src/components/JobWizard/JobWizardSteps/JobWizardAdvanced/JobWizardAdvanced.jsx +++ b/src/components/JobWizard/JobWizardSteps/JobWizardAdvanced/JobWizardAdvanced.jsx @@ -22,9 +22,9 @@ import PropTypes from 'prop-types' import { useSelector } from 'react-redux' import FormEnvironmentVariablesTable from '../../../../elements/FormEnvironmentVariablesTable/FormEnvironmentVariablesTable' -import { FormInput, FormKeyValueTable } from 'igz-controls/components' +import { FormCheckBox, FormInput, FormKeyValueTable, FormOnChange } from 'igz-controls/components' -import { ADVANCED_STEP, API_TOKEN_TIP } from '../../../../constants' +import { ADVANCED_STEP, API_TOKEN_TIP, IS_MF_MODE } from '../../../../constants' import { secretsKindOptions } from './JobWizardAdvanced.util' import './jobWizardAdvanced.scss' @@ -75,16 +75,38 @@ const JobWizardAdvanced = ({ formState, stepIsActive = false }) => { {!frontendSpec.ce?.version && (
-
- -
+ {IS_MF_MODE ? ( +
+ +
+ ) : ( + <> +
+ +
+ {!formState.values?.[ADVANCED_STEP]?.accessKey && ( +
+ +
+ )} + + )}
)} + {!IS_MF_MODE && formState.values?.[ADVANCED_STEP] !== undefined && stepIsActive && ( + formState.form.change(`${ADVANCED_STEP}.accessKeyInput`, '')} + name={`${ADVANCED_STEP}.accessKey`} + /> + )} ) } diff --git a/src/components/JobWizard/JobWizardSteps/JobWizardAdvanced/jobWizardAdvanced.scss b/src/components/JobWizard/JobWizardSteps/JobWizardAdvanced/jobWizardAdvanced.scss index a9484d1ba1..0a183a6a5f 100644 --- a/src/components/JobWizard/JobWizardSteps/JobWizardAdvanced/jobWizardAdvanced.scss +++ b/src/components/JobWizard/JobWizardSteps/JobWizardAdvanced/jobWizardAdvanced.scss @@ -1,4 +1,8 @@ .job-wizard__advanced { + .access-key-checkbox { + margin: 30px 10px 0 10px; + } + .api-token-field { display: flex; align-items: center; diff --git a/src/components/Jobs/jobs.util.js b/src/components/Jobs/jobs.util.js index 51612e2cc3..8dfc514bf5 100644 --- a/src/components/Jobs/jobs.util.js +++ b/src/components/Jobs/jobs.util.js @@ -41,6 +41,7 @@ import { COMPLETED_STATE, ABORTED_STATE, ABORTING_STATE, + IS_MF_MODE, PENDING_RETRY_STATE } from '../../constants' import { @@ -177,6 +178,13 @@ const generateEditableItem = (functionData, job) => { return { rerun_object: { function: { + ...(!IS_MF_MODE && { + metadata: { + credentials: { + access_key: functionData?.metadata?.credentials?.access_key ?? '' + } + } + }), spec: { env: functionData?.spec.env ?? [], resources: functionData?.spec.resources, @@ -195,7 +203,7 @@ const generateEditableItem = (functionData, job) => { project: job.project }, spec: { - ...(job.auth?.token_name && { auth: { token_name: job.auth.token_name } }), + ...(IS_MF_MODE && job.auth?.token_name && { auth: { token_name: job.auth.token_name } }), hyper_param_options: job.hyper_param_options, function: job.function, handler: job?.handler ?? '', diff --git a/src/components/MonitoringApplicationsPage/MonitoringApplications/monitoringApplications.util.js b/src/components/MonitoringApplicationsPage/MonitoringApplications/monitoringApplications.util.js index 8fbffbaa51..0522b327e5 100644 --- a/src/components/MonitoringApplicationsPage/MonitoringApplications/monitoringApplications.util.js +++ b/src/components/MonitoringApplicationsPage/MonitoringApplications/monitoringApplications.util.js @@ -21,6 +21,7 @@ import classnames from 'classnames' import moment from 'moment' import { generateNuclioLink } from '../../../utils' +import { IS_MF_MODE, NUCLIO_FUNCTIONS_PATH } from '../../../constants' import { formatDatetime } from 'igz-controls/utils/datetime.util' export const generateOperatingFunctionsTable = (functions, projectName) => { @@ -52,8 +53,8 @@ export const generateOperatingFunctionsTable = (functions, projectName) => { return { name: { value: func.name, - href: generateNuclioLink( - `/projects/${projectName}/real-time-functions/${nuclioFunctionName}` + [IS_MF_MODE ? 'link' : 'href']: generateNuclioLink( + `/projects/${projectName}/${NUCLIO_FUNCTIONS_PATH}/${nuclioFunctionName}` ), className: 'table-cell_big' }, diff --git a/src/components/Project/ProjectOverview/ProjectOverview.util.jsx b/src/components/Project/ProjectOverview/ProjectOverview.util.jsx index c1b2ba5fb6..f04649e4a4 100644 --- a/src/components/Project/ProjectOverview/ProjectOverview.util.jsx +++ b/src/components/Project/ProjectOverview/ProjectOverview.util.jsx @@ -23,7 +23,7 @@ import JobWizard from '../../JobWizard/JobWizard' import RegisterArtifactModal from '../../RegisterArtifactModal/RegisterArtifactModal' import RegisterModelModal from '../../../elements/RegisterModelModal/RegisterModelModal' -import { ARTIFACT_TYPE, DATASET_TYPE } from '../../../constants' +import { ARTIFACT_TYPE, DATASET_TYPE, IS_MF_MODE, NUCLIO_FUNCTIONS_PATH } from '../../../constants' import { PRIMARY_BUTTON, TERTIARY_BUTTON } from 'igz-controls/constants' import { generateNuclioLink } from '../../../utils' import { isSubmitDisabled } from 'igz-controls/utils/form.util' @@ -392,8 +392,10 @@ export const getInitialCards = (params, navigate, isDemoMode) => { icon: , label: 'Create real-time function', handleClick: () => ({ - path: generateNuclioLink(`${base_url}/real-time-functions/create-function`), - externalLink: true + path: IS_MF_MODE + ? `/projects/${params.projectName}/${NUCLIO_FUNCTIONS_PATH}/create-function` + : generateNuclioLink(`/projects/${params.projectName}/create-function`), + externalLink: !IS_MF_MODE }), tooltip: 'These are typically used for serving, APIs, and stream processing. Specify the code, resources, and triggers.' @@ -464,8 +466,8 @@ export const getInitialCards = (params, navigate, isDemoMode) => { { id: 'nuclioFunctions', handleClick: () => ({ - path: generateNuclioLink(`${base_url}/real-time-functions`), - externalLink: true + path: generateNuclioLink(`${base_url}/${NUCLIO_FUNCTIONS_PATH}`), + externalLink: !IS_MF_MODE }), label: 'Nuclio functions' }, diff --git a/src/components/Project/project.utils.jsx b/src/components/Project/project.utils.jsx index 94d4e5f4d5..2091bce9b4 100644 --- a/src/components/Project/project.utils.jsx +++ b/src/components/Project/project.utils.jsx @@ -21,7 +21,7 @@ import React from 'react' import JobWizard from '../JobWizard/JobWizard' -import { ARTIFACT_TYPE, DATASET_TYPE } from '../../constants' +import { ARTIFACT_TYPE, DATASET_TYPE, IS_MF_MODE, NUCLIO_FUNCTIONS_PATH } from '../../constants' import { PRIMARY_BUTTON, FORBIDDEN_ERROR_STATUS_CODE } from 'igz-controls/constants' import { showErrorNotification } from 'igz-controls/utils/notification.util' @@ -114,9 +114,11 @@ export const generateCreateNewOptions = ( id: 'createRealTimeFunction', icon: , handler: () => { - const url = generateNuclioLink( - `/projects/${params.projectName}/real-time-functions/create-function` - ) + if (IS_MF_MODE) { + return navigate(`/projects/${params.projectName}/${NUCLIO_FUNCTIONS_PATH}/create-function`) + } + + const url = generateNuclioLink(`/projects/${params.projectName}}/create-function`) if (window.top && window.top !== window.self) { window.top.location.assign(url) diff --git a/src/components/ProjectSettings/ProjectSettings.jsx b/src/components/ProjectSettings/ProjectSettings.jsx index 280713dd15..aa174016da 100644 --- a/src/components/ProjectSettings/ProjectSettings.jsx +++ b/src/components/ProjectSettings/ProjectSettings.jsx @@ -42,7 +42,12 @@ import { } from '../../elements/MembersPopUp/membersReducer' import projectsIguazioApi from '../../api/projects-iguazio-api' import { DANGER_BUTTON, TERTIARY_BUTTON } from 'igz-controls/constants' -import { PROJECTS_SETTINGS_MEMBERS_TAB, PROJECTS_SETTINGS_SECRETS_TAB } from '../../constants' +import { + COMPLETED_STATE, + IS_MF_MODE, + PROJECTS_SETTINGS_MEMBERS_TAB, + PROJECTS_SETTINGS_SECRETS_TAB +} from '../../constants' import { fetchProjects } from '../../reducers/projectReducer' import { onDeleteProject } from '../ProjectsPage/projects.util' import { setNotification } from 'igz-controls/reducers/notificationReducer' @@ -71,9 +76,12 @@ const ProjectSettings = () => { ) const userIsProjectOwner = useMemo(() => { - const activeUsername = membersState?.activeUser?.data?.attributes?.username - const ownerUsername = membersState?.projectInfo?.owner?.username - return Boolean(activeUsername && activeUsername === ownerUsername) + if (IS_MF_MODE) { + const activeUsername = membersState?.activeUser?.data?.attributes?.username + const ownerUsername = membersState?.projectInfo?.owner?.username + return Boolean(activeUsername && activeUsername === ownerUsername) + } + return membersState?.activeUser?.data?.id === membersState?.projectInfo?.owner.id }, [membersState]) const userIsSystemAdmin = useMemo( @@ -98,59 +106,162 @@ const ProjectSettings = () => { }) }, [dispatch, params.projectName]) + const fetchProjectIdAndOwner = useCallback(() => { + return projectsIguazioApi + .getProjects({ + params: { 'filter[name]': params.projectName, include: 'owner' } + }) + .then(projects => { + const currentProjectInfo = projects.data + const currentProjectData = currentProjectInfo.data?.[0] + const projectId = currentProjectData?.id + const ownerId = currentProjectData?.relationships?.owner?.data?.id ?? '' + const ownerInfo = currentProjectInfo?.included.find(data => data.id === ownerId) + const { + attributes: { username = '', first_name: firstName = '', last_name: lastName = '' } = {} + } = ownerInfo ?? {} + const payload = { + id: projectId, + owner: { id: ownerId, username, firstName, lastName } + } + + membersDispatch({ + type: membersActions.SET_PROJECT_INFO, + payload + }) + + return payload + }) + }, [params.projectName]) + + const fetchProjectMembers = useCallback( + (projectId, owner) => { + return projectsIguazioApi + .getProjectMembers(projectId) + .then(membersResponse => generateMembers(membersResponse, membersDispatch, owner)) + .catch(error => showErrorNotification(dispatch, error, 'Failed to fetch project members')) + }, + [dispatch] + ) + + const fetchProjectMembersVisibility = project => { + projectsIguazioApi + .getProjectMembersVisibility(project) + .then(() => { + setProjectMembersIsShown(true) + }) + .catch(() => { + setProjectMembersIsShown(false) + }) + } + const fetchActiveUser = () => { projectsIguazioApi.getActiveUser().then(response => { - const activeUser = response.data - const relationships = activeUser.relationships || [] - - const userGroupNames = new Set( - relationships - .filter(rel => rel['@type']?.includes('usergroup')) - .map(rel => rel.spec?.name) - .filter(Boolean) - ) - - const userPoliciesCollection = new Set( - relationships - .filter(rel => rel['@type']?.includes('policy.Policy')) - .map(rel => rel.spec?.displayName) - .filter(Boolean) - ) - - membersDispatch({ - type: membersActions.SET_ACTIVE_USER, - payload: { - data: { - id: activeUser.metadata?.id, - attributes: { - username: activeUser.metadata?.username, - user_policies_collection: userPoliciesCollection, - user_group_names: userGroupNames + if (IS_MF_MODE) { + const activeUser = response.data + const relationships = activeUser.relationships || [] + + const userGroupNames = new Set( + relationships + .filter(rel => rel['@type']?.includes('usergroup')) + .map(rel => rel.spec?.name) + .filter(Boolean) + ) + + const userPoliciesCollection = new Set( + relationships + .filter(rel => rel['@type']?.includes('policy.Policy')) + .map(rel => rel.spec?.displayName) + .filter(Boolean) + ) + + membersDispatch({ + type: membersActions.SET_ACTIVE_USER, + payload: { + data: { + id: activeUser.metadata?.id, + attributes: { + username: activeUser.metadata?.username, + user_policies_collection: userPoliciesCollection, + user_group_names: userGroupNames + } } } - } - }) + }) + } else { + const activeUser = response.data + activeUser.data.attributes.user_policies_collection = new Set([ + ...activeUser.data.attributes.assigned_policies, + ...(activeUser.included?.reduce?.( + (policies, group) => [...policies, ...group.attributes.assigned_policies], + [] + ) || []) + ]) + + membersDispatch({ + type: membersActions.SET_ACTIVE_USER, + payload: activeUser + }) + } }) } + const fetchProjectOwnerVisibility = project => { + projectsIguazioApi + .getProjectOwnerVisibility(project) + .then(() => { + setProjectOwnerIsShown(true) + }) + .catch(() => { + setProjectOwnerIsShown(false) + }) + } + const fetchProjectUsersData = useCallback(() => { if (projectMembershipIsEnabled) { - fetchActiveUser() + if (IS_MF_MODE) { + fetchActiveUser() - fetchProjectPolicies() - .catch(() => { - setProjectMembersIsShown(false) - setProjectOwnerIsShown(false) - }) - .finally(() => - membersDispatch({ - type: membersActions.GET_PROJECT_USERS_DATA_END + fetchProjectPolicies() + .catch(() => { + setProjectMembersIsShown(false) + setProjectOwnerIsShown(false) }) - ) + .finally(() => + membersDispatch({ + type: membersActions.GET_PROJECT_USERS_DATA_END + }) + ) + } else { + fetchProjectOwnerVisibility(params.projectName) + fetchProjectIdAndOwner() + .then(({ id: projectId, owner }) => { + fetchActiveUser() + fetchProjectMembersVisibility(params.projectName) + + return fetchProjectMembers(projectId, owner) + }) + .catch(() => { + setProjectMembersIsShown(false) + }) + .finally(() => + membersDispatch({ + type: membersActions.GET_PROJECT_USERS_DATA_END + }) + ) + } } - }, [fetchProjectPolicies, projectMembershipIsEnabled]) + }, [ + fetchProjectIdAndOwner, + fetchProjectMembers, + fetchProjectPolicies, + params.projectName, + projectMembershipIsEnabled + ]) useEffect(() => { + if (!IS_MF_MODE) return + const activeUsername = membersState?.activeUser?.data?.attributes?.username const projectId = membersState?.projectInfo?.id @@ -166,17 +277,32 @@ const ProjectSettings = () => { projectMembersTabIsShown ]) - const changeMembersCallback = userIsStillMember => { - membersDispatch({ - type: membersActions.GET_PROJECT_USERS_DATA_BEGIN - }) + const changeMembersCallback = IS_MF_MODE + ? userIsStillMember => { + membersDispatch({ + type: membersActions.GET_PROJECT_USERS_DATA_BEGIN + }) - if (userIsStillMember) { - fetchProjectPolicies() - .then(() => { - membersDispatch({ - type: membersActions.GET_PROJECT_USERS_DATA_END - }) + if (userIsStillMember) { + fetchProjectPolicies() + .then(() => { + membersDispatch({ + type: membersActions.GET_PROJECT_USERS_DATA_END + }) + dispatch( + setNotification({ + status: 200, + id: Math.random(), + message: 'Members updated successfully' + }) + ) + }) + .catch(() => { + membersDispatch({ + type: membersActions.GET_PROJECT_USERS_DATA_END + }) + }) + } else { dispatch( setNotification({ status: 200, @@ -184,32 +310,77 @@ const ProjectSettings = () => { message: 'Members updated successfully' }) ) + navigate('/projects/') + } + } + : (jobId, userIsValid) => { + const fetchJob = () => { + projectsIguazioApi + .getProjectJob(jobId) + .then(response => { + if (response.data.data.attributes.state !== COMPLETED_STATE) { + setTimeout(fetchJob, 1000) + } else { + if (userIsValid) { + fetchProjectMembers( + membersState.projectInfo.id, + membersState.projectInfo.owner + ).then(() => { + membersDispatch({ + type: membersActions.GET_PROJECT_USERS_DATA_END + }) + dispatch( + setNotification({ + status: 200, + id: Math.random(), + message: 'Members updated successfully' + }) + ) + }) + } else { + dispatch( + setNotification({ + status: 200, + id: Math.random(), + message: 'Members updated successfully' + }) + ) + navigate('/projects/') + } + } + }) + .catch(() => { + membersDispatch({ + type: membersActions.GET_PROJECT_USERS_DATA_END + }) + }) + } + + membersDispatch({ + type: membersActions.GET_PROJECT_USERS_DATA_BEGIN }) - .catch(() => { - membersDispatch({ - type: membersActions.GET_PROJECT_USERS_DATA_END - }) - }) - } else { - dispatch( - setNotification({ - status: 200, - id: Math.random(), - message: 'Members updated successfully' - }) - ) - navigate('/projects/') - } - } + + fetchJob() + } const changeOwnerCallback = () => { - const prevOwnerUsername = membersState.projectInfo.owner.username + if (IS_MF_MODE) { + const prevOwnerUsername = membersState.projectInfo.owner.username - return fetchProjectPolicies().then(() => { - if (!membersState.members.some(member => member.id === prevOwnerUsername)) { - navigate('/projects/') - } - }) + return fetchProjectPolicies().then(() => { + if (!membersState.members.some(member => member.id === prevOwnerUsername)) { + navigate('/projects/') + } + }) + } else { + const prevOwner = membersState.projectInfo.owner.id + + return fetchProjectIdAndOwner().then(() => { + if (!membersState.users.some(member => member.id === prevOwner)) { + navigate('/projects/') + } + }) + } } const resetProjectData = useCallback(() => { diff --git a/src/components/ProjectSettings/projectSettings.util.jsx b/src/components/ProjectSettings/projectSettings.util.jsx index e06e01f908..2b076dbebc 100644 --- a/src/components/ProjectSettings/projectSettings.util.jsx +++ b/src/components/ProjectSettings/projectSettings.util.jsx @@ -18,10 +18,12 @@ under the Apache 2.0 license is conditioned upon your compliance with such restriction. */ import React from 'react' +import { forEach, groupBy } from 'lodash' import { membersActions } from '../../elements/MembersPopUp/membersReducer' import { + IS_MF_MODE, PROJECTS_SETTINGS_GENERAL_TAB, PROJECTS_SETTINGS_PAGE, PROJECTS_SETTINGS_MEMBERS_TAB, @@ -76,7 +78,7 @@ const addMember = (members, name, id, type, initialRole, role) => { }) } -export const generateMembers = (policiesResponse, membersDispatch) => { +const generateMembersMF = (policiesResponse, membersDispatch) => { const members = [] const memberSet = new Set() const policies = policiesResponse.data.items || [] @@ -132,6 +134,78 @@ export const generateMembers = (policiesResponse, membersDispatch) => { }) } +const isOwnerInMembersList = (ownerId, membersList) => { + return membersList.some(member => { + return member.id === ownerId + }) +} + +const generateMembersIGZ3 = (membersResponse, membersDispatch, owner) => { + const members = [] + const { + project_authorization_role: projectAuthorizationRoles = [], + user: users = [], + user_group: userGroups = [] + } = groupBy(membersResponse.data.included, includeItem => { + return includeItem.type + }) + + membersDispatch({ + type: membersActions.SET_PROJECT_AUTHORIZATION_ROLES, + payload: projectAuthorizationRoles + }) + membersDispatch({ + type: membersActions.SET_USERS, + payload: users + }) + membersDispatch({ + type: membersActions.SET_USER_GROUPS, + payload: userGroups + }) + + if (owner.id && !isOwnerInMembersList(owner.id, users)) { + addMember(members, owner.username, owner.id, USER_ROLE, OWNER_ROLE, OWNER_ROLE) + } + + projectAuthorizationRoles.forEach(role => { + if (role.relationships) { + forEach(role.relationships, relationData => { + relationData.data.forEach(identity => { + const identityList = identity.type === USER_ROLE ? users : userGroups + + const { + attributes: { name, username }, + id, + type + } = identityList.find(identityData => { + return identityData.id === identity.id + }) + + addMember( + members, + type === USER_ROLE ? username : name, + id, + type, + owner.id === id ? OWNER_ROLE : role.attributes.name, + owner.id === id ? OWNER_ROLE : role.attributes.name + ) + }) + }) + } + }) + + membersDispatch({ + type: membersActions.SET_MEMBERS_ORIGINAL, + payload: members + }) + membersDispatch({ + type: membersActions.SET_MEMBERS, + payload: members + }) +} + +export const generateMembers = IS_MF_MODE ? generateMembersMF : generateMembersIGZ3 + export const isProjectMembersTabShown = ( projectMembershipIsEnabled, userIsProjectOwner, @@ -143,14 +217,27 @@ export const isProjectMembersTabShown = ( const userIsProjectSecurityAdmin = activeUser.data?.attributes?.user_policies_collection?.has('Project Security Admin') ?? false - const activeUsername = activeUser.data?.attributes?.username - const userIsAdmin = members.some( - member => - member.role === ADMIN_ROLE && - (member.id === activeUsername || - (member.type === USER_GROUP_ROLE && - activeUser.data?.attributes?.user_group_names?.has(member.id))) - ) + + const userIsAdmin = IS_MF_MODE + ? (() => { + const activeUsername = activeUser.data?.attributes?.username + return members.some( + member => + member.role === ADMIN_ROLE && + (member.id === activeUsername || + (member.type === USER_GROUP_ROLE && + activeUser.data?.attributes?.user_group_names?.has(member.id))) + ) + })() + : members.some( + member => + member.role === ADMIN_ROLE && + (member.id === activeUser.data?.id || + (member.type === USER_GROUP_ROLE && + activeUser.data?.relationships?.user_groups?.data?.some?.( + group => group.id === member.id + ))) + ) return userIsProjectOwner || userIsAdmin || userIsProjectSecurityAdmin } diff --git a/src/constants.js b/src/constants.js index adf47f206e..47ba2ba606 100644 --- a/src/constants.js +++ b/src/constants.js @@ -19,6 +19,8 @@ such restriction. */ /*=========== GENERAL =============*/ +export const IS_MF_MODE = import.meta.env.VITE_FEDERATION === 'true' + export const SET_LOADING = 'SET_LOADING' export const AZURE_STORAGE_INPUT_PATH_SCHEME = 'az://' @@ -145,6 +147,8 @@ export const PROJECT_QUICK_ACTIONS_PAGE = 'quick-actions' export const ALL_VERSIONS_PATH = 'all-versions' +export const NUCLIO_FUNCTIONS_PATH = IS_MF_MODE ? 'real-time-functions' : 'functions' + /*=========== CONSUMER_GROUPS =============*/ export const CONSUMER_GROUP_PAGE = 'CONSUMER_GROUP' diff --git a/src/elements/ChangeOwnerPopUp/ChangeOwnerPopUp.jsx b/src/elements/ChangeOwnerPopUp/ChangeOwnerPopUp.jsx index 9fc0885d35..27bce99e3e 100644 --- a/src/elements/ChangeOwnerPopUp/ChangeOwnerPopUp.jsx +++ b/src/elements/ChangeOwnerPopUp/ChangeOwnerPopUp.jsx @@ -34,10 +34,13 @@ import { } from 'igz-controls/constants' import { deleteUnsafeHtml } from 'igz-controls/utils/string.util' import { getErrorMsg } from 'igz-controls/utils/common.util' +import { isIgzVersionCompatible } from '../../utils/isIgzVersionCompatible' import { setNotification } from 'igz-controls/reducers/notificationReducer' import { showErrorNotification } from 'igz-controls/utils/notification.util' import { useDetectOutsideClick } from 'igz-controls/hooks' +import { IS_MF_MODE, USER_ROLE } from '../../constants' + import SearchIcon from 'igz-controls/images/search.svg?react' import './changeOwnerPopUp.scss' @@ -71,33 +74,80 @@ const ChangeOwnerPopUp = ({ changeOwnerCallback, projectId }) => { setShowSuggestionList(false) } + const applyChangesMF = () => { + projectsIguazioApi + .updateProjectOwner(projectId, newOwnerId) + .then(changeOwnerCallback) + .then(() => { + dispatch( + setNotification({ + status: 200, + id: Math.random(), + message: 'Owner updated successfully' + }) + ) + }) + .catch(error => { + const customErrorMsg = + error.response?.status === FORBIDDEN_ERROR_STATUS_CODE + ? 'Missing edit permission for the project' + : getErrorMsg(error, 'Failed to edit project data') + + showErrorNotification(dispatch, error, '', customErrorMsg, () => applyChanges()) + }) + .finally(handleOnClose) + } + + const applyChangesIGZ3 = () => { + const projectData = { + data: { + type: 'project', + attributes: {}, + relationships: { + owner: { + data: { + id: newOwnerId, + type: USER_ROLE + } + } + } + } + } + + projectsIguazioApi + .editProject(projectId, projectData) + .then(changeOwnerCallback) + .then(() => { + dispatch( + setNotification({ + status: 200, + id: Math.random(), + message: 'Owner updated successfully' + }) + ) + }) + .catch(error => { + const customErrorMsg = + error.response?.status === FORBIDDEN_ERROR_STATUS_CODE + ? 'Missing edit permission for the project' + : getErrorMsg(error, 'Failed to edit project data') + + showErrorNotification(dispatch, error, '', customErrorMsg, () => applyChanges(newOwnerId)) + }) + .finally(handleOnClose) + } + const applyChanges = () => { if (newOwnerId) { - projectsIguazioApi - .updateProjectOwner(projectId, newOwnerId) - .then(changeOwnerCallback) - .then(() => { - dispatch( - setNotification({ - status: 200, - id: Math.random(), - message: 'Owner updated successfully' - }) - ) - }) - .catch(error => { - const customErrorMsg = - error.response?.status === FORBIDDEN_ERROR_STATUS_CODE - ? 'Missing edit permission for the project' - : getErrorMsg(error, 'Failed to edit project data') - - showErrorNotification(dispatch, error, '', customErrorMsg, () => applyChanges()) - }) - .finally(handleOnClose) + if (IS_MF_MODE) { + applyChangesMF() + } else { + applyChangesIGZ3() + } } } - const generateSuggestionList = async (memberName, resolve) => { + const generateSuggestionListMF = async (memberName, resolve) => { let formattedUsers = [] try { @@ -121,6 +171,43 @@ const ChangeOwnerPopUp = ({ changeOwnerCallback, projectId }) => { resolve(formattedUsers) } + const generateSuggestionListIGZ3 = async (memberName, resolve) => { + const params = { + 'filter[assigned_policies]': '[$contains_any]Developer,Project Admin', + 'page[size]': 200 + } + const requiredIgzVersion = '3.5.3' + let formattedUsers = [] + + if (isIgzVersionCompatible(requiredIgzVersion)) { + params['filter[username]'] = `[$contains_istr]${memberName}` + } + + try { + const response = await projectsIguazioApi.getScrubbedUsers({ params }) + const { + data: { data: users } + } = response + + formattedUsers = users.map(user => { + return { + name: `${user.attributes.first_name} ${user.attributes.last_name}`, + username: user.attributes.username, + label: `${user.attributes.first_name} ${user.attributes.last_name} (${user.attributes.username})`, + id: user.id, + role: '' + } + }) + setUsersList(formattedUsers) + } catch (error) { + showErrorNotification(dispatch, error, 'Failed to fetch users') + } + + resolve(formattedUsers) + } + + const generateSuggestionList = IS_MF_MODE ? generateSuggestionListMF : generateSuggestionListIGZ3 + const onSearchChange = debounce(memberName => { const memberNameEscaped = deleteUnsafeHtml(memberName) setSearchValue(memberNameEscaped) diff --git a/src/elements/MembersPopUp/MembersPopUp.jsx b/src/elements/MembersPopUp/MembersPopUp.jsx index ad19e366ec..34c0707fb0 100644 --- a/src/elements/MembersPopUp/MembersPopUp.jsx +++ b/src/elements/MembersPopUp/MembersPopUp.jsx @@ -34,10 +34,11 @@ import projectsIguazioApi from '../../api/projects-iguazio-api' import { FORBIDDEN_ERROR_STATUS_CODE } from 'igz-controls/constants' import { getErrorMsg } from 'igz-controls/utils/common.util' import { getRoleOptions, initialNewMembersRole, DELETE_MODIFICATION } from './membersPopUp.util' +import { isIgzVersionCompatible } from '../../utils/isIgzVersionCompatible' import { membersActions } from './membersReducer' import { showErrorNotification } from 'igz-controls/utils/notification.util' -import { OWNER_ROLE, USER_GROUP_ROLE, USER_ROLE } from '../../constants' +import { IS_MF_MODE, OWNER_ROLE, USER_GROUP_ROLE, USER_ROLE } from '../../constants' import Add from 'igz-controls/images/add.svg?react' import Close from 'igz-controls/images/close.svg?react' @@ -95,7 +96,7 @@ const MembersPopUp = ({ changeMembersCallback, membersDispatch, membersState }) setMembersData(state => ({ ...state, members: membersCopy })) } - const applyMembersChanges = () => { + const applyMembersChangesMF = () => { const projectName = membersData.projectInfo.id const visibleMembers = membersData.members.filter( member => member.modification !== DELETE_MODIFICATION && member.role !== OWNER_ROLE @@ -149,6 +150,119 @@ const MembersPopUp = ({ changeMembersCallback, membersDispatch, membersState }) handleOnClose() } + const applyMembersChangesIGZ3 = () => { + const changesBody = { + data: { + attributes: { + metadata: { + project_ids: [membersData.projectInfo.id], + notify_by_email: notifyByEmail + }, + requests: [] + } + } + } + const rolesData = {} + const modifiedRoles = Array.from( + membersData.members.reduce((prevValue, member) => { + if (member.modification) { + prevValue.add(member.role) + + if (member.initialRole) { + prevValue.add(member.initialRole) + } + } + + return prevValue + }, new Set()) + ) + const groupedVisibleMembers = groupBy( + membersData.members.filter(member => member.modification !== DELETE_MODIFICATION), + item => item.role + ) + + membersState.projectAuthorizationRoles.forEach(roleData => { + rolesData[roleData.attributes.name] = roleData + }) + + changesBody.data.attributes.requests = modifiedRoles.map(modifiedRole => { + const membersCopy = groupedVisibleMembers[modifiedRole] ?? [] + + return { + method: 'put', + resource: `project_authorization_roles/${rolesData[modifiedRole].id}`, + body: { + data: { + type: rolesData[modifiedRole].type, + attributes: { + name: modifiedRole, + permissions: rolesData[modifiedRole].attributes.permissions + }, + relationships: { + project: { + data: { + type: 'project', + id: membersData.projectInfo.id + } + }, + principal_users: { + data: membersCopy + .filter(member => member.type === USER_ROLE) + .map(member => { + return { id: member.id, type: member.type } + }) + }, + principal_user_groups: { + data: membersCopy + .filter(member => member.type === 'user_group') + .map(member => { + return { id: member.id, type: member.type } + }) + } + } + } + } + } + }) + + membersDispatch({ + type: membersActions.SET_MEMBERS, + payload: membersData.members + }) + + projectsIguazioApi + .updateProjectMembers(changesBody) + .then(response => { + const validMember = membersData.members?.some( + member => + member.modification !== DELETE_MODIFICATION && + (member.id === membersData.activeUser.data?.id || + (member.type === USER_GROUP_ROLE && + membersData.activeUser.data?.relationships?.user_groups?.data?.some?.( + group => group.id === member.id + ))) + ) + const userIsProjectSecurityAdmin = + membersData.activeUser.data?.attributes?.user_policies_collection?.has( + 'Project Security Admin' + ) ?? false + + changeMembersCallback(response.data.data.id, validMember || userIsProjectSecurityAdmin) + }) + .catch(error => { + const customErrorMsg = + error.response?.status === FORBIDDEN_ERROR_STATUS_CODE + ? 'Missing edit permission for the project' + : getErrorMsg(error, 'Failed to edit project data') + + showErrorNotification(dispatch, error, '', customErrorMsg, () => applyMembersChanges()) + }) + + handleOnClose() + } + + const applyMembersChanges = IS_MF_MODE ? applyMembersChangesMF : applyMembersChangesIGZ3 + const areChangesMade = () => { return membersData.members.some(member => member.modification !== '') } @@ -191,7 +305,7 @@ const MembersPopUp = ({ changeMembersCallback, membersDispatch, membersState }) } } - const generateUsersSuggestionList = debounce(searchQuery => { + const generateUsersSuggestionListMF = debounce(searchQuery => { const getUsersPromise = projectsIguazioApi.searchUsersMetadata(searchQuery) const getUserGroupsPromise = projectsIguazioApi.searchGroupsMetadata(searchQuery) @@ -217,6 +331,74 @@ const MembersPopUp = ({ changeMembersCallback, membersDispatch, membersState }) }) }, 400) + const generateUsersSuggestionListIGZ3 = debounce(searchQuery => { + const requiredIgzVersion = '3.5.3' + let paramsScrubbedUsers = { + 'filter[username]': `[$match-i]^.*${searchQuery}.*$`, + 'page[size]': 200 + } + let paramsUserGroups = { + 'filter[name]': `[$match-i]^.*${searchQuery}.*$`, + 'page[size]': 200 + } + + if (isIgzVersionCompatible(requiredIgzVersion)) { + paramsScrubbedUsers['filter[username]'] = `[$contains_istr]${searchQuery}` + paramsUserGroups['filter[name]'] = `[$contains_istr]${searchQuery}` + } + + const getUsersPromise = projectsIguazioApi.getScrubbedUsers({ + params: paramsScrubbedUsers + }) + const getUserGroupsPromise = projectsIguazioApi.getScrubbedUserGroups({ + params: paramsUserGroups + }) + const suggestionList = [] + + Promise.all([getUsersPromise, getUserGroupsPromise]) + .then(response => { + response.forEach(identityResponse => { + identityResponse.data.data.forEach(identity => { + const existingMember = membersData.members.find( + member => member.id === identity.id && member.modification !== DELETE_MODIFICATION + ) + + suggestionList.push({ + label: + identity.type === USER_ROLE + ? identity.attributes.username + : identity.attributes.name, + id: identity.id, + subLabel: existingMember?.role ?? '', + disabled: Boolean(existingMember), + icon: + identity.type === USER_ROLE ? ( + + + + ) : ( + + + + ), + ui: { + type: identity.type + } + }) + }) + }) + + setNewMembersSuggestionList(suggestionList) + }) + .catch(error => { + showErrorNotification(dispatch, error, 'Failed to fetch users') + }) + }, 400) + + const generateUsersSuggestionList = IS_MF_MODE + ? generateUsersSuggestionListMF + : generateUsersSuggestionListIGZ3 + return (
diff --git a/src/elements/MembersPopUp/membersReducer.js b/src/elements/MembersPopUp/membersReducer.js index d4f25f4a9b..865deb46d0 100644 --- a/src/elements/MembersPopUp/membersReducer.js +++ b/src/elements/MembersPopUp/membersReducer.js @@ -42,6 +42,8 @@ export const initialMembersState = { } }, projectAuthorizationRoles: [], + users: [], + userGroups: [], membersOriginal: [], members: [], groupedOriginalMembers: [], @@ -57,7 +59,9 @@ export const membersActions = { SET_MEMBERS: 'SET_MEMBERS', SET_MEMBERS_ORIGINAL: 'SET_MEMBERS_ORIGINAL', SET_PROJECT_AUTHORIZATION_ROLES: 'SET_PROJECT_AUTHORIZATION_ROLES', - SET_PROJECT_INFO: 'SET_PROJECT_INFO' + SET_PROJECT_INFO: 'SET_PROJECT_INFO', + SET_USERS: 'SET_USERS', + SET_USER_GROUPS: 'SET_USER_GROUPS' } export const membersReducer = (state, { type, payload }) => { @@ -102,6 +106,16 @@ export const membersReducer = (state, { type, payload }) => { ...state, projectInfo: payload } + case membersActions.SET_USERS: + return { + ...state, + users: payload + } + case membersActions.SET_USER_GROUPS: + return { + ...state, + userGroups: payload + } default: return state } diff --git a/src/elements/ProjectFunctions/ProjectFunctions.jsx b/src/elements/ProjectFunctions/ProjectFunctions.jsx index 2c3cad4d24..48c0d872f7 100644 --- a/src/elements/ProjectFunctions/ProjectFunctions.jsx +++ b/src/elements/ProjectFunctions/ProjectFunctions.jsx @@ -32,7 +32,9 @@ import { FAILED_STATE, FUNCTION_READY_STATE, REQUEST_CANCELED, - RUNNING_STATE + RUNNING_STATE, + NUCLIO_FUNCTIONS_PATH, + IS_MF_MODE } from '../../constants' import { fetchApiGateways } from '../../reducers/nuclioReducer' import { generateNuclioLink } from '../../utils' @@ -82,7 +84,9 @@ const ProjectFunctions = ({ nuclioStreamsAreEnabled, project }) => { label: 'Running', className: RUNNING_STATE, status: RUNNING_STATE, - href: generateNuclioLink(`/projects/${params.projectName}/real-time-functions`), + [IS_MF_MODE ? 'link' : 'href']: generateNuclioLink( + `/projects/${params.projectName}/${NUCLIO_FUNCTIONS_PATH}` + ), loading: nuclioStore.loading }, failed: { @@ -91,14 +95,18 @@ const ProjectFunctions = ({ nuclioStreamsAreEnabled, project }) => { label: 'Failed', status: FAILED_STATE, className: functionsFailed > 0 ? FAILED_STATE : RUNNING_STATE, - href: generateNuclioLink(`/projects/${params.projectName}/real-time-functions`), + [IS_MF_MODE ? 'link' : 'href']: generateNuclioLink( + `/projects/${params.projectName}/${NUCLIO_FUNCTIONS_PATH}` + ), loading: nuclioStore.loading }, apiGateways: { value: nuclioStore.apiGateways, label: 'API gateways', className: RUNNING_STATE, - href: generateNuclioLink(`/projects/${params.projectName}/api-gateways`), + [IS_MF_MODE ? 'link' : 'href']: generateNuclioLink( + `/projects/${params.projectName}/api-gateways` + ), loading: nuclioStore.loading }, ...(nuclioStreamsAreEnabled && { @@ -149,8 +157,8 @@ const ProjectFunctions = ({ nuclioStreamsAreEnabled, project }) => { ? // if nuclio func is generated by MLRun then name only in this case starts with project name and we need to slice it func.metadata.name.slice(params.projectName.length + 1) : func.metadata.name, - href: generateNuclioLink( - `/projects/${params.projectName}/real-time-functions/${func.metadata.name}` + [IS_MF_MODE ? 'link' : 'href']: generateNuclioLink( + `/projects/${params.projectName}/${NUCLIO_FUNCTIONS_PATH}/${func.metadata.name}` ), className: 'table-cell_big' }, @@ -177,7 +185,11 @@ const ProjectFunctions = ({ nuclioStreamsAreEnabled, project }) => { loading: nuclioStore.loading }} footerLinkText="All real-time functions" - href={generateNuclioLink(`/projects/${params.projectName}/real-time-functions`)} + {...{ + [IS_MF_MODE ? 'link' : 'href']: generateNuclioLink( + `/projects/${params.projectName}/${NUCLIO_FUNCTIONS_PATH}` + ) + }} statistics={functions} subTitle="Recent real-time functions" table={functionsTable} diff --git a/src/httpClient.js b/src/httpClient.js index 47425cff32..142be3a0ba 100755 --- a/src/httpClient.js +++ b/src/httpClient.js @@ -69,7 +69,12 @@ export const nuclioHttpClient = axios.create({ }) export const iguazioHttpClient = axios.create({ - baseURL: import.meta.env.MODE === 'production' ? '/igz/api' : '/iguazio/api', + baseURL: + import.meta.env.MODE === 'production' + ? import.meta.env.VITE_FEDERATION === 'true' + ? '/igz/api' + : '/api' + : '/iguazio/api', headers }) diff --git a/src/layout/Navbar/navbar.util.jsx b/src/layout/Navbar/navbar.util.jsx index e52bbb02ad..b8806e65ab 100644 --- a/src/layout/Navbar/navbar.util.jsx +++ b/src/layout/Navbar/navbar.util.jsx @@ -23,7 +23,9 @@ import { DOCUMENTS_PAGE, LLM_PROMPTS_PAGE, PROJECT_MONITOR, - PROJECT_QUICK_ACTIONS_PAGE + PROJECT_QUICK_ACTIONS_PAGE, + NUCLIO_FUNCTIONS_PATH, + IS_MF_MODE } from '../../constants' import { generateNuclioLink } from '../../utils' @@ -123,15 +125,15 @@ export const getLinks = projectName => { icon: , id: 'real-time-functions', label: 'Real-time functions', - link: generateNuclioLink(`${pathname}/real-time-functions`), - externalLink: true + link: generateNuclioLink(`${pathname}/${NUCLIO_FUNCTIONS_PATH}`), + externalLink: !IS_MF_MODE }, { icon: , id: 'api-gateways', label: 'API gateways', link: generateNuclioLink(`${pathname}/api-gateways`), - externalLink: true + externalLink: !IS_MF_MODE } ] } diff --git a/src/lazyWithRetry.js b/src/lazyWithRetry.js index f44786a6bb..4be065b970 100644 --- a/src/lazyWithRetry.js +++ b/src/lazyWithRetry.js @@ -19,26 +19,33 @@ such restriction. */ import { lazy } from 'react' // a function to retry loading a chunk to avoid chunk load error for out of date code -export const lazyRetry = componentImport => - lazy(() => { +export const lazyRetry = (componentImport, name) => { + if (!name) { + throw new Error('lazyRetry requires a name for the component being imported') + } + + return lazy(() => { return new Promise((resolve, reject) => { // check if the window has already been refreshed - const hasRefreshed = JSON.parse( - window.sessionStorage.getItem('retry-lazy-refreshed') || 'false' - ) + const componentStorageKey = `retry-lazy-refreshed-${name}` + const hasRefreshed = JSON.parse(window.sessionStorage.getItem(componentStorageKey) || 'false') // try to import the component componentImport() .then(component => { - window.sessionStorage.setItem('retry-lazy-refreshed', 'false') // success so reset the refresh + window.sessionStorage.setItem(componentStorageKey, 'false') // success so reset the refresh resolve(component) }) .catch(error => { + window.sessionStorage.setItem(componentStorageKey, 'true') if (!hasRefreshed) { // not been refreshed yet window.sessionStorage.setItem('retry-lazy-refreshed', 'true') // we are now going to refresh return window.location.reload() // refresh the page } - reject(error) // Default error behaviour as already tried refresh + // eslint-disable-next-line no-console + console.error(`Failed to load component ${name} after retrying`, error) + reject(error) // Default error behavior as already tried refresh }) }) }) +} diff --git a/src/main.jsx b/src/main.jsx index 30c0994e12..40ada1bd44 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -2,6 +2,10 @@ import App from './App' import { Provider } from 'react-redux' import store from './store/toolkitStore' import ErrorBoundary from './components/ErrorBoundary/ErrorBoundary' +// Eagerly initialize react-text-mask in the entry chunk so its CJS/UMD module +// is resolved against React before any lazy chunks try to reference it. +// TODO: remove this import when custom react-text-mask will be included in our codebase (in progress) or when https://github.com/rollup/rollup/issues/6296 fixed (deps of vite) +import 'react-text-mask' const RemoteApp = () => { return ( diff --git a/src/reducers/jobReducer.js b/src/reducers/jobReducer.js index ac3f18890c..259d4a50da 100644 --- a/src/reducers/jobReducer.js +++ b/src/reducers/jobReducer.js @@ -27,7 +27,8 @@ import { LABELS_FILTER, NAME_FILTER, STATUS_FILTER, - TYPE_FILTER + TYPE_FILTER, + IS_MF_MODE } from '../constants' import { largeResponseCatchHandler } from '../utils/largeResponseCatchHandler' import functionsApi from '../api/functions-api' @@ -65,6 +66,13 @@ const initialState = { } }, function: { + ...(!IS_MF_MODE && { + metadata: { + credentials: { + access_key: '' + } + } + }), spec: { env: [], node_selector: {}, diff --git a/src/utils/createApplicationContent.jsx b/src/utils/createApplicationContent.jsx index 301e12846d..e9be466a5c 100644 --- a/src/utils/createApplicationContent.jsx +++ b/src/utils/createApplicationContent.jsx @@ -22,7 +22,7 @@ import { capitalize } from 'lodash' import { formatDatetime } from 'igz-controls/utils/datetime.util' import { generateNuclioLink } from './parseUri' import { saveAndTransformSearchParams } from 'igz-controls/utils/filter.util' -import { MONITORING_APP_PAGE } from '../constants' +import { IS_MF_MODE, MONITORING_APP_PAGE, NUCLIO_FUNCTIONS_PATH } from '../constants' export const createApplicationContent = (application, projectName) => { const identifierUnique = 'identifierUnique.' + application.name + application.application_class @@ -105,8 +105,10 @@ export const createApplicationContent = (application, projectName) => { value: application.name, className: 'table-cell-2', getLink: () => - generateNuclioLink(`/projects/${projectName}/real-time-functions/${nuclioFunctionName}`), - linkIsExternal: true, + generateNuclioLink( + `/projects/${projectName}/${NUCLIO_FUNCTIONS_PATH}/${nuclioFunctionName}` + ), + linkIsExternal: !IS_MF_MODE, showStatus: true } ] diff --git a/src/utils/createConsumerGroupsContent.js b/src/utils/createConsumerGroupsContent.js index 8dc1accdce..14781c18c4 100644 --- a/src/utils/createConsumerGroupsContent.js +++ b/src/utils/createConsumerGroupsContent.js @@ -19,6 +19,7 @@ such restriction. */ import { generateNuclioLink } from './parseUri' import { getV3ioStreamIdentifier } from './getUniqueIdentifier' +import { IS_MF_MODE, NUCLIO_FUNCTIONS_PATH } from '../constants' const createConsumerGroupsContent = (content, params) => { return content.map(contentItem => { @@ -46,10 +47,10 @@ const createConsumerGroupsContent = (content, params) => { value: contentItem.functionName, getLink: () => { return generateNuclioLink( - `/projects/${params.projectName}/real-time-functions/${contentItem.functionName}` + `/projects/${params.projectName}/${NUCLIO_FUNCTIONS_PATH}/${contentItem.functionName}` ) }, - linkIsExternal: true, + linkIsExternal: !IS_MF_MODE, className: 'table-cell-1' } } diff --git a/src/utils/createRealTimePipelinesContent.js b/src/utils/createRealTimePipelinesContent.js index 2143d53f59..4375fde20e 100644 --- a/src/utils/createRealTimePipelinesContent.js +++ b/src/utils/createRealTimePipelinesContent.js @@ -19,7 +19,13 @@ such restriction. */ import { formatDatetime } from 'igz-controls/utils/datetime.util' -import { DETAILS_MODEL_ENDPOINTS_TAB, MODELS_PAGE, REAL_TIME_PIPELINES_TAB } from '../constants' +import { + DETAILS_MODEL_ENDPOINTS_TAB, + IS_MF_MODE, + MODELS_PAGE, + NUCLIO_FUNCTIONS_PATH, + REAL_TIME_PIPELINES_TAB +} from '../constants' import { typesOfJob } from './jobs.util' import { generateNuclioLink } from './parseUri' @@ -50,9 +56,11 @@ const createRealTimePipelinesContent = (pipelines, projectName) => className: 'table-cell-2', showStatus: true, showTag: true, - linkIsExternal: true, + linkIsExternal: !IS_MF_MODE, getLink: () => - generateNuclioLink(`/projects/${projectName}/real-time-functions/${nuclioFunctionName}`) + generateNuclioLink( + `/projects/${projectName}/${NUCLIO_FUNCTIONS_PATH}/${nuclioFunctionName}` + ) }, { id: `topology.${pipeline.ui.identifierUnique}`, diff --git a/src/utils/nuclio.remotes.utils.js b/src/utils/nuclio.remotes.utils.js index e67f2a3f3c..90bf9f0365 100644 --- a/src/utils/nuclio.remotes.utils.js +++ b/src/utils/nuclio.remotes.utils.js @@ -31,15 +31,10 @@ const ensureNuclioRemote = async () => { throw new Error('[MF] Missing window.mlrunConfig.nuclioRemoteEntryUrl') } - /** - * FIX: Ensure the URL contains the /nuclio-ui proxy path. - * If it's just the domain, append the required prefix. - */ - - // TODO: Automate URL resolution (Local: remove suffix | Prod: append it) - if (!remoteEntryUrl.includes('/nuclio-ui')) { - // remoteEntryUrl = remoteEntryUrl.replace(/\/$/, ''); // Uncomment for Local + if (window.location.hostname !== 'localhost') { remoteEntryUrl = `${remoteEntryUrl.replace(/\/$/, '')}/nuclio-ui` + } else { + remoteEntryUrl = remoteEntryUrl.replace(/\/$/, '') } registerPromise = (async () => { diff --git a/src/utils/parseUri.js b/src/utils/parseUri.js index 2c8571a5a7..b15de1ef1d 100644 --- a/src/utils/parseUri.js +++ b/src/utils/parseUri.js @@ -24,7 +24,8 @@ import { MONITOR_JOBS_TAB, FILES_PAGE, DATASETS_PAGE, - DOCUMENTS_PAGE + DOCUMENTS_PAGE, + IS_MF_MODE } from '../constants' /** @@ -117,11 +118,17 @@ const generateLinkPath = (uri = '') => { } const generateNuclioLink = pathname => { - const base = window.mlrunConfig?.nuclioUiUrl || window.location.origin + if (IS_MF_MODE) return pathname + const base = window.mlrunConfig?.nuclioUiUrl || window.location.origin const cleanPath = pathname.startsWith('/') ? pathname : `/${pathname}` + const linkUrl = new URL(`${base}${cleanPath}`) + + if (window.location.origin !== base) { + linkUrl.searchParams.set('origin', window.location.origin) + } - return new URL(`${base}${cleanPath}`).toString() + return linkUrl.toString() } export { generateLinkPath, generateNuclioLink, parseUri, parseIdentifier } diff --git a/src/utils/projectAuth.util.js b/src/utils/projectAuth.util.js index 19620891c6..ec99b8b0b5 100644 --- a/src/utils/projectAuth.util.js +++ b/src/utils/projectAuth.util.js @@ -27,15 +27,27 @@ export const getActiveUsername = async () => { } export const checkProjectWriteAccess = async (projectName, activeUsername = null) => { - if (!activeUsername) { - activeUsername = await getActiveUsername() - } + if (import.meta.env.VITE_FEDERATION === 'true') { + if (!activeUsername) { + activeUsername = await getActiveUsername() + } - const policiesResponse = await projectsIguazioApi.getProjectPolicies(projectName) - const policies = policiesResponse.data.items || [] - return policies.some( - policy => - WRITE_ROLES.includes(policy.spec.displayName) && - policy.status?.assignedMembers?.some(member => member.id === activeUsername) - ) + const policiesResponse = await projectsIguazioApi.getProjectPolicies(projectName) + const policies = policiesResponse.data.items || [] + return policies.some( + policy => + WRITE_ROLES.includes(policy.spec.displayName) && + policy.status?.assignedMembers?.some(member => member.id === activeUsername) + ) + } else { + return projectsIguazioApi + .getProjectOwnerVisibility(projectName) + .then(() => true) + .catch(() => + projectsIguazioApi + .getProjectWorkflowsUpdateAuthorization(projectName) + .then(() => true) + .catch(() => false) + ) + } }