diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 05685a4945..2b46d5492b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,3 +1,3 @@ docs/* @jfermi -* @devcatalin @topliceanurazvan @mortada-codes @olensmar +* @devcatalin @topliceanurazvan @olensmar diff --git a/.github/workflows/monokle-build-nightly.yml b/.github/workflows/monokle-build-nightly.yml index 1fbe0a6439..b6c01a1f8f 100644 --- a/.github/workflows/monokle-build-nightly.yml +++ b/.github/workflows/monokle-build-nightly.yml @@ -114,6 +114,8 @@ jobs: CSC_KEY_PASSWORD: ${{ secrets.MONOKLE_MACOS_CERTS_PASSWORD }} APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} + APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} USE_HARD_LINKS: false - name: Create release and upload artifacts diff --git a/.github/workflows/monokle-publish-downloads.yml b/.github/workflows/monokle-publish-downloads.yml index 6c2270b2c3..d24139a366 100644 --- a/.github/workflows/monokle-publish-downloads.yml +++ b/.github/workflows/monokle-publish-downloads.yml @@ -108,6 +108,8 @@ jobs: CSC_KEY_PASSWORD: ${{ secrets.MONOKLE_MACOS_CERTS_PASSWORD }} APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} + APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} USE_HARD_LINKS: false # Check Binary Size diff --git a/.github/workflows/monokle-publish-test.yml b/.github/workflows/monokle-publish-test.yml index 06226dae97..8b9b832785 100644 --- a/.github/workflows/monokle-publish-test.yml +++ b/.github/workflows/monokle-publish-test.yml @@ -107,6 +107,8 @@ jobs: CSC_KEY_PASSWORD: ${{ secrets.MONOKLE_MACOS_CERTS_PASSWORD }} APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} + APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} USE_HARD_LINKS: false # Check Binary Size diff --git a/.github/workflows/monokle-publish-updater.yml b/.github/workflows/monokle-publish-updater.yml index 04474b3210..a01a7ec3d4 100644 --- a/.github/workflows/monokle-publish-updater.yml +++ b/.github/workflows/monokle-publish-updater.yml @@ -107,6 +107,8 @@ jobs: CSC_KEY_PASSWORD: ${{ secrets.MONOKLE_MACOS_CERTS_PASSWORD }} APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} + APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} USE_HARD_LINKS: false # Check Binary Size diff --git a/.github/workflows/monokle-publish.yml b/.github/workflows/monokle-publish.yml index 737eb49fd0..a8ba14ba99 100644 --- a/.github/workflows/monokle-publish.yml +++ b/.github/workflows/monokle-publish.yml @@ -102,6 +102,8 @@ jobs: CSC_KEY_PASSWORD: ${{ secrets.MONOKLE_MACOS_CERTS_PASSWORD }} APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} + APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} USE_HARD_LINKS: false # Check Binary Size diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a21d1571e..80937fc39a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [2.4.0](https://github.com/kubeshop/monokle/compare/v2.4.1-nightly-2023-09-28.0...v2.4.0) (2023-09-28) + + +### Features + +* compare clusters in quick cluster mode ([c9c0a12](https://github.com/kubeshop/monokle/commit/c9c0a12cb21eb65d99aa315082ff1501ef692b07)) + ### [2.3.3](https://github.com/kubeshop/monokle/compare/v2.3.3-nightly-2023-07-26.0...v2.3.3) (2023-07-26) diff --git a/electron/app/autoUpdater.ts b/electron/app/autoUpdater.ts index 3a7dd3ce19..791b6abdca 100644 --- a/electron/app/autoUpdater.ts +++ b/electron/app/autoUpdater.ts @@ -1,6 +1,6 @@ import {AppImageUpdater, MacUpdater, NsisUpdater} from 'electron-updater'; -import {GenericServerOptions} from 'electron-updater/node_modules/builder-util-runtime'; +import {GenericServerOptions} from 'builder-util-runtime'; import {join} from 'path'; const isDev = process.env.NODE_ENV === 'development'; diff --git a/electron/app/ipc/ipcListeners.ts b/electron/app/ipc/ipcListeners.ts index 43cecb2dab..b783325518 100644 --- a/electron/app/ipc/ipcListeners.ts +++ b/electron/app/ipc/ipcListeners.ts @@ -45,6 +45,7 @@ import { selectFileDialog, } from '../commands'; import {ProjectNameChange, StorePropagation} from '../models'; +import '../services/cloud/ipc'; import '../services/cluster/ipc'; import {downloadPlugin, updatePlugin} from '../services/pluginService'; import { diff --git a/electron/app/ipc/ipcMainRedux.ts b/electron/app/ipc/ipcMainRedux.ts index c5ac97ab65..a3ad19fd77 100644 --- a/electron/app/ipc/ipcMainRedux.ts +++ b/electron/app/ipc/ipcMainRedux.ts @@ -31,7 +31,7 @@ export const createDispatchForWindow = (window: BrowserWindow) => { export const dispatchToFocusedWindow = (action: AnyAction) => { const focusedWebContents = webContents.getFocusedWebContents(); - focusedWebContents.send('redux-dispatch', action); + focusedWebContents?.send('redux-dispatch', action); }; export const fetchStoreState = (contents: WebContents) => { @@ -51,11 +51,6 @@ export const fetchStoreState = (contents: WebContents) => { ); }; -export const fetchFocusedWindowStoreState = () => { - const focusedWebContents = webContents.getFocusedWebContents(); - return fetchStoreState(focusedWebContents); -}; - export const subscribeToStoreStateChanges = ( contents: WebContents, propertiesToPick: string[], diff --git a/electron/app/services/cloud/authenticator.ts b/electron/app/services/cloud/authenticator.ts new file mode 100644 index 0000000000..168f37b1b4 --- /dev/null +++ b/electron/app/services/cloud/authenticator.ts @@ -0,0 +1,22 @@ +import {app} from 'electron'; + +import {join} from 'path'; + +import {Authenticator, StorageHandlerAuth, createDefaultMonokleAuthenticator} from '@monokle/synchronizer'; + +let authenticator: Authenticator | undefined; + +const initAuthenticator = async (cloudStorageDir: string) => { + const newAuthenticator = createDefaultMonokleAuthenticator(new StorageHandlerAuth(cloudStorageDir)); + authenticator = newAuthenticator; + return newAuthenticator; +}; + +export const getAuthenticator = async () => { + const userDataDir = app.getPath('userData'); + const cloudStorageDir = join(userDataDir, 'cloud'); + if (!authenticator) { + await initAuthenticator(cloudStorageDir); + } + return authenticator; +}; diff --git a/electron/app/services/cloud/ipc.ts b/electron/app/services/cloud/ipc.ts new file mode 100644 index 0000000000..9bcd315453 --- /dev/null +++ b/electron/app/services/cloud/ipc.ts @@ -0,0 +1,11 @@ +import {handleIpc} from '../../utils/ipc'; +import {cloudLogin, cloudLogout} from './login'; +import {getPolicy} from './policy'; +import {getInfo} from './project'; +import {getSerializedUser} from './user'; + +handleIpc('cloud:login', cloudLogin); +handleIpc('cloud:logout', cloudLogout); +handleIpc('cloud:getUser', getSerializedUser); +handleIpc('cloud:getPolicy', getPolicy); +handleIpc('cloud:getInfo', getInfo); diff --git a/electron/app/services/cloud/login.ts b/electron/app/services/cloud/login.ts new file mode 100644 index 0000000000..8748c9510e --- /dev/null +++ b/electron/app/services/cloud/login.ts @@ -0,0 +1,28 @@ +import {shell} from 'electron'; + +import {CloudLoginResponse} from '@shared/models/cloud'; + +import {getAuthenticator} from './authenticator'; +import {serializeUser} from './user'; + +export const cloudLogin = async (): Promise => { + const authenticator = await getAuthenticator(); + if (!authenticator) { + throw new Error('Something went wrong with the authenticator'); + } + const loginResponse = await authenticator.login('device code'); + if (!loginResponse.handle) { + throw new Error('Something went wrong with the login response'); + } + shell.openExternal(loginResponse.handle.verification_uri_complete); + const user = await loginResponse.onDone; + if (!user) { + throw new Error('Login to Cloud has failed. Please try again later.'); + } + return {user: serializeUser(user)}; +}; + +export const cloudLogout = async (): Promise => { + const authenticator = await getAuthenticator(); + await authenticator?.logout(); +}; diff --git a/electron/app/services/cloud/policy.ts b/electron/app/services/cloud/policy.ts new file mode 100644 index 0000000000..c52744605a --- /dev/null +++ b/electron/app/services/cloud/policy.ts @@ -0,0 +1,23 @@ +import log from 'loglevel'; + +import {getSynchronizer} from './synchronizer'; +import {getUser} from './user'; + +export const getPolicy = async (repoPath: string) => { + const synchronizer = await getSynchronizer(); + const user = await getUser(); + if (!user?.token || !synchronizer) { + return null; + } + + try { + const policy = await synchronizer.getPolicy(repoPath, true, user.token); + return policy; + } catch (e: any) { + if (e instanceof Error) { + log.warn(e.message); + } + log.warn('Failed to synchronize policy'); + } + return null; +}; diff --git a/electron/app/services/cloud/project.ts b/electron/app/services/cloud/project.ts new file mode 100644 index 0000000000..ef225e0774 --- /dev/null +++ b/electron/app/services/cloud/project.ts @@ -0,0 +1,26 @@ +import {CloudPolicyInfo, CloudProjectInfo} from '@shared/models/cloud'; + +import {getSynchronizer} from './synchronizer'; +import {getUser} from './user'; + +export const getInfo = async ( + repoPath: string +): Promise<{projectInfo: CloudProjectInfo; policyInfo: CloudPolicyInfo} | null> => { + const synchronizer = await getSynchronizer(); + const user = await getUser(); + if (!user?.token || !synchronizer) { + return null; + } + + try { + const project = await synchronizer?.getProjectInfo(repoPath, user.token, true); + return project + ? { + projectInfo: {...project, link: synchronizer.generateDeepLinkProject(project.slug)}, + policyInfo: {link: synchronizer.generateDeepLinkProjectPolicy(project.slug)}, + } + : null; + } catch { + return null; + } +}; diff --git a/electron/app/services/cloud/synchronizer.ts b/electron/app/services/cloud/synchronizer.ts new file mode 100644 index 0000000000..262b672caf --- /dev/null +++ b/electron/app/services/cloud/synchronizer.ts @@ -0,0 +1,22 @@ +import {app} from 'electron'; + +import {join} from 'path'; + +import {StorageHandlerPolicy, Synchronizer, createDefaultMonokleSynchronizer} from '@monokle/synchronizer'; + +let synchronizer: Synchronizer | undefined; + +const initSynchronizer = async (cloudStorageDir: string) => { + const newSynchronizer = createDefaultMonokleSynchronizer(new StorageHandlerPolicy(cloudStorageDir)); + synchronizer = newSynchronizer; + return newSynchronizer; +}; + +export const getSynchronizer = async () => { + const userDataDir = app.getPath('userData'); + const cloudStorageDir = join(userDataDir, 'cloud'); + if (!synchronizer) { + await initSynchronizer(cloudStorageDir); + } + return synchronizer; +}; diff --git a/electron/app/services/cloud/user.ts b/electron/app/services/cloud/user.ts new file mode 100644 index 0000000000..528118b292 --- /dev/null +++ b/electron/app/services/cloud/user.ts @@ -0,0 +1,45 @@ +import log from 'loglevel'; + +import {User} from '@monokle/synchronizer'; +import {CloudUser} from '@shared/models/cloud'; + +import {getAuthenticator} from './authenticator'; + +export const getUser = async (): Promise => { + const authenticator = await getAuthenticator(); + if (!authenticator) { + return undefined; + } + try { + const user = await authenticator.getUser(); + if (!user.isAuthenticated) { + return undefined; + } + return user; + } catch (e: any) { + log.warn(e.message); + return undefined; + } +}; + +export const getSerializedUser = async (): Promise => { + const user = await getUser(); + if (!user) { + return undefined; + } + try { + const serializedUser = serializeUser(user); + return serializedUser; + } catch { + return undefined; + } +}; + +export const serializeUser = (user: User): CloudUser => { + if (!user.email) { + throw new Error('User not found'); + } + return { + email: user.email, + }; +}; diff --git a/notarization/afterSignHook.js b/notarization/afterSignHook.js index 6fdb5739b2..fd1657c74e 100644 --- a/notarization/afterSignHook.js +++ b/notarization/afterSignHook.js @@ -32,10 +32,12 @@ module.exports = async function (params) { try { await electron_notarize.notarize({ + tool: 'notarytool', + teamId: process.env.APPLE_TEAM_ID, appBundleId: appId, appPath: appPath, appleId: process.env.APPLE_ID, - appleIdPassword: process.env.APPLE_ID_PASSWORD, + appleIdPassword: process.env.APPLE_APP_SPECIFIC_PASSWORD, }); } catch (error) { log.error(error); diff --git a/package-lock.json b/package-lock.json index e25cce182d..2914f054a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "monokle", - "version": "2.3.3", + "version": "2.4.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "monokle", - "version": "2.3.3", + "version": "2.4.0", "hasInstallScript": true, "dependencies": { "@ant-design/icons": "4.8.0", @@ -14,6 +14,7 @@ "@dnd-kit/sortable": "7.0.2", "@kubernetes/client-node": "0.18.1", "@monokle/components": "1.7.0", + "@monokle/synchronizer": "^0.6.0", "@monokle/validation": "0.24.2", "@open-policy-agent/opa-wasm": "1.8.0", "@reduxjs/toolkit": "1.9.5", @@ -35,7 +36,7 @@ "electron-devtools-installer": "3.2.0", "electron-log": "4.4.8", "electron-store": "8.1.0", - "electron-updater": "5.3.0", + "electron-updater": "6.1.4", "elkjs": "0.8.2", "execa": "5.1.1", "fast-json-stable-stringify": "2.1.0", @@ -97,7 +98,7 @@ "@commitlint/cli": "17.6.3", "@commitlint/config-conventional": "17.6.3", "@craco/craco": "7.1.0", - "@electron/notarize": "1.2.3", + "@electron/notarize": "2.1.0", "@playwright/test": "1.34.0", "@sentry/webpack-plugin": "1.20.1", "@testing-library/jest-dom": "5.16.5", @@ -134,8 +135,8 @@ "concurrently": "8.0.1", "craco-less": "2.0.0", "cross-env": "7.0.3", - "electron": "24.1.1", - "electron-builder": "23.6.0", + "electron": "26.2.2", + "electron-builder": "24.6.4", "electron-reload": "2.0.0-alpha.1", "eslint-config-airbnb": "19.0.4", "eslint-config-prettier": "8.8.0", @@ -3369,8 +3370,9 @@ }, "node_modules/@develar/schema-utils": { "version": "2.6.5", + "resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.6.5.tgz", + "integrity": "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==", "dev": true, - "license": "MIT", "dependencies": { "ajv": "^6.12.0", "ajv-keywords": "^3.4.1" @@ -3385,8 +3387,9 @@ }, "node_modules/@develar/schema-utils/node_modules/ajv": { "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -3400,8 +3403,9 @@ }, "node_modules/@develar/schema-utils/node_modules/json-schema-traverse": { "version": "0.4.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, "node_modules/@dnd-kit/accessibility": { "version": "3.0.1", @@ -3448,6 +3452,23 @@ "react": ">=16.8.0" } }, + "node_modules/@electron/asar": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.7.tgz", + "integrity": "sha512-8FaSCAIiZGYFWyjeevPQt+0e9xCK9YmJ2Rjg5SXgdsXon6cRnU0Yxnbe6CvJbQn26baifur2Y2G5EBayRIsjyg==", + "dev": true, + "dependencies": { + "commander": "^5.0.0", + "glob": "^7.1.6", + "minimatch": "^3.0.4" + }, + "bin": { + "asar": "bin/asar.js" + }, + "engines": { + "node": ">=10.12.0" + } + }, "node_modules/@electron/get": { "version": "2.0.2", "dev": true, @@ -3506,12 +3527,14 @@ } }, "node_modules/@electron/notarize": { - "version": "1.2.3", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.1.0.tgz", + "integrity": "sha512-Q02xem1D0sg4v437xHgmBLxI2iz/fc0D4K7fiVWHa/AnW8o7D751xyKNXgziA6HrTOme9ul1JfWN5ark8WH1xA==", "dev": true, - "license": "MIT", "dependencies": { "debug": "^4.1.1", - "fs-extra": "^9.0.1" + "fs-extra": "^9.0.1", + "promise-retry": "^2.0.1" }, "engines": { "node": ">= 10.0.0" @@ -3531,15 +3554,63 @@ "node": ">=10" } }, + "node_modules/@electron/osx-sign": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.0.5.tgz", + "integrity": "sha512-k9ZzUQtamSoweGQDV2jILiRIHUu7lYlJ3c6IEmjv1hC17rclE+eb9U+f6UFlOOETo0JzY1HNlXy4YOlCvl+Lww==", + "dev": true, + "dependencies": { + "compare-version": "^0.1.2", + "debug": "^4.3.4", + "fs-extra": "^10.0.0", + "isbinaryfile": "^4.0.8", + "minimist": "^1.2.6", + "plist": "^3.0.5" + }, + "bin": { + "electron-osx-flat": "bin/electron-osx-flat.js", + "electron-osx-sign": "bin/electron-osx-sign.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@electron/osx-sign/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@electron/osx-sign/node_modules/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true, + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, "node_modules/@electron/universal": { - "version": "1.2.1", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.4.1.tgz", + "integrity": "sha512-lE/U3UNw1YHuowNbTmKNs9UlS3En3cPgwM5MI+agIgr/B1hSze9NdOP0qn7boZaI9Lph8IDv3/24g9IxnJP7aQ==", "dev": true, - "license": "MIT", "dependencies": { + "@electron/asar": "^3.2.1", "@malept/cross-spawn-promise": "^1.1.0", - "asar": "^3.1.0", "debug": "^4.3.1", - "dir-compare": "^2.4.0", + "dir-compare": "^3.0.0", "fs-extra": "^9.0.1", "minimatch": "^3.0.4", "plist": "^3.0.4" @@ -3550,8 +3621,9 @@ }, "node_modules/@electron/universal/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==", "dev": true, - "license": "MIT", "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", @@ -4633,6 +4705,8 @@ }, "node_modules/@malept/cross-spawn-promise": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", + "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", "dev": true, "funding": [ { @@ -4644,7 +4718,6 @@ "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" } ], - "license": "Apache-2.0", "dependencies": { "cross-spawn": "^7.0.1" }, @@ -4654,8 +4727,9 @@ }, "node_modules/@malept/flatpak-bundler": { "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@malept/flatpak-bundler/-/flatpak-bundler-0.4.0.tgz", + "integrity": "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==", "dev": true, - "license": "MIT", "dependencies": { "debug": "^4.1.1", "fs-extra": "^9.0.0", @@ -4668,8 +4742,9 @@ }, "node_modules/@malept/flatpak-bundler/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==", "dev": true, - "license": "MIT", "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", @@ -4701,6 +4776,91 @@ "use-debounced-effect": "2.0.1" } }, + "node_modules/@monokle/synchronizer": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@monokle/synchronizer/-/synchronizer-0.6.0.tgz", + "integrity": "sha512-CoFzrikvXUOa2PEzsoegERHN1OZ+mNzXfup1FJvNG59VvWTJ2pFZNppfzM2pJMmp07FiHqads2T4yFOWn5Cl9A==", + "dependencies": { + "@monokle/types": "*", + "env-paths": "^2.2.1", + "git-url-parse": "^13.1.0", + "mkdirp": "^3.0.1", + "node-fetch": "^2.6.13", + "normalize-url": "^4.5.1", + "openid-client": "^5.4.3", + "simple-git": "^3.19.1", + "slugify": "^1.6.6", + "yaml": "^2.3.1" + } + }, + "node_modules/@monokle/synchronizer/node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@monokle/synchronizer/node_modules/node-fetch": { + "version": "2.6.13", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.13.tgz", + "integrity": "sha512-StxNAxh15zr77QvvkmveSQ8uCQ4+v5FkvNTj0OESmiHu+VRi/gXArXtkWMElOsOUNLtUEvI4yS+rdtOHZTwlQA==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@monokle/synchronizer/node_modules/normalize-url": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@monokle/synchronizer/node_modules/simple-git": { + "version": "3.19.1", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.19.1.tgz", + "integrity": "sha512-Ck+rcjVaE1HotraRAS8u/+xgTvToTuoMkT9/l9lvuP5jftwnYUp6DwuJzsKErHgfyRk8IB8pqGHWEbM3tLgV1w==", + "dependencies": { + "@kwsites/file-exists": "^1.1.1", + "@kwsites/promise-deferred": "^1.1.1", + "debug": "^4.3.4" + }, + "funding": { + "type": "github", + "url": "https://github.com/steveukx/git-js?sponsor=1" + } + }, + "node_modules/@monokle/synchronizer/node_modules/yaml": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", + "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", + "engines": { + "node": ">= 14" + } + }, + "node_modules/@monokle/types": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@monokle/types/-/types-0.2.0.tgz", + "integrity": "sha512-6Sld9+1p+W6kRWkdNpNVQCSgAjSPZu8EiIq8ANT9+RfA1rKlYuouOdaadGqtPUsD75lOl67r5Wzn3IxTm34gmw==" + }, "node_modules/@monokle/validation": { "version": "0.24.2", "resolved": "https://registry.npmjs.org/@monokle/validation/-/validation-0.24.2.tgz", @@ -5762,8 +5922,9 @@ }, "node_modules/@tootallnate/once": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", "dev": true, - "license": "MIT", "engines": { "node": ">= 10" } @@ -6245,9 +6406,10 @@ "license": "MIT" }, "node_modules/@types/plist": { - "version": "3.0.2", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.3.tgz", + "integrity": "sha512-DXkBoKc7jwUR0p439icInmXXMJNhoImdpOrrgA5/nDFK7LVtcJ9MyQNKhJEKpEztnHGWnNWMWLOIR62By0Ln0A==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "@types/node": "*", @@ -6365,6 +6527,7 @@ }, "node_modules/@types/semver": { "version": "7.5.0", + "dev": true, "license": "MIT" }, "node_modules/@types/send": { @@ -6465,9 +6628,10 @@ "license": "MIT" }, "node_modules/@types/verror": { - "version": "1.10.6", + "version": "1.10.7", + "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.7.tgz", + "integrity": "sha512-4c5F4T0qMSoXq1KHx7WV1FMuD2h0xdaFoJ7HSVWUfQ8w5YbqCwLOA8K7/yy1I+Txuzvm417dnPUaLmqazX1F7g==", "dev": true, - "license": "MIT", "optional": true }, "node_modules/@types/wait-on": { @@ -7024,6 +7188,15 @@ "@xtuc/long": "4.2.2" } }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", + "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/@xobotyi/scrollbar-width": { "version": "1.9.5", "license": "MIT" @@ -7045,8 +7218,9 @@ }, "node_modules/7zip-bin": { "version": "5.1.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.1.1.tgz", + "integrity": "sha512-sAP4LldeWNz0lNzmTird3uWfFDWWTeg6V/MsmyyLR9X1idwKBWIgt/ZvinqQldJm3LecKEs1emkbquO6PCiLVQ==", + "dev": true }, "node_modules/abab": { "version": "2.0.6", @@ -7375,61 +7549,72 @@ }, "node_modules/app-builder-bin": { "version": "4.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-4.0.0.tgz", + "integrity": "sha512-xwdG0FJPQMe0M0UA4Tz0zEB8rBJTRA5a476ZawAqiBkMv16GRK5xpXThOjMaEOFnZ6zabejjG4J3da0SXG63KA==", + "dev": true }, "node_modules/app-builder-lib": { - "version": "23.6.0", + "version": "24.6.4", + "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-24.6.4.tgz", + "integrity": "sha512-m9931WXb83teb32N0rKg+ulbn6+Hl8NV5SUpVDOVz9MWOXfhV6AQtTdftf51zJJvCQnQugGtSqoLvgw6mdF/Rg==", "dev": true, - "license": "MIT", "dependencies": { "@develar/schema-utils": "~2.6.5", - "@electron/universal": "1.2.1", + "@electron/notarize": "2.1.0", + "@electron/osx-sign": "1.0.5", + "@electron/universal": "1.4.1", "@malept/flatpak-bundler": "^0.4.0", + "@types/fs-extra": "9.0.13", "7zip-bin": "~5.1.1", "async-exit-hook": "^2.0.1", "bluebird-lst": "^1.0.9", - "builder-util": "23.6.0", - "builder-util-runtime": "9.1.1", + "builder-util": "24.5.0", + "builder-util-runtime": "9.2.1", "chromium-pickle-js": "^0.2.0", "debug": "^4.3.4", - "ejs": "^3.1.7", - "electron-osx-sign": "^0.6.0", - "electron-publish": "23.6.0", + "ejs": "^3.1.8", + "electron-publish": "24.5.0", "form-data": "^4.0.0", "fs-extra": "^10.1.0", "hosted-git-info": "^4.1.0", "is-ci": "^3.0.0", - "isbinaryfile": "^4.0.10", + "isbinaryfile": "^5.0.0", "js-yaml": "^4.1.0", "lazy-val": "^1.0.5", - "minimatch": "^3.1.2", - "read-config-file": "6.2.0", + "minimatch": "^5.1.1", + "read-config-file": "6.3.2", "sanitize-filename": "^1.6.3", - "semver": "^7.3.7", - "tar": "^6.1.11", + "semver": "^7.3.8", + "tar": "^6.1.12", "temp-file": "^3.4.0" }, "engines": { "node": ">=14.0.0" } }, - "node_modules/app-builder-lib/node_modules/builder-util-runtime": { - "version": "9.1.1", + "node_modules/app-builder-lib/node_modules/@types/fs-extra": { + "version": "9.0.13", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", + "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", "dev": true, - "license": "MIT", "dependencies": { - "debug": "^4.3.4", - "sax": "^1.2.4" - }, - "engines": { - "node": ">=12.0.0" + "@types/node": "*" + } + }, + "node_modules/app-builder-lib/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" } }, "node_modules/app-builder-lib/node_modules/form-data": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "dev": true, - "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -7441,8 +7626,9 @@ }, "node_modules/app-builder-lib/node_modules/fs-extra": { "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, - "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -7452,6 +7638,18 @@ "node": ">=12" } }, + "node_modules/app-builder-lib/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/append-transform": { "version": "2.0.0", "dev": true, @@ -7666,8 +7864,9 @@ }, "node_modules/async-exit-hook": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", + "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -8169,6 +8368,8 @@ }, "node_modules/base64-js": { "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "dev": true, "funding": [ { @@ -8183,8 +8384,7 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT" + ] }, "node_modules/batch": { "version": "0.6.1", @@ -8242,8 +8442,9 @@ }, "node_modules/bluebird-lst": { "version": "1.0.9", + "resolved": "https://registry.npmjs.org/bluebird-lst/-/bluebird-lst-1.0.9.tgz", + "integrity": "sha512-7B1Rtx82hjnSD4PGLAjVWeYH3tHAcVUmChh85a3lltKQm6FresXh9ErQo6oAv6CqxttczC3/kEg8SY5NluPuUw==", "dev": true, - "license": "MIT", "dependencies": { "bluebird": "^3.5.5" } @@ -8435,6 +8636,8 @@ }, "node_modules/buffer": { "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "dev": true, "funding": [ { @@ -8450,27 +8653,12 @@ "url": "https://feross.org/support" } ], - "license": "MIT", "optional": true, "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, - "node_modules/buffer-alloc": { - "version": "1.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-alloc-unsafe": "^1.1.0", - "buffer-fill": "^1.0.0" - } - }, - "node_modules/buffer-alloc-unsafe": { - "version": "1.1.0", - "dev": true, - "license": "MIT" - }, "node_modules/buffer-crc32": { "version": "0.2.13", "dev": true, @@ -8480,40 +8668,39 @@ } }, "node_modules/buffer-equal": { - "version": "1.0.0", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.1.tgz", + "integrity": "sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==", "dev": true, - "license": "MIT", "engines": { - "node": ">=0.4.0" + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/buffer-fill": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, "node_modules/buffer-from": { "version": "1.1.2", "dev": true, "license": "MIT" }, "node_modules/builder-util": { - "version": "23.6.0", + "version": "24.5.0", + "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-24.5.0.tgz", + "integrity": "sha512-STnBmZN/M5vGcv01u/K8l+H+kplTaq4PAIn3yeuufUKSpcdro0DhJWxPI81k5XcNfC//bjM3+n9nr8F9uV4uAQ==", "dev": true, - "license": "MIT", "dependencies": { "@types/debug": "^4.1.6", - "@types/fs-extra": "^9.0.11", "7zip-bin": "~5.1.1", "app-builder-bin": "4.0.0", "bluebird-lst": "^1.0.9", - "builder-util-runtime": "9.1.1", - "chalk": "^4.1.1", + "builder-util-runtime": "9.2.1", + "chalk": "^4.1.2", "cross-spawn": "^7.0.3", "debug": "^4.3.4", - "fs-extra": "^10.0.0", + "fs-extra": "^10.1.0", "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", "is-ci": "^3.0.0", "js-yaml": "^4.1.0", "source-map-support": "^0.5.19", @@ -8523,27 +8710,6 @@ }, "node_modules/builder-util-runtime": { "version": "9.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4", - "sax": "^1.2.4" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/builder-util/node_modules/@types/fs-extra": { - "version": "9.0.13", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/builder-util/node_modules/builder-util-runtime": { - "version": "9.1.1", - "dev": true, "license": "MIT", "dependencies": { "debug": "^4.3.4", @@ -8555,8 +8721,9 @@ }, "node_modules/builder-util/node_modules/fs-extra": { "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, - "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -9187,14 +9354,6 @@ "dev": true, "license": "MIT" }, - "node_modules/colors": { - "version": "1.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.1.90" - } - }, "node_modules/combined-stream": { "version": "1.0.8", "license": "MIT", @@ -9249,8 +9408,9 @@ }, "node_modules/compare-version": { "version": "0.1.2", + "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", + "integrity": "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -9419,6 +9579,29 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/config-file-ts": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/config-file-ts/-/config-file-ts-0.2.4.tgz", + "integrity": "sha512-cKSW0BfrSaAUnxpgvpXPLaaW/umg4bqg4k3GO1JqlRfpx+d5W0GDXznCMkWotJQek5Mmz1MJVChQnz3IVaeMZQ==", + "dev": true, + "dependencies": { + "glob": "^7.1.6", + "typescript": "^4.0.2" + } + }, + "node_modules/config-file-ts/node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, "node_modules/confusing-browser-globals": { "version": "1.0.11", "dev": true, @@ -9963,8 +10146,9 @@ }, "node_modules/crc": { "version": "3.8.0", + "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", + "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "buffer": "^5.1.0" @@ -10977,39 +11161,13 @@ } }, "node_modules/dir-compare": { - "version": "2.4.0", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-equal": "1.0.0", - "colors": "1.0.3", - "commander": "2.9.0", - "minimatch": "3.0.4" - }, - "bin": { - "dircompare": "src/cli/dircompare.js" - } - }, - "node_modules/dir-compare/node_modules/commander": { - "version": "2.9.0", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-readlink": ">= 1.0.0" - }, - "engines": { - "node": ">= 0.6.x" - } - }, - "node_modules/dir-compare/node_modules/minimatch": { - "version": "3.0.4", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-3.3.0.tgz", + "integrity": "sha512-J7/et3WlGUCxjdnD3HAAzQ6nsnc0WL6DD7WcwJb7c39iH1+AWfg+9OqzJNaI6PkBwBvm1mhZNL9iY/nRiZXlPg==", "dev": true, - "license": "ISC", "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" + "buffer-equal": "^1.0.0", + "minimatch": "^3.0.4" } }, "node_modules/dir-glob": { @@ -11029,14 +11187,15 @@ "license": "MIT" }, "node_modules/dmg-builder": { - "version": "23.6.0", + "version": "24.6.4", + "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-24.6.4.tgz", + "integrity": "sha512-BNcHRc9CWEuI9qt0E655bUBU/j/3wUCYBVKGu1kVpbN5lcUdEJJJeiO0NHK3dgKmra6LUUZlo+mWqc+OCbi0zw==", "dev": true, - "license": "MIT", "dependencies": { - "app-builder-lib": "23.6.0", - "builder-util": "23.6.0", - "builder-util-runtime": "9.1.1", - "fs-extra": "^10.0.0", + "app-builder-lib": "24.6.4", + "builder-util": "24.5.0", + "builder-util-runtime": "9.2.1", + "fs-extra": "^10.1.0", "iconv-lite": "^0.6.2", "js-yaml": "^4.1.0" }, @@ -11044,22 +11203,11 @@ "dmg-license": "^1.0.11" } }, - "node_modules/dmg-builder/node_modules/builder-util-runtime": { - "version": "9.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4", - "sax": "^1.2.4" - }, - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/dmg-builder/node_modules/fs-extra": { "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, - "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -11071,8 +11219,9 @@ }, "node_modules/dmg-license": { "version": "1.0.11", + "resolved": "https://registry.npmjs.org/dmg-license/-/dmg-license-1.0.11.tgz", + "integrity": "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==", "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -11096,8 +11245,9 @@ }, "node_modules/dmg-license/node_modules/ajv": { "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "fast-deep-equal": "^3.1.1", @@ -11112,8 +11262,9 @@ }, "node_modules/dmg-license/node_modules/json-schema-traverse": { "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, - "license": "MIT", "optional": true }, "node_modules/dns-equal": { @@ -11373,10 +11524,11 @@ } }, "node_modules/electron": { - "version": "24.1.1", + "version": "26.2.2", + "resolved": "https://registry.npmjs.org/electron/-/electron-26.2.2.tgz", + "integrity": "sha512-Ihb3Zt4XYnHF52DYSq17ySkgFqJV4OT0VnfhUYZASAql7Vembz3VsAq7mB3OALBHXltAW34P8BxTIwTqZaMS3g==", "dev": true, "hasInstallScript": true, - "license": "MIT", "dependencies": { "@electron/get": "^2.0.0", "@types/node": "^18.11.18", @@ -11390,22 +11542,22 @@ } }, "node_modules/electron-builder": { - "version": "23.6.0", + "version": "24.6.4", + "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-24.6.4.tgz", + "integrity": "sha512-uNWQoU7pE7qOaIQ6CJHpBi44RJFVG8OHRBIadUxrsDJVwLLo8Nma3K/EEtx5/UyWAQYdcK4nVPYKoRqBb20hbA==", "dev": true, - "license": "MIT", "dependencies": { - "@types/yargs": "^17.0.1", - "app-builder-lib": "23.6.0", - "builder-util": "23.6.0", - "builder-util-runtime": "9.1.1", - "chalk": "^4.1.1", - "dmg-builder": "23.6.0", - "fs-extra": "^10.0.0", + "app-builder-lib": "24.6.4", + "builder-util": "24.5.0", + "builder-util-runtime": "9.2.1", + "chalk": "^4.1.2", + "dmg-builder": "24.6.4", + "fs-extra": "^10.1.0", "is-ci": "^3.0.0", "lazy-val": "^1.0.5", - "read-config-file": "6.2.0", - "simple-update-notifier": "^1.0.7", - "yargs": "^17.5.1" + "read-config-file": "6.3.2", + "simple-update-notifier": "2.0.0", + "yargs": "^17.6.2" }, "bin": { "electron-builder": "cli.js", @@ -11415,18 +11567,6 @@ "node": ">=14.0.0" } }, - "node_modules/electron-builder/node_modules/builder-util-runtime": { - "version": "9.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4", - "sax": "^1.2.4" - }, - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/electron-builder/node_modules/fs-extra": { "version": "10.1.0", "dev": true, @@ -11454,88 +11594,35 @@ "version": "4.4.8", "license": "MIT" }, - "node_modules/electron-osx-sign": { - "version": "0.6.0", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "bluebird": "^3.5.0", - "compare-version": "^0.1.2", - "debug": "^2.6.8", - "isbinaryfile": "^3.0.2", - "minimist": "^1.2.0", - "plist": "^3.0.1" - }, - "bin": { - "electron-osx-flat": "bin/electron-osx-flat.js", - "electron-osx-sign": "bin/electron-osx-sign.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/electron-osx-sign/node_modules/debug": { - "version": "2.6.9", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/electron-osx-sign/node_modules/isbinaryfile": { - "version": "3.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-alloc": "^1.2.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/electron-osx-sign/node_modules/ms": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, "node_modules/electron-publish": { - "version": "23.6.0", + "version": "24.5.0", + "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-24.5.0.tgz", + "integrity": "sha512-zwo70suH15L15B4ZWNDoEg27HIYoPsGJUF7xevLJLSI7JUPC8l2yLBdLGwqueJ5XkDL7ucYyRZzxJVR8ElV9BA==", "dev": true, - "license": "MIT", "dependencies": { "@types/fs-extra": "^9.0.11", - "builder-util": "23.6.0", - "builder-util-runtime": "9.1.1", - "chalk": "^4.1.1", - "fs-extra": "^10.0.0", + "builder-util": "24.5.0", + "builder-util-runtime": "9.2.1", + "chalk": "^4.1.2", + "fs-extra": "^10.1.0", "lazy-val": "^1.0.5", "mime": "^2.5.2" } }, "node_modules/electron-publish/node_modules/@types/fs-extra": { "version": "9.0.13", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", + "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*" } }, - "node_modules/electron-publish/node_modules/builder-util-runtime": { - "version": "9.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4", - "sax": "^1.2.4" - }, - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/electron-publish/node_modules/fs-extra": { "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, - "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -11580,29 +11667,18 @@ "license": "ISC" }, "node_modules/electron-updater": { - "version": "5.3.0", - "license": "MIT", + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-6.1.4.tgz", + "integrity": "sha512-yYAJc6RQjjV4WtInZVn+ZcLyXRhbVXoomKEfUUwDqIk5s2wxzLhWaor7lrNgxODyODhipjg4SVPMhJHi5EnsCA==", "dependencies": { - "@types/semver": "^7.3.6", - "builder-util-runtime": "9.1.1", - "fs-extra": "^10.0.0", + "builder-util-runtime": "9.2.1", + "fs-extra": "^10.1.0", "js-yaml": "^4.1.0", "lazy-val": "^1.0.5", "lodash.escaperegexp": "^4.1.2", "lodash.isequal": "^4.5.0", - "semver": "^7.3.5", - "typed-emitter": "^2.1.0" - } - }, - "node_modules/electron-updater/node_modules/builder-util-runtime": { - "version": "9.1.1", - "license": "MIT", - "dependencies": { - "debug": "^4.3.4", - "sax": "^1.2.4" - }, - "engines": { - "node": ">=12.0.0" + "semver": "^7.3.8", + "tiny-typed-emitter": "^2.1.0" } }, "node_modules/electron-updater/node_modules/fs-extra": { @@ -11693,6 +11769,12 @@ "node": ">=6" } }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true + }, "node_modules/errno": { "version": "0.1.8", "dev": true, @@ -14012,11 +14094,6 @@ "version": "4.2.11", "license": "ISC" }, - "node_modules/graceful-readlink": { - "version": "1.0.1", - "dev": true, - "license": "MIT" - }, "node_modules/grapheme-splitter": { "version": "1.0.4", "dev": true, @@ -14499,8 +14576,9 @@ }, "node_modules/http-proxy-agent": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "dev": true, - "license": "MIT", "dependencies": { "@tootallnate/once": "2", "agent-base": "6", @@ -14612,8 +14690,9 @@ }, "node_modules/iconv-corefoundation": { "version": "1.1.7", + "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", + "integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==", "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -14666,6 +14745,8 @@ }, "node_modules/ieee754": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "dev": true, "funding": [ { @@ -14681,7 +14762,6 @@ "url": "https://feross.org/support" } ], - "license": "BSD-3-Clause", "optional": true }, "node_modules/ignore": { @@ -14917,8 +14997,9 @@ }, "node_modules/is-ci": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", + "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", "dev": true, - "license": "MIT", "dependencies": { "ci-info": "^3.2.0" }, @@ -15313,11 +15394,12 @@ "license": "MIT" }, "node_modules/isbinaryfile": { - "version": "4.0.10", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.0.tgz", + "integrity": "sha512-UDdnyGvMajJUWCkib7Cei/dvyJrrvo4FIrsvSFWdPpXSUorzXrDJ0S+X5Q4ZlasfPjca4yqCNNsjbCeiy8FFeg==", "dev": true, - "license": "MIT", "engines": { - "node": ">= 8.0.0" + "node": ">= 14.0.0" }, "funding": { "url": "https://github.com/sponsors/gjtorikian/" @@ -17491,7 +17573,6 @@ "node_modules/jose": { "version": "4.14.4", "license": "MIT", - "optional": true, "funding": { "url": "https://github.com/sponsors/panva" } @@ -19555,8 +19636,9 @@ }, "node_modules/mime": { "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", "dev": true, - "license": "MIT", "bin": { "mime": "cli.js" }, @@ -19942,8 +20024,9 @@ }, "node_modules/node-addon-api": { "version": "1.7.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", + "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", "dev": true, - "license": "MIT", "optional": true }, "node_modules/node-domexception": { @@ -20294,7 +20377,6 @@ "node_modules/object-hash": { "version": "2.2.0", "license": "MIT", - "optional": true, "engines": { "node": ">= 6" } @@ -20430,7 +20512,6 @@ "node_modules/oidc-token-hash": { "version": "5.0.3", "license": "MIT", - "optional": true, "engines": { "node": "^10.13.0 || >=12.0.0" } @@ -20531,11 +20612,11 @@ } }, "node_modules/openid-client": { - "version": "5.4.2", - "license": "MIT", - "optional": true, + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.4.3.tgz", + "integrity": "sha512-sVQOvjsT/sbSfYsQI/9liWQGVZH/Pp3rrtlGEwgk/bbHfrUDZ24DN57lAagIwFtuEu+FM9Ev7r85s8S/yPjimQ==", "dependencies": { - "jose": "^4.14.1", + "jose": "^4.14.4", "lru-cache": "^6.0.0", "object-hash": "^2.2.0", "oidc-token-hash": "^5.0.3" @@ -21067,15 +21148,17 @@ } }, "node_modules/plist": { - "version": "3.0.6", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", + "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", "dev": true, - "license": "MIT", "dependencies": { + "@xmldom/xmldom": "^0.8.8", "base64-js": "^1.5.1", "xmlbuilder": "^15.1.1" }, "engines": { - "node": ">=6" + "node": ">=10.4.0" } }, "node_modules/polished": { @@ -22388,6 +22471,28 @@ "asap": "~2.0.6" } }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/promise-retry/node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, "node_modules/prompts": { "version": "2.4.2", "dev": true, @@ -23539,10 +23644,12 @@ } }, "node_modules/read-config-file": { - "version": "6.2.0", + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-6.3.2.tgz", + "integrity": "sha512-M80lpCjnE6Wt6zb98DoW8WHR09nzMSpu8XHtPkiTHrJ5Az9CybfeQhTJ8D7saeBHpGhLPIVyA8lcL6ZmdKwY6Q==", "dev": true, - "license": "MIT", "dependencies": { + "config-file-ts": "^0.2.4", "dotenv": "^9.0.2", "dotenv-expand": "^5.1.0", "js-yaml": "^4.1.0", @@ -23555,8 +23662,9 @@ }, "node_modules/read-config-file/node_modules/dotenv": { "version": "9.0.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-9.0.2.tgz", + "integrity": "sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg==", "dev": true, - "license": "BSD-2-Clause", "engines": { "node": ">=10" } @@ -24361,7 +24469,7 @@ }, "node_modules/rxjs": { "version": "7.8.1", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "dependencies": { "tslib": "^2.1.0" @@ -24431,8 +24539,9 @@ }, "node_modules/sanitize-filename": { "version": "1.6.3", + "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", + "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", "dev": true, - "license": "WTFPL OR ISC", "dependencies": { "truncate-utf8-bytes": "^1.0.0" } @@ -24857,22 +24966,30 @@ } }, "node_modules/simple-update-notifier": { - "version": "1.1.0", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", "dev": true, - "license": "MIT", "dependencies": { - "semver": "~7.0.0" + "semver": "^7.5.3" }, "engines": { - "node": ">=8.10.0" + "node": ">=10" } }, "node_modules/simple-update-notifier/node_modules/semver": { - "version": "7.0.0", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, - "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, "bin": { "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/sisteransi": { @@ -24901,10 +25018,19 @@ "node": ">=8" } }, + "node_modules/slugify": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz", + "integrity": "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/smart-buffer": { "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "dev": true, - "license": "MIT", "optional": true, "engines": { "node": ">= 6.0.0", @@ -25362,8 +25488,9 @@ }, "node_modules/stat-mode": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz", + "integrity": "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 6" } @@ -26509,8 +26636,9 @@ }, "node_modules/temp-file": { "version": "3.4.0", + "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz", + "integrity": "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==", "dev": true, - "license": "MIT", "dependencies": { "async-exit-hook": "^2.0.1", "fs-extra": "^10.0.0" @@ -26518,8 +26646,9 @@ }, "node_modules/temp-file/node_modules/fs-extra": { "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, - "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -26728,6 +26857,11 @@ "version": "1.3.1", "license": "MIT" }, + "node_modules/tiny-typed-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-typed-emitter/-/tiny-typed-emitter-2.1.0.tgz", + "integrity": "sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA==" + }, "node_modules/titleize": { "version": "3.0.0", "dev": true, @@ -26861,8 +26995,9 @@ }, "node_modules/truncate-utf8-bytes": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", "dev": true, - "license": "WTFPL", "dependencies": { "utf8-byte-length": "^1.0.1" } @@ -27107,13 +27242,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/typed-emitter": { - "version": "2.1.0", - "license": "MIT", - "optionalDependencies": { - "rxjs": "*" - } - }, "node_modules/typedarray": { "version": "0.0.6", "dev": true, @@ -27456,8 +27584,9 @@ }, "node_modules/utf8-byte-length": { "version": "1.0.4", - "dev": true, - "license": "WTFPL" + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz", + "integrity": "sha512-4+wkEYLBbWxqTahEsWrhxepcoVOJ+1z5PGIjPZxRkytcdSUaNjIjBM7Xn8E+pdSuV7SzvWovBFA54FO0JSoqhA==", + "dev": true }, "node_modules/util-deprecate": { "version": "1.0.2", @@ -27603,8 +27732,9 @@ }, "node_modules/verror": { "version": "1.10.1", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", + "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "assert-plus": "^1.0.0", @@ -28620,8 +28750,9 @@ }, "node_modules/xmlbuilder": { "version": "15.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", "dev": true, - "license": "MIT", "engines": { "node": ">=8.0" } diff --git a/package.json b/package.json index 37c1ffcbc5..6c27529607 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "monokle", - "version": "2.3.3", + "version": "2.4.0", "author": "Kubeshop", "description": "UI for managing k8s manifests", "homepage": "./", @@ -26,7 +26,7 @@ "@commitlint/cli": "17.6.3", "@commitlint/config-conventional": "17.6.3", "@craco/craco": "7.1.0", - "@electron/notarize": "1.2.3", + "@electron/notarize": "2.1.0", "@playwright/test": "1.34.0", "@sentry/webpack-plugin": "1.20.1", "@testing-library/jest-dom": "5.16.5", @@ -63,8 +63,8 @@ "concurrently": "8.0.1", "craco-less": "2.0.0", "cross-env": "7.0.3", - "electron": "24.1.1", - "electron-builder": "23.6.0", + "electron": "26.2.2", + "electron-builder": "24.6.4", "electron-reload": "2.0.0-alpha.1", "eslint-config-airbnb": "19.0.4", "eslint-config-prettier": "8.8.0", @@ -101,6 +101,7 @@ "@dnd-kit/sortable": "7.0.2", "@kubernetes/client-node": "0.18.1", "@monokle/components": "1.7.0", + "@monokle/synchronizer": "^0.6.0", "@monokle/validation": "0.24.2", "@open-policy-agent/opa-wasm": "1.8.0", "@reduxjs/toolkit": "1.9.5", @@ -122,7 +123,7 @@ "electron-devtools-installer": "3.2.0", "electron-log": "4.4.8", "electron-store": "8.1.0", - "electron-updater": "5.3.0", + "electron-updater": "6.1.4", "elkjs": "0.8.2", "execa": "5.1.1", "fast-json-stable-stringify": "2.1.0", @@ -210,6 +211,7 @@ "productName": "Monokle", "copyright": "Copyright © 2021 ${author}", "mac": { + "notarize": false, "category": "public.app-category.utilities", "icon": "build/icon.png", "hardenedRuntime": true, diff --git a/resources/releaseNotes/2.4/2.4.json b/resources/releaseNotes/2.4/2.4.json new file mode 100644 index 0000000000..2f959b06c9 --- /dev/null +++ b/resources/releaseNotes/2.4/2.4.json @@ -0,0 +1,9 @@ +{ + "title": "Welcome to Monokle 2.4", + "learnMoreUrl": "https://monokle.io/blog/monokle-2-4-release", + "about": { + "when": "September 2023", + "title": "2.4 focused on:", + "features": ["Sync with Cloud", "Bug fixes", "Stability improvements"] + } +} diff --git a/resources/releaseNotes/2.4/2.4.md b/resources/releaseNotes/2.4/2.4.md new file mode 100644 index 0000000000..15a16dceb3 --- /dev/null +++ b/resources/releaseNotes/2.4/2.4.md @@ -0,0 +1,6 @@ +Here's what we've been working on: +- Seamless connection with Monokle Cloud +- Automatic Policy synchronization +- Cloud connection status indicator +- Minor fixes and UI enhancements +- [Read our blog post for more details →](https://monokle.io/blog/monokle-2-4-release) diff --git a/resources/releaseNotes/2.4/2.4.svg b/resources/releaseNotes/2.4/2.4.svg new file mode 100644 index 0000000000..c827d0d234 --- /dev/null +++ b/resources/releaseNotes/2.4/2.4.svg @@ -0,0 +1,248 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/CloudIcon.svg b/src/assets/CloudIcon.svg new file mode 100644 index 0000000000..254e144f15 --- /dev/null +++ b/src/assets/CloudIcon.svg @@ -0,0 +1,14 @@ + + + + diff --git a/src/assets/CloudIconWhite.svg b/src/assets/CloudIconWhite.svg new file mode 100644 index 0000000000..761e49df82 --- /dev/null +++ b/src/assets/CloudIconWhite.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/CloudManaged.png b/src/assets/CloudManaged.png new file mode 100644 index 0000000000..88533624ef Binary files /dev/null and b/src/assets/CloudManaged.png differ diff --git a/src/assets/CloudSync.svg b/src/assets/CloudSync.svg new file mode 100644 index 0000000000..67219002df --- /dev/null +++ b/src/assets/CloudSync.svg @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/CloudSynced.png b/src/assets/CloudSynced.png new file mode 100644 index 0000000000..677b78efc2 Binary files /dev/null and b/src/assets/CloudSynced.png differ diff --git a/src/assets/CloudUnsynced.png b/src/assets/CloudUnsynced.png new file mode 100644 index 0000000000..c0b79f2819 Binary files /dev/null and b/src/assets/CloudUnsynced.png differ diff --git a/src/components/organisms/ActionsPane/ActionsPaneHeader.tsx b/src/components/organisms/ActionsPane/ActionsPaneHeader.tsx index f46348e636..191d61e7ce 100644 --- a/src/components/organisms/ActionsPane/ActionsPaneHeader.tsx +++ b/src/components/organisms/ActionsPane/ActionsPaneHeader.tsx @@ -1,6 +1,6 @@ -import {useCallback, useMemo} from 'react'; +import {useCallback, useMemo, useState} from 'react'; -import {Button, Dropdown, Modal, Tooltip} from 'antd'; +import {Button, Dropdown, Tooltip} from 'antd'; import {LeftOutlined, RightOutlined} from '@ant-design/icons'; @@ -21,6 +21,7 @@ import {runPreviewConfiguration} from '@redux/thunks/runPreviewConfiguration'; import {selectFromHistory} from '@redux/thunks/selectFromHistory'; import {TitleBarWrapper} from '@components/atoms'; +import {HelmChartModalConfirmWithNamespaceSelect} from '@components/molecules'; import {useRefSelector} from '@utils/hooks'; @@ -50,6 +51,8 @@ const ActionsPaneHeader: React.FC = props => { const selectedHelmConfig = useAppSelector(selectedHelmConfigSelector); const selectedImage = useAppSelector(selectedImageSelector); + const [isHelmChartApplyModalVisible, setIsHelmChartApplyModalVisible] = useState(false); + const onClickEditPreviewConfiguration = useCallback(() => { if (!selectedHelmConfig) { return; @@ -74,18 +77,23 @@ const ActionsPaneHeader: React.FC = props => { dispatch(startPreview({type: 'helm-config', configId: selectedHelmConfig.id})); }, [dispatch, selectedHelmConfig]); - const onClickInstallPreviewConfiguration = useCallback(() => { - Modal.confirm({ - title: 'Install Helm Chart', - content: `Are you sure you want to install the ${selectedHelmConfig?.name} configuration to the cluster?`, - onOk: () => { - if (!selectedHelmConfig) { - return; - } - dispatch(runPreviewConfiguration({helmConfigId: selectedHelmConfig.id, performDeploy: true})); - }, - }); - }, [dispatch, selectedHelmConfig]); + const onConfirmInstallPreviewConfiguration = useCallback( + (selectedNamespace?: string, shouldCreateNamespace?: boolean) => { + if (!selectedHelmConfig) { + return; + } + dispatch( + runPreviewConfiguration({ + helmConfigId: selectedHelmConfig.id, + performDeploy: true, + selectedNamespace, + shouldCreateNamespace, + }) + ); + setIsHelmChartApplyModalVisible(false); + }, + [dispatch, selectedHelmConfig] + ); const onClickLeftArrow = useCallback(() => { dispatch(selectFromHistory('left')); @@ -135,6 +143,14 @@ const ActionsPaneHeader: React.FC = props => { if (selectedHelmConfig) { return ( + { + setIsHelmChartApplyModalVisible(false); + }} + /> = props => { title={InstallPreviewConfigurationTooltip} placement="bottomLeft" > - diff --git a/src/components/organisms/PageHeader/CloudSync/CloudSync.tsx b/src/components/organisms/PageHeader/CloudSync/CloudSync.tsx new file mode 100644 index 0000000000..c5b0d005f7 --- /dev/null +++ b/src/components/organisms/PageHeader/CloudSync/CloudSync.tsx @@ -0,0 +1,217 @@ +import {useCallback} from 'react'; + +import {Button, Dropdown, Spin, Tooltip} from 'antd'; + +import {CheckCircleFilled, CloseCircleFilled, InfoCircleFilled, LoadingOutlined} from '@ant-design/icons'; + +import styled from 'styled-components'; + +import {TOOLTIP_DELAY} from '@constants/constants'; + +import {useCloudPolicy, useCloudUser} from '@redux/validation/validation.hooks'; + +import CloudIcon from '@assets/CloudIcon.svg'; +import CloudIconWhite from '@assets/CloudIconWhite.svg'; +import CloudSynced from '@assets/CloudSynced.png'; +import CloudUnsynced from '@assets/CloudUnsynced.png'; + +import {Colors} from '@monokle/components'; +import {openUrlInExternalBrowser} from '@shared/utils'; + +const LoadingIcon = ; + +const CloudSync = () => { + const {connect, cloudUser, isConnecting, isInitializing} = useCloudUser(); + const {cloudPolicy, projectInfo, policyInfo} = useCloudPolicy(); + + const dropdownRender = useCallback(() => { + if (isInitializing) { + return ( + + + Initializing... + + ); + } + + if (cloudUser) { + return ( + + + + + Connected to Monokle Cloud + + + + + <GreenCircle /> E-mail + + {cloudUser.email} + + + {projectInfo?.name ? <GreenCircle /> : <RedCircle />} Cloud project + + {projectInfo?.name ? ( + <> + openUrlInExternalBrowser(projectInfo.link)}>{projectInfo.name} + + + ) : ( + <> + Not found + + + )} + + + + {cloudPolicy ? <GreenCircle /> : <RedCircle />} Policy + + {cloudPolicy ? ( + <> + openUrlInExternalBrowser(policyInfo?.link)}>View project policy + + + ) : ( + <> + Not found + + + )} + + + + ); + } + return ( + + + + + Not Connected to Monokle Cloud + + + + + ); + }, [connect, cloudUser, isConnecting, cloudPolicy, isInitializing, projectInfo, policyInfo]); + + return ( + + document.getElementById('monokleCloudSync')!} + > +
+ + + {projectInfo?.name} + +
+
+
+ + ); +}; + +export default CloudSync; + +const Info = (props: {description: string}) => { + const {description} = props; + return ( + + + + ); +}; + +const Container = styled.div<{$hasText: boolean}>` + display: flex; + align-items: center; + border-radius: 4px; + padding: 0 0.5rem; + background: ${Colors.grey3b}; + border: none; + min-width: fit-content; +`; + +const Icon = styled.img` + cursor: pointer; + height: 20px; + width: 20px; + margin-right: 4px; +`; + +const Image = styled.img` + margin-bottom: 8px; + margin-left: -4px; +`; + +const DropdownContent = styled.div` + padding: 28px; + margin-top: 10px; + background-color: ${Colors.grey2}; +`; + +const GreenSpan = styled.span` + color: ${Colors.polarGreen}; +`; + +const RedSpan = styled.span` + color: ${Colors.red7}; +`; + +const GraySpan = styled.span` + color: ${Colors.grey7}; +`; + +const GreenCircle = styled(CheckCircleFilled)` + font-size: 12px; + color: ${Colors.polarGreen}; + margin-right: 4px; +`; + +const RedCircle = styled(CloseCircleFilled)` + font-size: 12px; + color: ${Colors.red7}; + margin-right: 4px; +`; + +const Item = styled.div` + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 8px; +`; + +const Title = styled.div` + width: 120px; + margin-right: 8px; + color: ${Colors.grey7}; +`; + +const Value = styled.div` + flex: 1; + text-align: left; + margin-left: 8px; +`; + +const InfoIcon = styled(InfoCircleFilled)` + color: ${Colors.grey6}; + margin-left: 8px; + cursor: pointer; +`; + +const Link = styled.span` + cursor: pointer; + color: ${Colors.blue7}; +`; diff --git a/src/components/organisms/PageHeader/CloudSync/index.tsx b/src/components/organisms/PageHeader/CloudSync/index.tsx new file mode 100644 index 0000000000..b93a69bf74 --- /dev/null +++ b/src/components/organisms/PageHeader/CloudSync/index.tsx @@ -0,0 +1 @@ +export {default} from './CloudSync'; diff --git a/src/components/organisms/PageHeader/ClusterControl/Buttons.tsx b/src/components/organisms/PageHeader/ClusterControl/Buttons.tsx index 38ce9e7220..7317f00785 100644 --- a/src/components/organisms/PageHeader/ClusterControl/Buttons.tsx +++ b/src/components/organisms/PageHeader/ClusterControl/Buttons.tsx @@ -90,12 +90,12 @@ const SquareBtn = styled(Button)` `; const ConnectBtn = styled(SquareBtn)` - border-color: ${Colors.geekblue7}; - background-color: ${Colors.geekblue7}; + border-color: ${Colors.blue6}; + background-color: ${Colors.blue6}; `; const CloseBtn = styled(SquareBtn)` min-width: 20px; border: none; - color: ${Colors.geekblue7}; + color: ${Colors.blue6}; `; diff --git a/src/components/organisms/PageHeader/K8sVersionSelection.style.tsx b/src/components/organisms/PageHeader/K8sVersionSelection.style.tsx index 1550273e4b..7bae10b678 100644 --- a/src/components/organisms/PageHeader/K8sVersionSelection.style.tsx +++ b/src/components/organisms/PageHeader/K8sVersionSelection.style.tsx @@ -76,7 +76,7 @@ export const WarningText = styled(Typography.Text)` `; export const K8sVersionText = styled(Typography.Text)` - color: ${Colors.blue8}; + color: ${Colors.blue6}; `; export const MenuItem = styled(Menu.Item)<{$selected: boolean}>` diff --git a/src/components/organisms/PageHeader/PageHeader.tsx b/src/components/organisms/PageHeader/PageHeader.tsx index 5872f5e978..ac11e4356d 100644 --- a/src/components/organisms/PageHeader/PageHeader.tsx +++ b/src/components/organisms/PageHeader/PageHeader.tsx @@ -42,6 +42,7 @@ import {Icon} from '@monokle/components'; import {isInClusterModeSelector, isInPreviewModeSelector} from '@shared/utils/selectors'; import {trackEvent} from '@shared/utils/telemetry'; +import CloudSync from './CloudSync'; import {ClusterControls} from './ClusterControl/ClusterControls'; import DownloadProgress from './DownloadProgress'; import {K8sVersionSelection} from './K8sVersionSelection'; @@ -281,6 +282,7 @@ const PageHeader = () => {
+ {isInPreviewMode ? : } diff --git a/src/components/organisms/PaneManager/activities.tsx b/src/components/organisms/PaneManager/activities.tsx index 08811a8d01..ed6bc96707 100644 --- a/src/components/organisms/PaneManager/activities.tsx +++ b/src/components/organisms/PaneManager/activities.tsx @@ -38,7 +38,7 @@ export const activities: ActivityType[] = [ icon: () => , component: , useBadge: () => undefined, - isVisible: () => Boolean(useAppSelector(activeProjectSelector)), + isVisible: () => Boolean(useAppSelector(activeProjectSelector)) || Boolean(useAppSelector(isInClusterModeSelector)), }, { type: 'panel', diff --git a/src/components/organisms/ResourceSetSelector/ResourceSetTypeSelect.tsx b/src/components/organisms/ResourceSetSelector/ResourceSetTypeSelect.tsx index d208600cba..661667ed9f 100644 --- a/src/components/organisms/ResourceSetSelector/ResourceSetTypeSelect.tsx +++ b/src/components/organisms/ResourceSetSelector/ResourceSetTypeSelect.tsx @@ -1,4 +1,5 @@ import {useCallback} from 'react'; +import {useMount} from 'react-use'; import {Select} from 'antd'; @@ -19,6 +20,8 @@ type Props = { export const ResourceSetTypeSelect: React.FC = ({side}) => { const dispatch = useAppDispatch(); + const currentContext = useAppSelector(state => state.main.clusterConnection?.context); + const isInQuickClusterMode = useAppSelector(state => state.ui.isInQuickClusterMode); const isGitDisabled = useAppSelector(state => Boolean(!state.git.repo)); const isHelmDisabled = useAppSelector(state => isEmpty(state.main.helmChartMap)); const isCommandDisabled = useAppSelector(state => @@ -29,6 +32,16 @@ export const ResourceSetTypeSelect: React.FC = ({side}) => { const isKustomizeDisabled = useAppSelector(state => isEmpty(kustomizationsSelector(state))); const resourceSet = useAppSelector(state => selectResourceSet(state.compare, side)); + useMount(() => { + if (!isInQuickClusterMode) return; + if (side === 'left') { + dispatch(resourceSetSelected({side, value: {type: 'cluster', context: currentContext}})); + } + if (side === 'right') { + dispatch(resourceSetSelected({side, value: {type: 'cluster'}})); + } + }); + const handleSelectType = useCallback( (type: ResourceSet['type']) => { if (type === 'local' || type === 'git') { @@ -48,22 +61,26 @@ export const ResourceSetTypeSelect: React.FC = ({side}) => { value={resourceSet?.type === 'helm-custom' ? 'helm' : resourceSet?.type} style={{width: 180}} > - Local + {!isInQuickClusterMode && Local} Cluster - - Helm Preview - - - Kustomize Preview - - - Git - - - Command - + {!isInQuickClusterMode && ( + <> + + Helm Preview + + + Kustomize Preview + + + Git + + + Command + + + )} ); diff --git a/src/components/organisms/SettingsPane/CloudConnect/CloudConnect.styled.tsx b/src/components/organisms/SettingsPane/CloudConnect/CloudConnect.styled.tsx new file mode 100644 index 0000000000..eeb7a64d1d --- /dev/null +++ b/src/components/organisms/SettingsPane/CloudConnect/CloudConnect.styled.tsx @@ -0,0 +1,26 @@ +import {Row as RawRow} from 'antd'; + +import styled from 'styled-components'; + +import {Colors} from '@shared/styles/colors'; + +export const Row = styled(RawRow)` + overflow: hidden; + padding: 24px; + background: linear-gradient(94.81deg, rgba(42, 56, 90, 0.4) 7%, rgba(53, 35, 60, 0.4) 101.38%); +`; + +export const Heading = styled.h1` + font-size: 24px; + font-weight: 700; + color: ${Colors.geekblue8}; +`; + +export const Subheading = styled.h2` + font-weight: 700; + font-size: 14px; +`; + +export const Paragraph = styled.p` + font-size: 12px; +`; diff --git a/src/components/organisms/SettingsPane/CloudConnect/CloudConnect.tsx b/src/components/organisms/SettingsPane/CloudConnect/CloudConnect.tsx new file mode 100644 index 0000000000..8338858f42 --- /dev/null +++ b/src/components/organisms/SettingsPane/CloudConnect/CloudConnect.tsx @@ -0,0 +1,85 @@ +import {Button, Col, Popconfirm, Row} from 'antd'; + +import {useCloudUser} from '@redux/validation/validation.hooks'; + +import CloudSync from '@assets/CloudSync.svg'; + +import {Spinner} from '@monokle/components'; + +import * as S from './CloudConnect.styled'; + +export default function CloudConnect(props: {wide: boolean}) { + const {wide} = props; + const {connect, disconnect, cloudUser, isInitializing, isConnecting, isDisconnecting} = useCloudUser(); + + if (isInitializing) { + return ( + + + + ); + } + + return ( + + + + + {cloudUser ? ( + <> + Connected to Monokle Cloud + + E-mail: {cloudUser.email} + + + You have successfuly connected to your Monokle Cloud Account.
+ Your validation policies are now unified and synchronized across platforms. +
+ + + + + ) : ( + <> + Connect with Monokle Cloud + What is Monokle Cloud? + + Monokle Cloud is our comprehensive cloud-based solution designed to seamlessly synchronize, validate, + and manage your Kubernetes configurations. By working hand-in-hand with Monokle Desktop, it ensures + that your configurations remain consistent and error-free + + Why connect? +
    +
  • + Unified Validation: Ensure that the same validation policies applied in the cloud are consistent + with those in your local projects. +
  • +
  • + Sync Seamlessly: Automatically synchronize and update your validation rules and policies between + Monokle Cloud and Desktop. +
  • +
  • + Collaborate Efficiently: Work in tandem with your team, ensuring everyone adheres to the latest and + most accurate configuration standards. +
  • +
+ + + )} + + + + +
+ +
+ ); +} diff --git a/src/components/organisms/SettingsPane/CloudConnect/index.tsx b/src/components/organisms/SettingsPane/CloudConnect/index.tsx new file mode 100644 index 0000000000..a8fe49271f --- /dev/null +++ b/src/components/organisms/SettingsPane/CloudConnect/index.tsx @@ -0,0 +1 @@ +export {default} from './CloudConnect'; diff --git a/src/components/organisms/SettingsPane/SettingsPane.tsx b/src/components/organisms/SettingsPane/SettingsPane.tsx index ee72cadcea..50e43336b9 100644 --- a/src/components/organisms/SettingsPane/SettingsPane.tsx +++ b/src/components/organisms/SettingsPane/SettingsPane.tsx @@ -10,6 +10,7 @@ import {TitleBar} from '@monokle/components'; import {SettingsPanel} from '@shared/models/config'; import ValidationSettings from '../ValidationSettings'; +import CloudConnect from './CloudConnect'; import {CurrentProjectSettings} from './CurrentProjectSettings/CurrentProjectSettings'; import {DefaultProjectSettings} from './DefaultProjectSettings/DefaultProjectSettings'; import {GlobalSettings} from './GlobalSettings/GlobalSettings'; @@ -94,6 +95,18 @@ const SettingsPane = () => { }, ] : []), + + ...[ + { + key: 'cloud-connect', + label: Sync with Cloud, + children: ( + + + + ), + }, + ], ], [activeProject, isInQuickClusterMode, isOnStartProjectPage, isStartProjectPaneVisible] ); diff --git a/src/components/organisms/ValidationSettings/ValidationCustom/ValidationCustom.tsx b/src/components/organisms/ValidationSettings/ValidationCustom/ValidationCustom.tsx index 07b683e833..a5e0126795 100644 --- a/src/components/organisms/ValidationSettings/ValidationCustom/ValidationCustom.tsx +++ b/src/components/organisms/ValidationSettings/ValidationCustom/ValidationCustom.tsx @@ -1,10 +1,11 @@ import {useCallback, useLayoutEffect} from 'react'; -import {Space, Switch, Tooltip} from 'antd'; +import {Button, Space, Switch, Tooltip} from 'antd'; import {TOOLTIP_DELAY} from '@constants/constants'; -import {useAppDispatch} from '@redux/hooks'; +import {useAppDispatch, useAppSelector} from '@redux/hooks'; +import {isUsingCloudPolicySelector} from '@redux/validation/validation.selectors'; import {toggleRule, toggleValidation} from '@redux/validation/validation.slice'; import {PluginMetadataWithConfig} from '@monokle/validation'; @@ -23,6 +24,8 @@ const ValidationCustom: React.FC = props => { const dispatch = useAppDispatch(); + const isUsingCloudPolicy = useAppSelector(isUsingCloudPolicySelector); + const toggleEnabled = useCallback(() => { dispatch(toggleValidation(name)); trackEvent('configure/toggle_validation', {id: name}); @@ -57,13 +60,17 @@ const ValidationCustom: React.FC = props => { Enable plugin - + - Enable all - Disable all + + diff --git a/src/components/organisms/ValidationSettings/ValidationCustom/useValidationTable.tsx b/src/components/organisms/ValidationSettings/ValidationCustom/useValidationTable.tsx index 71692e7a13..02803d930d 100644 --- a/src/components/organisms/ValidationSettings/ValidationCustom/useValidationTable.tsx +++ b/src/components/organisms/ValidationSettings/ValidationCustom/useValidationTable.tsx @@ -7,7 +7,8 @@ import styled from 'styled-components'; import {TOOLTIP_DELAY} from '@constants/constants'; -import {useAppDispatch} from '@redux/hooks'; +import {useAppDispatch, useAppSelector} from '@redux/hooks'; +import {isUsingCloudPolicySelector} from '@redux/validation/validation.selectors'; import {changeRuleLevel, toggleRule} from '@redux/validation/validation.slice'; import {Icon, IconNames} from '@monokle/components'; @@ -24,6 +25,8 @@ const VALIDATION_HIDING_LABELS_WIDTH = 450; export function useValidationTable(plugin: PluginMetadataWithConfig, width: number) { const dispatch = useAppDispatch(); + const isUsingCloudPolicy = useAppSelector(isUsingCloudPolicySelector); + const handleToggle = useCallback( (rule: Rule) => { dispatch(toggleRule({plugin: plugin.name, rule: rule.name})); @@ -91,14 +94,14 @@ export function useValidationTable(plugin: PluginMetadataWithConfig, width: numb return ( handleToggle(rule)} /> @@ -109,7 +112,7 @@ export function useValidationTable(plugin: PluginMetadataWithConfig, width: numb }), }, ]; - }, [width, plugin.configuration.enabled, changeLevel, handleToggle]); + }, [width, plugin.configuration.enabled, changeLevel, handleToggle, isUsingCloudPolicy]); return columns; } diff --git a/src/components/organisms/ValidationSettings/ValidationOverview/ValidationCard.tsx b/src/components/organisms/ValidationSettings/ValidationOverview/ValidationCard.tsx index 7c76015a30..aea1d7708d 100644 --- a/src/components/organisms/ValidationSettings/ValidationOverview/ValidationCard.tsx +++ b/src/components/organisms/ValidationSettings/ValidationOverview/ValidationCard.tsx @@ -2,8 +2,12 @@ import {shell} from 'electron'; import {useCallback, useState} from 'react'; -import {useAppDispatch} from '@redux/hooks'; -import {pluginRulesSelector, useValidationSelector} from '@redux/validation/validation.selectors'; +import {useAppDispatch, useAppSelector} from '@redux/hooks'; +import { + isUsingCloudPolicySelector, + pluginRulesSelector, + useValidationSelector, +} from '@redux/validation/validation.selectors'; import {toggleValidation, updateSelectedPluginConfiguration} from '@redux/validation/validation.slice'; import {IconNames} from '@monokle/components'; @@ -31,6 +35,8 @@ const ValidationCard: React.FC = ({configurable, plugin}) => { const dispatch = useAppDispatch(); const hasRules = useValidationSelector(s => pluginRulesSelector(s, name).length > 0); + const isUsingCloudPolicy = useAppSelector(isUsingCloudPolicySelector); + const [isChecked, setIsChecked] = useState(enabled); const openLearnMore = useCallback(() => shell.openExternal(learnMoreUrl || ''), [learnMoreUrl]); @@ -61,11 +67,11 @@ const ValidationCard: React.FC = ({configurable, plugin}) => { {hasRules && configurable && ( - Configure + {isUsingCloudPolicy ? 'View' : 'Configure'} )} - + ); }; diff --git a/src/components/organisms/ValidationSettings/ValidationOverview/ValidationCardPolicy.tsx b/src/components/organisms/ValidationSettings/ValidationOverview/ValidationCardPolicy.tsx new file mode 100644 index 0000000000..18e9a29278 --- /dev/null +++ b/src/components/organisms/ValidationSettings/ValidationOverview/ValidationCardPolicy.tsx @@ -0,0 +1,65 @@ +import styled from 'styled-components'; + +import CloudManaged from '@assets/CloudManaged.png'; + +import {Colors} from '@shared/styles'; + +export function ValidationCardPolicy() { + return ( + + + + +
+ A Monokle Cloud policy manages this repository + + + Policies add compliancy across your project's repositories by providing consistent validation. The + configuration can no longer be adjusted in this pane. Set up below is read-only. + +
+
+ ); +} + +const CardContainer = styled.div` + border-radius: 2px; + background-color: ${Colors.geekblue4}; + padding: 12px; + display: flex; + gap: 12px; + margin-bottom: 20px; + align-items: center; + padding-right: 32px; +`; + +const Description = styled.div` + color: ${Colors.grey9}; + line-height: 22px; + + & span { + color: ${Colors.blue7}; + font-weight: 700; + cursor: pointer; + transition: all 0.2s ease-in; + + &:hover { + color: ${Colors.blue6}; + } + } +`; + +const ImageContainer = styled.div` + padding: 24px; +`; + +const Image = styled.img` + height: 60px; +`; + +const Title = styled.div` + font-weight: 700; + line-height: 22px; + color: ${Colors.whitePure}; + margin-bottom: 12px; +`; diff --git a/src/components/organisms/ValidationSettings/ValidationOverview/ValidationOverview.tsx b/src/components/organisms/ValidationSettings/ValidationOverview/ValidationOverview.tsx index 33f538c31b..8336b73f86 100644 --- a/src/components/organisms/ValidationSettings/ValidationOverview/ValidationOverview.tsx +++ b/src/components/organisms/ValidationSettings/ValidationOverview/ValidationOverview.tsx @@ -1,6 +1,7 @@ import React from 'react'; -import {useValidationSelector} from '@redux/validation/validation.selectors'; +import {useAppSelector} from '@redux/hooks'; +import {isUsingCloudPolicySelector, useValidationSelector} from '@redux/validation/validation.selectors'; import {CRD_SCHEMA_INTEGRATION} from '@shared/models/validationPlugins'; @@ -8,14 +9,18 @@ import {VALIDATION_CONFIGURATION_COMPONENTS} from './ConfigurationComponents'; import CustomValidationCard from './CustomValidationCard'; import ValidationCard from './ValidationCard'; import {ValidationCardPlugins} from './ValidationCardPlugins'; +import {ValidationCardPolicy} from './ValidationCardPolicy'; import ValidationCardUpNext from './ValidationCardUpNext'; import * as S from './ValidationOverview.styled'; const ValidationOverview: React.FC = () => { const plugins = useValidationSelector(s => Object.values(s.metadata ?? {})); + const isUsingCloudPolicy = useAppSelector(isUsingCloudPolicySelector); return ( + {isUsingCloudPolicy && } + {plugins.map(plugin => ( ('cloud:login'); +export const logoutFromCloud = invokeIpc('cloud:logout'); +export const getCloudUser = invokeIpc('cloud:getUser'); +export const getCloudPolicy = invokeIpc('cloud:getPolicy'); +export const getCloudInfo = invokeIpc( + 'cloud:getInfo' +); diff --git a/src/redux/initialState.ts b/src/redux/initialState.ts index 2ee12b58dc..0fbcc6f3a8 100644 --- a/src/redux/initialState.ts +++ b/src/redux/initialState.ts @@ -5,6 +5,7 @@ import {DEFAULT_PANE_CONFIGURATION} from '@constants/constants'; import {PREDEFINED_K8S_VERSION} from '@shared/constants/k8s'; import {AlertState} from '@shared/models/alert'; import {AppState} from '@shared/models/appState'; +import {CloudState} from '@shared/models/cloud'; import {AppConfig, NewVersionCode, SettingsPanel} from '@shared/models/config'; import {ExtensionState} from '@shared/models/extension'; import {TerminalState} from '@shared/models/terminal'; @@ -269,6 +270,8 @@ const initialTerminalState: TerminalState = { terminalsMap: {}, }; +const initialCloudState: CloudState = {}; + export default { alert: initialAlertState, config: initialAppConfigState, @@ -276,4 +279,5 @@ export default { main: initialAppState, terminal: initialTerminalState, ui: initialUiState, + cloud: initialCloudState, }; diff --git a/src/redux/reducers/cloud.ts b/src/redux/reducers/cloud.ts new file mode 100644 index 0000000000..7769e34805 --- /dev/null +++ b/src/redux/reducers/cloud.ts @@ -0,0 +1,24 @@ +import {Draft, PayloadAction, createSlice} from '@reduxjs/toolkit'; + +import initialState from '@redux/initialState'; + +import {CloudPolicyInfo, CloudProjectInfo, CloudState, CloudUser} from '@shared/models/cloud'; + +export const cloudSlice = createSlice({ + name: 'cloud', + initialState: initialState.cloud, + reducers: { + setCloudUser: (state: Draft, action: PayloadAction) => { + state.user = action.payload; + }, + setCloudProjectInfo: (state: Draft, action: PayloadAction) => { + state.projectInfo = action.payload; + }, + setCloudPolicyInfo: (state: Draft, action: PayloadAction) => { + state.policyInfo = action.payload; + }, + }, +}); + +export const {setCloudUser, setCloudPolicyInfo, setCloudProjectInfo} = cloudSlice.actions; +export default cloudSlice.reducer; diff --git a/src/redux/store.ts b/src/redux/store.ts index 3e40aa80b9..602b8c0ed6 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -16,6 +16,7 @@ import {formSlice} from './forms'; import {gitSlice} from './git'; import {combineListeners, listenerMiddleware} from './listeners/base'; import {alertSlice} from './reducers/alert'; +import {cloudSlice} from './reducers/cloud'; import {extensionSlice} from './reducers/extension'; import {mainSlice} from './reducers/main'; import {imageListParserListener} from './reducers/main/mainListeners'; @@ -70,6 +71,7 @@ const appReducer = combineReducers({ dashboard: dashboardSlice.reducer, cluster: clusterSlice.reducer, editor: editorSlice.reducer, + cloud: cloudSlice.reducer, }); const rootReducer: typeof appReducer = (state, action) => { diff --git a/src/redux/thunks/cluster/loadClusterHelmReleases.ts b/src/redux/thunks/cluster/loadClusterHelmReleases.ts index eb16b25e44..f287a85f8d 100644 --- a/src/redux/thunks/cluster/loadClusterHelmReleases.ts +++ b/src/redux/thunks/cluster/loadClusterHelmReleases.ts @@ -18,7 +18,10 @@ export const loadClusterHelmReleases = createAsyncThunk', '')}) + listHelmReleasesCommand({ + filter: helmRepoSearch, + namespace: selectedNamespace?.replace('', '').replace('', ''), + }) ); if (result.stderr) { dispatch(setAlert(errorAlert("Couldn't load cluster helm releases", result.stderr))); diff --git a/src/redux/thunks/cluster/loadClusterResources.ts b/src/redux/thunks/cluster/loadClusterResources.ts index ec487f7a59..ec8aa1e2b6 100644 --- a/src/redux/thunks/cluster/loadClusterResources.ts +++ b/src/redux/thunks/cluster/loadClusterResources.ts @@ -26,18 +26,25 @@ import {trackEvent} from '@shared/utils/telemetry'; import {findDefaultVersionForCRD} from './findDefaultVersionForCRD'; const getNonCustomClusterObjects = async (kc: any, namespace?: string, allNamespaces?: boolean) => { + const registeredHandlers = getRegisteredKindHandlers(); + const filteredHandlers = registeredHandlers.filter(handler => { + if (handler.isCustom) { + return false; + } + if (allNamespaces) { + return true; + } + if (namespace === '') { + return !handler.isNamespaced; + } + return handler.isNamespaced; + }); return Promise.allSettled( - getRegisteredKindHandlers() - .filter( - handler => - !handler.isCustom && - (allNamespaces ? true : namespace === '' ? !handler.isNamespaced : handler.isNamespaced) - ) - .map(resourceKindHandler => - resourceKindHandler - .listResourcesInCluster(kc, {namespace}) - .then(items => getK8sObjectsAsYaml(items, resourceKindHandler.kind, resourceKindHandler.clusterApiVersion)) - ) + filteredHandlers.map(resourceKindHandler => + resourceKindHandler + .listResourcesInCluster(kc, {namespace}) + .then(items => getK8sObjectsAsYaml(items, resourceKindHandler.kind, resourceKindHandler.clusterApiVersion)) + ) ); }; @@ -121,8 +128,6 @@ const loadClusterResourcesHandler = async ( if (currentNamespace === '') { results = await getNonCustomClusterObjects(kc, undefined, true); - } else if (currentNamespace === '') { - results = await getNonCustomClusterObjects(kc); } else { results = await getNonCustomClusterObjects(kc, currentNamespace); } diff --git a/src/redux/thunks/runPreviewConfiguration.ts b/src/redux/thunks/runPreviewConfiguration.ts index 9a7590dbcf..0f06354b98 100644 --- a/src/redux/thunks/runPreviewConfiguration.ts +++ b/src/redux/thunks/runPreviewConfiguration.ts @@ -36,12 +36,15 @@ export const runPreviewConfiguration = createAsyncThunk< { helmConfigId: string; performDeploy?: boolean; + selectedNamespace?: string; + shouldCreateNamespace?: boolean; }, { dispatch: AppDispatch; state: RootState; } ->('main/runPreviewConfiguration', async ({helmConfigId, performDeploy}, thunkAPI) => { +>('main/runPreviewConfiguration', async (props, thunkAPI) => { + const {helmConfigId, performDeploy, selectedNamespace, shouldCreateNamespace} = props; const startTime = new Date().getTime(); const configState = thunkAPI.getState().config; const mainState = thunkAPI.getState().main; @@ -133,6 +136,16 @@ export const runPreviewConfiguration = createAsyncThunk< env: {KUBECONFIG: kubeconfig.path}, }; + if (selectedNamespace) { + if (!commandOptions.args.some(arg => arg.includes('--namespace'))) { + commandOptions.args.push(...['--namespace', selectedNamespace]); + } + + if (shouldCreateNamespace && !commandOptions.args.some(arg => arg.includes('--create-namespace'))) { + commandOptions.args.push('--create-namespace'); + } + } + const result = await runCommandInMainThread(commandOptions); if (result.error || result.stderr) { diff --git a/src/redux/validation/validation.hooks.ts b/src/redux/validation/validation.hooks.ts new file mode 100644 index 0000000000..4c6d83afa8 --- /dev/null +++ b/src/redux/validation/validation.hooks.ts @@ -0,0 +1,145 @@ +import {useCallback, useEffect, useState} from 'react'; +import {useStore} from 'react-redux'; +import {useAsync, useInterval, useMount} from 'react-use'; + +import {isEqual} from 'lodash'; + +import {getCloudInfo, getCloudPolicy, getCloudUser, logoutFromCloud, startCloudLogin} from '@redux/cloud/ipc'; +import {useAppDispatch, useAppSelector} from '@redux/hooks'; +import {setAlert} from '@redux/reducers/alert'; +import {setCloudPolicyInfo, setCloudProjectInfo, setCloudUser} from '@redux/reducers/cloud'; +import {rootFolderSelector} from '@redux/selectors'; + +import {ROOT_FILE_ENTRY} from '@shared/constants/fileEntry'; +import {AlertEnum} from '@shared/models/alert'; +import {AppDispatch} from '@shared/models/appDispatch'; +import {RootState} from '@shared/models/rootState'; +import {trackEvent} from '@shared/utils'; + +import {setCloudPolicy} from './validation.slice'; + +/** + * Used as a telemetry cache to avoid sending multiple events for the same project + */ +const cloudProjectsThisSession = new Set(); + +export const pollCloudPolicy = async (state: RootState, dispatch: AppDispatch) => { + const rootFileEntry = state.main.fileMap[ROOT_FILE_ENTRY]; + const rootFolderPath = rootFileEntry?.filePath; + const cloudPolicy = rootFolderPath ? await getCloudPolicy(rootFolderPath) : undefined; + const previousCloudPolicy = state.validation.cloudPolicy; + + if (!cloudPolicy) { + dispatch(setCloudPolicy(undefined)); + return; + } + + if (previousCloudPolicy && isEqual(previousCloudPolicy, cloudPolicy)) { + return; + } + + if (!previousCloudPolicy) { + dispatch( + setAlert({ + type: AlertEnum.Success, + title: 'Repository connected to Cloud Project', + message: + 'This repository has been connected successfully to a Cloud Project. The Policy is now being synchronized.', + }) + ); + } else { + dispatch( + setAlert({ + type: AlertEnum.Success, + title: 'Cloud Policy updated', + message: 'The Policy has been changed in the Cloud Project and is now being synchronized.', + }) + ); + } + + dispatch(setCloudPolicy(cloudPolicy)); +}; + +export const useCloudPolicy = () => { + const dispatch = useAppDispatch(); + const store = useStore(); + const cloudPolicy = useAppSelector(state => state.validation.cloudPolicy); + const rootFolderPath = useAppSelector(rootFolderSelector); + + const projectInfo = useAppSelector(state => state.cloud.projectInfo); + const policyInfo = useAppSelector(state => state.cloud.policyInfo); + + const updateProjectInfo = useCallback(async () => { + const cloudInfo = await getCloudInfo(rootFolderPath); + dispatch(setCloudProjectInfo(cloudInfo?.projectInfo)); + dispatch(setCloudPolicyInfo(cloudInfo?.policyInfo)); + + if (cloudPolicy && cloudInfo?.projectInfo && !cloudProjectsThisSession.has(cloudInfo.projectInfo.slug)) { + trackEvent('cloud_sync/policy', {projectSlug: cloudInfo.projectInfo.slug}); + cloudProjectsThisSession.add(cloudInfo.projectInfo.slug); + } + }, [rootFolderPath, cloudPolicy, dispatch]); + + useMount(() => { + pollCloudPolicy(store.getState(), dispatch); + updateProjectInfo(); + }); + + useEffect(() => { + pollCloudPolicy(store.getState(), dispatch); + updateProjectInfo(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [rootFolderPath]); + + useInterval(() => { + pollCloudPolicy(store.getState(), dispatch); + updateProjectInfo(); + }, 10 * 1000); + + return {cloudPolicy, projectInfo, policyInfo}; +}; + +export const useCloudUser = () => { + const store = useStore(); + const dispatch = useAppDispatch(); + const [isInitializing, setIsInitializing] = useState(false); + const [isConnecting, setIsConnecting] = useState(false); + const [isDisconnecting, setIsDisconnecting] = useState(false); + const cloudUser = useAppSelector(state => state.cloud.user); + + useAsync(async () => { + setIsInitializing(true); + const user = await getCloudUser(undefined); + dispatch(setCloudUser(user)); + setIsInitializing(false); + }, []); + + const connect = async () => { + setIsConnecting(true); + const {user} = await startCloudLogin(undefined); + trackEvent('cloud_sync/login'); + dispatch(setCloudUser(user)); + pollCloudPolicy(store.getState(), dispatch); + setIsConnecting(false); + }; + + const disconnect = async () => { + setIsDisconnecting(true); + await logoutFromCloud(undefined); + trackEvent('cloud_sync/logout'); + dispatch(setCloudUser(undefined)); + dispatch(setCloudPolicy(undefined)); + dispatch(setCloudProjectInfo(undefined)); + dispatch(setCloudPolicyInfo(undefined)); + setIsDisconnecting(false); + }; + + return { + connect, + disconnect, + cloudUser, + isInitializing, + isConnecting, + isDisconnecting, + }; +}; diff --git a/src/redux/validation/validation.listeners.tsx b/src/redux/validation/validation.listeners.tsx index 850db102c2..bb56d12c7e 100644 --- a/src/redux/validation/validation.listeners.tsx +++ b/src/redux/validation/validation.listeners.tsx @@ -54,6 +54,7 @@ import { addValidationPlugin, changeRuleLevel, removeValidationPlugin, + setCloudPolicy, setConfigK8sSchemaVersion, toggleRule, toggleValidation, @@ -81,7 +82,8 @@ const loadListener: AppListenerFn = listen => { toggleValidation, changeRuleLevel, addValidationPlugin, - removeValidationPlugin + removeValidationPlugin, + setCloudPolicy ), async effect(_action, {dispatch, delay, signal, cancelActiveListeners}) { trackEvent('validation/load_config', {actionType: _action.type}); diff --git a/src/redux/validation/validation.selectors.ts b/src/redux/validation/validation.selectors.ts index ec64e7742b..30a9552525 100644 --- a/src/redux/validation/validation.selectors.ts +++ b/src/redux/validation/validation.selectors.ts @@ -219,7 +219,8 @@ export const activePluginsSelector = createDeepEqualSelector( ); export const pluginEnabledSelector = createDeepEqualSelector( - (state: RootState, id: string) => state.validation.config?.plugins?.[id], + (state: RootState, id: string) => + state.validation.cloudPolicy?.policy?.plugins?.[id] ?? state.validation.config?.plugins?.[id], (_: RootState, id: string) => id, (_config, id): boolean => VALIDATOR.getPlugin(id)?.enabled ?? false ); @@ -280,3 +281,8 @@ export const problemResourceAndRangeSelector = createDeepEqualSelector( return {resourceId, storage, range}; } ); + +export const isUsingCloudPolicySelector = createSelector( + (state: RootState) => state.validation.cloudPolicy, + cloudPolicy => Boolean(cloudPolicy?.policy && cloudPolicy?.valid) +); diff --git a/src/redux/validation/validation.slice.ts b/src/redux/validation/validation.slice.ts index ff746c5997..e988816037 100644 --- a/src/redux/validation/validation.slice.ts +++ b/src/redux/validation/validation.slice.ts @@ -7,6 +7,7 @@ import {stopClusterConnection} from '@redux/thunks/cluster'; import {setRootFolder} from '@redux/thunks/setRootFolder'; import {ValidationFiltersValueType} from '@monokle/components'; +import type {PolicyData} from '@monokle/synchronizer'; import {CORE_PLUGINS, PluginMetadataWithConfig} from '@monokle/validation'; import {SelectedProblem, ValidationState} from '@shared/models/validation'; import {CustomValidationPlugin} from '@shared/models/validationPlugins'; @@ -174,11 +175,16 @@ export const validationSlice = createSlice({ state.config.plugins = pick(state.config.plugins, CORE_PLUGINS); } }, + + setCloudPolicy(state, {payload}: PayloadAction) { + state.cloudPolicy = payload; + }, }, extraReducers: builder => { builder.addCase(setRootFolder.fulfilled, state => { state.validationOverview.selectedProblem = undefined; state.lastResponse = undefined; + state.cloudPolicy = undefined; state.validationOverview.newProblemsIntroducedType = 'initial'; }); @@ -237,5 +243,6 @@ export const { updateSelectedPluginConfiguration, addValidationPlugin, removeValidationPlugin, + setCloudPolicy, } = validationSlice.actions; export default validationSlice.reducer; diff --git a/src/redux/validation/validation.thunks.ts b/src/redux/validation/validation.thunks.ts index 9ce6f7a5e1..b33cd271c2 100644 --- a/src/redux/validation/validation.thunks.ts +++ b/src/redux/validation/validation.thunks.ts @@ -9,7 +9,7 @@ import {activeResourceStorageSelector} from '@redux/selectors/resourceMapSelecto import {getResourceKindHandler} from '@src/kindhandlers'; -import {CORE_PLUGINS, ResourceRefType, ValidationResponse} from '@monokle/validation'; +import {CORE_PLUGINS, Config, ResourceRefType, ValidationResponse} from '@monokle/validation'; import {K8sResource, ResourceMeta} from '@shared/models/k8sResource'; import type {ThunkApi} from '@shared/models/thunk'; import type {LoadValidationResult, ValidationArgs, ValidationResource} from '@shared/models/validation'; @@ -24,15 +24,18 @@ export const loadValidation = createAsyncThunk [p, false])), }; + merge(localConfig, state.config); + electronStore.set('validation.config', localConfig); - merge(config, state.config); - - electronStore.set('validation.config', config); + let cloudConfig: Config | undefined; + if (state.cloudPolicy?.policy && state.cloudPolicy.valid) { + cloudConfig = state.cloudPolicy.policy; + } - await VALIDATOR.loadValidation({config}); + await VALIDATOR.loadValidation({config: cloudConfig ?? localConfig}); return { metadata: VALIDATOR.metadata, diff --git a/src/shared/models/cloud.ts b/src/shared/models/cloud.ts new file mode 100644 index 0000000000..bcf5d13f39 --- /dev/null +++ b/src/shared/models/cloud.ts @@ -0,0 +1,26 @@ +import {ProjectInfo} from '@monokle/synchronizer'; + +/** + * This type is used by the renderer so it doesn't need to have all the properties of the User class + */ +export type CloudUser = { + readonly email: string; +}; + +export type CloudLoginResponse = { + user: CloudUser; +}; + +export type CloudProjectInfo = ProjectInfo & { + link: string; +}; + +export type CloudPolicyInfo = { + link: string; +}; + +export type CloudState = { + user?: CloudUser; + projectInfo?: CloudProjectInfo; + policyInfo?: CloudPolicyInfo; +}; diff --git a/src/shared/models/rootState.ts b/src/shared/models/rootState.ts index 1e726e4b32..fe7881f11b 100644 --- a/src/shared/models/rootState.ts +++ b/src/shared/models/rootState.ts @@ -1,5 +1,6 @@ import {AlertState} from './alert'; import {AppState} from './appState'; +import {CloudState} from './cloud'; import {ClusterState} from './clusterState'; import {CompareState} from './compare'; import {AppConfig} from './config'; @@ -28,6 +29,7 @@ type RootState = { validation: ValidationState; dashboard: DashboardState; cluster: ClusterState; + cloud: CloudState; }; type ElectronMenuDataType = { diff --git a/src/shared/models/telemetry.ts b/src/shared/models/telemetry.ts index 73f6007c9b..e4f02128a5 100644 --- a/src/shared/models/telemetry.ts +++ b/src/shared/models/telemetry.ts @@ -195,6 +195,9 @@ export type EventMap = { 'project_summary/new_empty_resource': undefined; 'project_summary/new_ai_resource': undefined; 'project_summary/select_explorer_section': {section: string}; + 'cloud_sync/login': undefined; + 'cloud_sync/logout': undefined; + 'cloud_sync/policy': {projectSlug: string}; }; export const APP_INSTALLED = 'APP_INSTALLED'; diff --git a/src/shared/models/validation.ts b/src/shared/models/validation.ts index 8e4380a989..facd3dab07 100644 --- a/src/shared/models/validation.ts +++ b/src/shared/models/validation.ts @@ -1,4 +1,5 @@ import type {ValidationFiltersValueType} from '@monokle/components'; +import type {PolicyData} from '@monokle/synchronizer'; import type { Config, PluginMetadataWithConfig, @@ -21,6 +22,7 @@ export type SelectedProblem = { export type ValidationState = { config: Config; + cloudPolicy?: PolicyData; status: Initialization; lastResponse?: ValidationResponse; loadRequestId?: string; diff --git a/src/shared/utils/types.ts b/src/shared/utils/types.ts index 129def2ad2..6b6c703abe 100644 --- a/src/shared/utils/types.ts +++ b/src/shared/utils/types.ts @@ -13,3 +13,10 @@ export type DeepPartial = T extends object [P in keyof T]?: DeepPartial; } : T; + +export type ExtractProperties = Pick< + ObjectType, + { + [Property in keyof ObjectType]: ObjectType[Property] extends (...params: unknown[]) => unknown ? never : Property; + }[keyof ObjectType] +>;