diff --git a/.github/workflows/deploy-pr-preview.yaml b/.github/workflows/deploy-pr-preview.yaml index 8521dc9ed..1bf57f6f6 100644 --- a/.github/workflows/deploy-pr-preview.yaml +++ b/.github/workflows/deploy-pr-preview.yaml @@ -96,7 +96,20 @@ jobs: - name: Build DOOP if changes detected if: github.event.action != 'closed' && contains(needs.run-detect-changes.outputs.changes, 'doop') run: | - ./.github/scripts/build-app.sh + # collect the necessary information + entry_file=$(jq -r '.module // .main' $PACKAGE_PATH/package.json) + build_folder=$(dirname $entry_file) + package_name=$(jq -r '.name' $PACKAGE_PATH/package.json) + + # Run build using turbo + npx turbo run build:static --filter $package_name + + # Copy build folder to deploy path + mkdir -p "$DEPLOY_PATH/$TARGET_FOLDER" + cp -r "$PACKAGE_PATH/$build_folder/." "$DEPLOY_PATH/$TARGET_FOLDER" + + # Generate appProps.json + echo "$APP_PROPS_BASE64" | base64 -d > "$DEPLOY_PATH/$TARGET_FOLDER/appProps.json" env: PACKAGE_PATH: apps/doop TARGET_FOLDER: doop diff --git a/apps/doop/.gitignore b/apps/doop/.gitignore deleted file mode 100644 index 6aaca981d..000000000 --- a/apps/doop/.gitignore +++ /dev/null @@ -1,15 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -node_modules - -# production -build -/build -/public/build - -secretProps.json -secretProps.js -.npm/* - -npm-debug.log* diff --git a/apps/doop/README.md b/apps/doop/README.md index 12f37e242..eb451e570 100644 --- a/apps/doop/README.md +++ b/apps/doop/README.md @@ -4,3 +4,65 @@ [![Built with Juno](https://cloudoperators.github.io/juno/built-with-juno.svg)](https://github.com/cloudoperators/juno) This UI offers a dashboard to aggregate all policy violations reported by the Gatekeeper instances in each cluster. + +# Usage + +## Standalone Mode + +To create a static, runnable build, execute the following commands: + +```bash +cd apps/doop +npx turbo build:static +``` + +This will generate an `index.html` file along with the necessary assets in the dist folder. You’ll need to copy a `appProps.json` file containing the required props into the dist folder. + +## As a Micro Frontend (MFE) + +To build a library version for dynamic import, use the following commands: + +```bash +cd apps/doop +npx turbo build +``` + +This will create a build folder with all assets. You can host this folder and load it as an MFE using dynamic import: + +```html +
+ + +``` + +## Development Mode + +First, create an `appProps.json` file in the root directory of the application (apps/doop), using appProps.template.json as a reference. Customize the file with the necessary properties. Once completed, run the following commands: + +```bash +cd apps/doop +npx turbo dev +``` + +### Testing + +```bash +cd apps/doop +npx turbo test +``` + +## App Props + +These are the customizable application properties (appProps) that you can define in your appProps.json file: + +- **id** (optional): Use unique IDs if you need to instantiate multiple instances of the app on the same page. +- **theme** (optional): Overrides the default theme. Acceptable values are `"theme-light"` or `"theme-dark"` (default). +- **displayName** (optional): The name to be displayed in the app's header. +- **apiEndpoint** (required): The URL of the API endpoint the app will interact with. +- **embedded** (optional): Set to `true` if the app will be embedded within another app or page. When `true`, the app will not display the header or footer, rendering only the content. Default is `false`. +- **isMock** (optional): Use mocked data for development purposes. Default is `false`. +- **showDebugSeverities** (optional): Display debug severity levels in the log. Default is `false`. diff --git a/apps/doop/__mocks__/client.js b/apps/doop/__mocks__/client.js deleted file mode 100644 index e478dae25..000000000 --- a/apps/doop/__mocks__/client.js +++ /dev/null @@ -1,9 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import { JSDOM } from "jsdom" -const dom = new JSDOM() -global.document = dom.window.document -global.window = dom.window diff --git a/apps/doop/__mocks__/fileMock.js b/apps/doop/__mocks__/fileMock.js deleted file mode 100644 index c846a2a3b..000000000 --- a/apps/doop/__mocks__/fileMock.js +++ /dev/null @@ -1,6 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -module.exports = "test-file-stub" diff --git a/apps/doop/__mocks__/styleMock.js b/apps/doop/__mocks__/styleMock.js deleted file mode 100644 index 7aec9878d..000000000 --- a/apps/doop/__mocks__/styleMock.js +++ /dev/null @@ -1,6 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -module.exports = {} diff --git a/apps/doop/appProps.template.json b/apps/doop/appProps.template.json new file mode 100644 index 000000000..f92daaf12 --- /dev/null +++ b/apps/doop/appProps.template.json @@ -0,0 +1,9 @@ +{ + "id": "app-instance-1", + "theme": "theme-dark", + "displayName": "MyApp", + "apiEndpoint": "https://api.example.com/v1", + "embedded": false, + "isMock": true, + "showDebugSeverities": false +} diff --git a/apps/doop/babel.config.js b/apps/doop/babel.config.js deleted file mode 100644 index ab7d61b5b..000000000 --- a/apps/doop/babel.config.js +++ /dev/null @@ -1,13 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -module.exports = { - env: { - test: { - presets: ["@babel/preset-env", "@babel/preset-react"], - plugins: [["babel-plugin-transform-import-meta", { module: "ES6" }]], - }, - }, -} diff --git a/apps/doop/esbuild.config.js b/apps/doop/esbuild.config.js deleted file mode 100644 index 6ca970e4f..000000000 --- a/apps/doop/esbuild.config.js +++ /dev/null @@ -1,198 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -const esbuild = require("esbuild") -const fs = require("node:fs/promises") -const pkg = require("./package.json") -const postcss = require("postcss") -const sass = require("sass") -const { transform } = require("@svgr/core") -const url = require("postcss-url") -// this function generates app props based on package.json and propSecrets.json -const appProps = require("./helpers/appProps") - -if (!/.+\/.+\.js/.test(pkg.module)) throw new Error("module value is incorrect, use DIR/FILE.js like build/index.js") - -const isProduction = process.env.NODE_ENV === "production" -// If the jspm server fails and we cannot use external packages -// in our import map then IGNORE_EXTERNALS (global env variable) -// should be set to true -const IGNORE_EXTERNALS = process.env.IGNORE_EXTERNALS === "true" -// in dev environment we prefix output file with public -let outfile = `${isProduction ? "" : "public/"}${pkg.main || pkg.module}` -// get output from outputfile -let outdir = outfile.slice(0, outfile.lastIndexOf("/")) -const args = process.argv.slice(2) -const watch = args.indexOf("--watch") >= 0 -const serve = args.indexOf("--serve") >= 0 - -// helpers for console log -const green = "\x1b[32m%s\x1b[0m" -const yellow = "\x1b[33m%s\x1b[0m" -const clear = "\x1b" - -const build = async () => { - // delete build folder and re-create it as an empty folder - if (!isProduction) { - await fs.rm(outdir, { recursive: true, force: true }) - await fs.mkdir(outdir, { recursive: true }) - } - // build app - let ctx = await esbuild.context({ - bundle: true, - minify: isProduction, - // target: ["es2020"], - target: ["es2020"], //["chrome64", "firefox67", "safari11.1", "edge79"], - format: "esm", - platform: "browser", - // built-in loaders: js, jsx, ts, tsx, css, json, text, base64, dataurl, file, binary - loader: { ".js": "jsx" }, - sourcemap: !isProduction, - // here we exclude package from bundle which are defined in peerDependencies - // our importmap generator uses also the peerDependencies to create the importmap - // it means all packages defined in peerDependencies are in browser available via the importmap - external: isProduction && !IGNORE_EXTERNALS ? Object.keys(pkg.peerDependencies || {}) : [], - entryPoints: [pkg.source], - outdir, - // this step is important for performance reason. - // the main file (index.js) contains minimal code needed to - // load the app via dynamic import (splitting: true) - splitting: true, - // we suport only esm! - format: "esm", - plugins: [ - // minimal plugin to log the recompiling process. - { - name: "start/end", - setup(build) { - build.onStart(() => { - console.log(clear) - console.log(yellow, "Compiling...") - }) - build.onEnd(() => console.log(green, "Done!")) - }, - }, - - // this custom plugin rewrites SVG imports to - // dataurls, paths or react components based on the - // search param and size - { - name: "svg-loader", - setup(build) { - build.onLoad( - // consider only .svg files - { filter: /.\.(svg)$/, namespace: "file" }, - async (args) => { - let contents = await fs.readFile(args.path) - // built-in loaders: js, jsx, ts, tsx, css, json, text, base64, dataurl, file, binary - let loader = "text" - if (args.suffix === "?url") { - // as URL - const maxSize = 10240 // 10Kb - // use dataurl loader for small files and file loader for big files! - loader = contents.length <= maxSize ? "dataurl" : "file" - } else { - // as react component - // use react component loader (jsx) - loader = "jsx" - contents = await transform(contents, { - plugins: ["@svgr/plugin-jsx"], - }) - } - - return { contents, loader } - } - ) - }, - }, - - // this custom plugin rewrites image imports to - // dataurls or urls based on the size - { - name: "image-loader", - setup(build) { - build.onLoad( - // consider only .svg files - { filter: /.\.(png|jpg|jpeg|gif)$/, namespace: "file" }, - async (args) => { - let contents = await fs.readFile(args.path) - const maxSize = 10240 // 10Kb - // built-in loaders: js, jsx, ts, tsx, css, json, text, base64, dataurl, file, binary - // use dataurl loader for small files and file loader for big files! - loader = contents.length <= maxSize ? "dataurl" : "file" - - return { contents, loader } - } - ) - }, - }, - - // this custom plugin parses the style files - { - name: "parse-styles", - setup(build) { - build.onLoad( - // consider only .scss and .css files - { filter: /.\.(css|scss)$/, namespace: "file" }, - async (args) => { - let content - // handle scss, convert to css - if (args.path.endsWith(".scss")) { - const result = sass.renderSync({ file: args.path }) - content = result.css - } else { - // read file content - content = await fs.readFile(args.path) - } - - // postcss plugins - const plugins = [ - require("tailwindcss"), - require("autoprefixer"), - // rewrite urls inside css - url({ - url: "inline", - maxSize: 10, // use dataurls if files are smaller than 10k - fallback: "copy", // if files are bigger use copy method - assetsPath: "./build/assets", - useHash: true, - optimizeSvgEncode: true, - }), - ] - - const { css } = await postcss(plugins).process(content, { - from: args.path, - to: outdir, - }) - // built-in loaders: js, jsx, ts, tsx, css, json, text, base64, dataurl, file, binary - return { contents: css, loader: "text" } - } - ) - }, - }, - ], - }) - - // watch and serve - if (watch || serve) { - if (watch) await ctx.watch() - if (serve) { - // generate app props based on package.json and secretProps.json - await fs.writeFile(`./${outdir}/appProps.js`, `export default ${JSON.stringify(appProps())}`) - - let { host, port } = await ctx.serve({ - host: "0.0.0.0", - port: parseInt(process.env.APP_PORT || process.env.PORT || 3000), - servedir: "public", - }) - console.log("serve on", `${host}:${port}`) - } - } else { - await ctx.rebuild() - await ctx.dispose() - } -} - -build() diff --git a/apps/doop/helpers/appProps.js b/apps/doop/helpers/appProps.js deleted file mode 100644 index 0bd9cf318..000000000 --- a/apps/doop/helpers/appProps.js +++ /dev/null @@ -1,52 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -const path = require("path") -const fs = require("fs") - -module.exports = ({ appPath = "" } = {}) => { - const pkg = require(path.resolve(appPath, "package.json")) - let secrets - try { - if (fs.existsSync(path.resolve(appPath, "secretProps.js"))) { - secrets = require(path.resolve(appPath, "secretProps.js")) - } else { - secrets = require(path.resolve(appPath, "secretProps.json")) - } - } catch (e) { - console.error(e) - secrets = {} - } - - const pkgAppProps = pkg.appProps || {} - const pkgDependencyProps = pkg.appDependencies || {} - const appProps = {} - const dependencyProps = {} - for (let propName in pkgAppProps) { - // skip appDependencies - if (propName === "appDependencies") return - let value = pkgAppProps[propName] - if (typeof value !== "string") value = pkgAppProps[propName].value - appProps[propName] = value - } - - // map pkg app props with the secret props - for (let propName in secrets) { - if (propName === "appDependencies") continue - if (!appProps.hasOwnProperty(propName)) - throw Error(`Secret property ${propName} is not defined in package.json -> appProps`) - appProps[propName] = secrets[propName] - } - - if (secrets.appDependencies) { - for (let propName in secrets.appDependencies) { - if (!pkgDependencyProps.hasOwnProperty(propName)) - throw Error(`Secret property ${propName} is not defined in package.json -> appDependencies`) - dependencyProps[propName] = secrets.appDependencies[propName] - } - } - - return { appProps, dependencyProps } -} diff --git a/apps/doop/index.html b/apps/doop/index.html new file mode 100644 index 000000000..c70c09ec6 --- /dev/null +++ b/apps/doop/index.html @@ -0,0 +1,50 @@ + + + + + + + + + + + Doop + + + + + +
+ + diff --git a/apps/doop/jest.config.js b/apps/doop/jest.config.js deleted file mode 100644 index b1ae42bf1..000000000 --- a/apps/doop/jest.config.js +++ /dev/null @@ -1,19 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -module.exports = { - transform: { "\\.[jt]sx?$": "babel-jest" }, - testEnvironment: "jsdom", - setupFilesAfterEnv: ["/setupTests.js"], - transformIgnorePatterns: [], - moduleNameMapper: { - // Jest currently doesn't support resources with query parameters. - // Therefore we add the optional query parameter matcher at the end - // https://github.com/facebook/jest/issues/4181 - "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)(\\?.+)?$": - require.resolve("./__mocks__/fileMock"), - "\\.(css|less|scss)$": require.resolve("./__mocks__/styleMock"), - }, -} diff --git a/apps/doop/package.json b/apps/doop/package.json index 1cddd110e..22f321a65 100644 --- a/apps/doop/package.json +++ b/apps/doop/package.json @@ -7,87 +7,39 @@ "Arturo Reuschenbach Pucernau" ], "repository": "https://github.com/cloudoperators/juno/tree/main/apps/doop", - "source": "src/index.js", "module": "build/index.js", "license": "Apache-2.0", "private": true, "devDependencies": { - "@babel/core": "^7.20.2", - "@babel/preset-env": "^7.20.2", - "@babel/preset-react": "^7.18.6", "@cloudoperators/juno-config": "*", "@svgr/core": "^8.0.0", "@svgr/plugin-jsx": "^8.0.0", "@tanstack/react-query": "5.36.2", - "@testing-library/dom": "^10.0.0", - "@testing-library/jest-dom": "^6.0.0", - "@testing-library/react": "^16.0.0", - "@testing-library/user-event": "^14.4.3", - "assert": "^2.0.0", + "@testing-library/react": "^16.0.1", "autoprefixer": "^10.4.2", - "babel-jest": "^29.3.1", - "babel-plugin-transform-import-meta": "^2.2.0", - "esbuild": "^0.20.1", "interweave": "^13.1.0", - "jest": "^29.3.1", - "jest-environment-jsdom": "^29.3.1", "jsdoc": "^4.0.2", + "jsdom": "^25.0.1", "luxon": "^3.0.0", "postcss": "^8.4.31", - "postcss-url": "^10.1.3", "prop-types": "^15.8.1", "react-markdown": "^9.0.0", "react-test-renderer": "^18.2.0", "sass": "^1.77.5", - "shadow-dom-testing-library": "^1.7.1", + "shadow-dom-testing-library": "^1.11.3", "tailwindcss": "^3.3.1", + "vitest": "^2.1.1", "zustand": "4.5.5" }, "scripts": { - "test": "jest", - "dev": "NODE_ENV=development node esbuild.config.js --serve --watch", - "build": "NODE_ENV=production node esbuild.config.js", + "test": "vitest run", + "dev": "vite", + "build": "vite build", + "build:static": "vite build --mode static", + "serve": "vite preview", "lint": "eslint", "clean": "rm -rf build && rm -rf node_modules && rm -rf .turbo" }, - "appProps": { - "id": { - "value": "doop", - "type": "optional", - "description": "If you want to instantiate more than one app per page then use different ids" - }, - "theme": { - "value": "theme-dark", - "type": "optional", - "description": "Override the default theme. Possible values are theme-light or theme-dark (default)" - }, - "displayName": { - "value": "Global", - "description": "Name to use in UI", - "type": "optional" - }, - "apiEndpoint": { - "value": "", - "type": "required", - "description": "Endpoint URL of the Global Doop API" - }, - "embedded": { - "value": "true", - "type": "optional", - "description": "Set to true if app is to be embedded in another existing app or page. If set to true the app won't render a page header/footer and instead render only the content. The default value is false." - }, - "isMock": { - "value": "false", - "type": "optional", - "description": "Use mocked data (only in dev)" - }, - "showDebugSeverities": { - "value": "false", - "type": "optional", - "description": "Show debug severities in the log" - } - }, - "appPreview": true, "dependencies": { "@cloudoperators/juno-communicator": "*", "@cloudoperators/juno-messages-provider": "*", diff --git a/apps/doop/public/index.html b/apps/doop/public/index.html deleted file mode 100644 index 55af0cbed..000000000 --- a/apps/doop/public/index.html +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - - - - doop Dev - - - - - - - - -
- - diff --git a/apps/doop/secretProps.template.json b/apps/doop/secretProps.template.json deleted file mode 100644 index c463552e5..000000000 --- a/apps/doop/secretProps.template.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "theme": "theme-dark", - "endpoint": "https://endpoint/api/v1" -} \ No newline at end of file diff --git a/apps/doop/setupTests.js b/apps/doop/setupTests.js deleted file mode 100644 index bc62a34b6..000000000 --- a/apps/doop/setupTests.js +++ /dev/null @@ -1,10 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -// jest-dom adds custom jest matchers for asserting on DOM nodes. -// allows you to do things like: -// expect(element).toHaveTextContent(/react/i) -// learn more: https://github.com/testing-library/jest-dom -import "@testing-library/jest-dom" diff --git a/apps/doop/src/App.jsx b/apps/doop/src/App.jsx index fab8bb1fc..985736399 100644 --- a/apps/doop/src/App.jsx +++ b/apps/doop/src/App.jsx @@ -7,7 +7,7 @@ import React, { useEffect, useMemo, useLayoutEffect } from "react" import { AppShellProvider, ContentHeading } from "@cloudoperators/juno-ui-components" import AppContent from "./components/AppContent" -import styles from "./styles.scss" +import styles from "./styles.module.scss" import AuthProvider from "./components/AuthProvider" import { MessagesProvider } from "@cloudoperators/juno-messages-provider" import StoreProvider from "./components/StoreProvider" @@ -15,7 +15,7 @@ import AsyncWorker from "./components/AsyncWorker" import { AppShell } from "@cloudoperators/juno-ui-components" import { QueryClientProvider, QueryClient } from "@tanstack/react-query" import { fetchProxyInitDB } from "@cloudoperators/juno-utils" -import db from "../db.json" +import db from "./db.json" import { useGlobalsActions } from "./components/StoreProvider" const App = (props = {}) => { diff --git a/apps/doop/src/App.test.js b/apps/doop/src/App.test.js deleted file mode 100644 index 6f50c58fc..000000000 --- a/apps/doop/src/App.test.js +++ /dev/null @@ -1,25 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import React from "react" -import { render, act } from "@testing-library/react" -// support shadow dom queries -// https://reactjsexample.com/an-extension-of-dom-testing-library-to-provide-hooks-into-the-shadow-dom/ -import { screen } from "shadow-dom-testing-library" -import App from "./App" - -// Mock BroadcastChannel -globalThis.BroadcastChannel = jest.fn(() => ({ - postMessage: jest.fn(), - close: jest.fn(), -})) - -test("renders app", async () => { - await act(() => render()) - - let loginTitle = await screen.queryAllByShadowText(/DOOP/i) - - expect(loginTitle.length > 0).toBe(true) -}) diff --git a/apps/doop/src/App.test.jsx b/apps/doop/src/App.test.jsx new file mode 100644 index 000000000..e8821da79 --- /dev/null +++ b/apps/doop/src/App.test.jsx @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from "react" +import { render } from "@testing-library/react" +// support shadow dom queries +// https://reactjsexample.com/an-extension-of-dom-testing-library-to-provide-hooks-into-the-shadow-dom/ +import { screen } from "shadow-dom-testing-library" +import App from "./App" +import { describe } from "node:test" + +// Mock the styles +vi.mock("./styles.module.scss", () => ({ + default: new Proxy(new Object(), { + toString() { + return "/*TEST STYLES*/" + }, + }), +})) + +describe("App", () => { + it("should render the App component", () => { + render() + const loginTitle = screen.queryAllByShadowText(/DOOP/i) + expect(loginTitle.length > 0).toBe(true) + }) +}) diff --git a/apps/doop/src/assets/juno-danger.svg b/apps/doop/src/assets/juno-danger.svg deleted file mode 100644 index 495d4d1b9..000000000 --- a/apps/doop/src/assets/juno-danger.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - \ No newline at end of file diff --git a/apps/doop/src/assets/map.svg b/apps/doop/src/assets/map.svg deleted file mode 100644 index b4fdaa72d..000000000 --- a/apps/doop/src/assets/map.svg +++ /dev/null @@ -1,4421 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - EU-DE-1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - EU-DE-2 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - EU-NL-1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - AP-SA-1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - AP-SA-2 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - AP-AE-1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - AP-AU-1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - AP-CN-1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - AP-JP-2 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - AP-JP-1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NA-CA-1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NA-US-1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NA-US-3 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NA-US-2 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - LA-BR-1 - - - - - diff --git a/apps/doop/src/assets/rocket.gif b/apps/doop/src/assets/rocket.gif deleted file mode 100644 index 999d3d3bf..000000000 Binary files a/apps/doop/src/assets/rocket.gif and /dev/null differ diff --git a/apps/doop/db.json b/apps/doop/src/db.json similarity index 100% rename from apps/doop/db.json rename to apps/doop/src/db.json diff --git a/apps/doop/src/hooks/useCommunication.js b/apps/doop/src/hooks/useCommunication.js index 842ef8963..2e6162094 100644 --- a/apps/doop/src/hooks/useCommunication.js +++ b/apps/doop/src/hooks/useCommunication.js @@ -32,7 +32,7 @@ const useCommunication = () => { const unwatch = watch( "USER_ACTIVITY_UPDATE_DATA", (data) => { - console.log("got message USER_ACTIVITY_UPDATE_DATA: ", data) + console.debug("got message USER_ACTIVITY_UPDATE_DATA: ", data) setIsActive(data?.isActive) }, { debug: true, consumerID: "doop" } diff --git a/apps/doop/src/hooks/useUrlState.js b/apps/doop/src/hooks/useUrlState.js index 69f0aa8cf..ef339ead8 100644 --- a/apps/doop/src/hooks/useUrlState.js +++ b/apps/doop/src/hooks/useUrlState.js @@ -39,7 +39,7 @@ const useUrlState = (key) => { useEffect(() => { // don't read the url if we are already reading it or if we are not logged in if (isURLRead || !loggedIn) return - console.log(`DOOP: (${key || DEFAULT_KEY}) setting up state from url:`, urlStateManager.currentState()) + console.debug(`DOOP: (${key || DEFAULT_KEY}) setting up state from url:`, urlStateManager.currentState()) const searchTerm = urlStateManager.currentState()?.[SEARCH_TERM] const activeFilters = urlStateManager.currentState()?.[ACTIVE_FILTERS] diff --git a/apps/doop/public/favicon-16x16.png b/apps/doop/src/img/favicon-16x16.png similarity index 100% rename from apps/doop/public/favicon-16x16.png rename to apps/doop/src/img/favicon-16x16.png diff --git a/apps/doop/public/favicon-32x32.png b/apps/doop/src/img/favicon-32x32.png similarity index 100% rename from apps/doop/public/favicon-32x32.png rename to apps/doop/src/img/favicon-32x32.png diff --git a/apps/doop/public/favicon.ico b/apps/doop/src/img/favicon.ico similarity index 100% rename from apps/doop/public/favicon.ico rename to apps/doop/src/img/favicon.ico diff --git a/apps/doop/src/lib/store/createAuthDataSlice.js b/apps/doop/src/lib/store/createAuthDataSlice.jsx similarity index 100% rename from apps/doop/src/lib/store/createAuthDataSlice.js rename to apps/doop/src/lib/store/createAuthDataSlice.jsx diff --git a/apps/doop/src/lib/store/createDataSlice.js b/apps/doop/src/lib/store/createDataSlice.jsx similarity index 100% rename from apps/doop/src/lib/store/createDataSlice.js rename to apps/doop/src/lib/store/createDataSlice.jsx diff --git a/apps/doop/src/lib/store/createDataSlice.test.js b/apps/doop/src/lib/store/createDataSlice.test.jsx similarity index 99% rename from apps/doop/src/lib/store/createDataSlice.test.js rename to apps/doop/src/lib/store/createDataSlice.test.jsx index d2440e41e..021ec6c28 100644 --- a/apps/doop/src/lib/store/createDataSlice.test.js +++ b/apps/doop/src/lib/store/createDataSlice.test.jsx @@ -13,7 +13,7 @@ import StoreProvider, { useDataClusterIdentities, } from "../../components/StoreProvider.jsx" -import data from "../../../db.json" +import data from "../../db.json" describe("createDataSlice", () => { describe("setData", () => { diff --git a/apps/doop/src/lib/store/createFiltersSlice.js b/apps/doop/src/lib/store/createFiltersSlice.jsx similarity index 100% rename from apps/doop/src/lib/store/createFiltersSlice.js rename to apps/doop/src/lib/store/createFiltersSlice.jsx diff --git a/apps/doop/src/lib/store/createFiltersSlice.test.js b/apps/doop/src/lib/store/createFiltersSlice.test.jsx similarity index 99% rename from apps/doop/src/lib/store/createFiltersSlice.test.js rename to apps/doop/src/lib/store/createFiltersSlice.test.jsx index daed0a3a9..d7fe3763b 100644 --- a/apps/doop/src/lib/store/createFiltersSlice.test.js +++ b/apps/doop/src/lib/store/createFiltersSlice.test.jsx @@ -6,7 +6,8 @@ import * as React from "react" import { renderHook, act } from "@testing-library/react" import StoreProvider, { useDataActions, useFiltersActions, useDataFilteredItems } from "../../components/StoreProvider" -import data from "../../../db.json" + +import data from "../../db.json" describe("createFiltersSlice", () => { describe("set", () => { diff --git a/apps/doop/src/lib/store/createGlobalsSlice.js b/apps/doop/src/lib/store/createGlobalsSlice.jsx similarity index 100% rename from apps/doop/src/lib/store/createGlobalsSlice.js rename to apps/doop/src/lib/store/createGlobalsSlice.jsx diff --git a/apps/doop/src/lib/store/createUserActivitySlice.js b/apps/doop/src/lib/store/createUserActivitySlice.jsx similarity index 100% rename from apps/doop/src/lib/store/createUserActivitySlice.js rename to apps/doop/src/lib/store/createUserActivitySlice.jsx diff --git a/apps/doop/src/styles.scss b/apps/doop/src/styles.module.scss similarity index 77% rename from apps/doop/src/styles.scss rename to apps/doop/src/styles.module.scss index 7aee5657f..cebb6de1d 100644 --- a/apps/doop/src/styles.scss +++ b/apps/doop/src/styles.module.scss @@ -6,18 +6,6 @@ @tailwind components; @tailwind utilities; - -/* If necessary, app styles can be added below */ - -.svg-bg-test { - background: url('assets/juno-danger.svg') -} - -// .svg-bg-test-big-file { -// background: left 80px no-repeat url('assets/map.svg') -// } - - .info-box { h4 { font-size: 1.2rem; @@ -30,7 +18,6 @@ } } - // datagrid row hover style // REMOVE THIS ONCE DATAGRID COMPONENT SUPPORTS HOVER .violations-list .juno-datagrid-row:hover { @@ -44,4 +31,3 @@ @apply bg-theme-background-lvl-2; } } - diff --git a/apps/doop/turbo.json b/apps/doop/turbo.json index fcfeaa2b7..26f1bd3d4 100644 --- a/apps/doop/turbo.json +++ b/apps/doop/turbo.json @@ -16,6 +16,14 @@ "@cloudoperators/juno-messages-provider#build", "@cloudoperators/juno-communicator#build" ] + }, + "build:static": { + "dependsOn": [ + "@cloudoperators/juno-ui-components#build", + "@cloudoperators/juno-utils#build", + "@cloudoperators/juno-messages-provider#build", + "@cloudoperators/juno-communicator#build" + ] } } } diff --git a/apps/doop/vite.config.ts b/apps/doop/vite.config.ts new file mode 100644 index 000000000..74360cbf0 --- /dev/null +++ b/apps/doop/vite.config.ts @@ -0,0 +1,53 @@ +import { defineConfig } from "vite" +import react from "@vitejs/plugin-react" +import tailwindcss from "tailwindcss" +import autoprefixer from "autoprefixer" + +export default defineConfig(({ mode }) => { + const sharedConfig = { + root: "./", + + define: { + "process.env": {}, + }, + + plugins: [react()], + css: { + postcss: { + plugins: [tailwindcss, autoprefixer], + }, + }, + + server: { + host: "0.0.0.0", + port: parseInt(process.env.PORT || "3000"), + }, + } + + // with vite it is possible to have different configurations based on the mode + // we can use this to create a static build for previewing the app in github pages + // and also to create a docker image for the standalone app + if (mode === "static") { + return { + ...sharedConfig, + base: "./", // Relative Path in Generated index.html + build: { + outDir: "build", + }, + } + } + + // Default is a library + return { + ...sharedConfig, + build: { + outDir: "build", + + lib: { + entry: "src/index.js", + formats: ["es"], + fileName: (format) => `index.js`, + }, + }, + } +}) diff --git a/apps/doop/vitest.config.ts b/apps/doop/vitest.config.ts new file mode 100644 index 000000000..5fbf7036f --- /dev/null +++ b/apps/doop/vitest.config.ts @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { defineConfig } from "vitest/config" + +export default defineConfig({ + test: { + globals: true, + environment: "jsdom", + setupFiles: "./vitest.setup.ts", + watch: true, + }, +}) diff --git a/apps/doop/vitest.setup.ts b/apps/doop/vitest.setup.ts new file mode 100644 index 000000000..64c5b19e3 --- /dev/null +++ b/apps/doop/vitest.setup.ts @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { beforeAll } from "vitest" +import { expect } from "vitest" +import matchers from "@testing-library/jest-dom/matchers" + +expect.extend(matchers) + +beforeAll(() => { + // Mock global objects if necessary + global.window = window + global.document = window.document +}) diff --git a/package-lock.json b/package-lock.json index 2083fffe0..ec0d51324 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,35 +40,24 @@ "react-dom": "^18.2.0" }, "devDependencies": { - "@babel/core": "^7.20.2", - "@babel/preset-env": "^7.20.2", - "@babel/preset-react": "^7.18.6", "@cloudoperators/juno-config": "*", "@svgr/core": "^8.0.0", "@svgr/plugin-jsx": "^8.0.0", "@tanstack/react-query": "5.36.2", - "@testing-library/dom": "^10.0.0", - "@testing-library/jest-dom": "^6.0.0", - "@testing-library/react": "^16.0.0", - "@testing-library/user-event": "^14.4.3", - "assert": "^2.0.0", + "@testing-library/react": "^16.0.1", "autoprefixer": "^10.4.2", - "babel-jest": "^29.3.1", - "babel-plugin-transform-import-meta": "^2.2.0", - "esbuild": "^0.20.1", "interweave": "^13.1.0", - "jest": "^29.3.1", - "jest-environment-jsdom": "^29.3.1", "jsdoc": "^4.0.2", + "jsdom": "^25.0.1", "luxon": "^3.0.0", "postcss": "^8.4.31", - "postcss-url": "^10.1.3", "prop-types": "^15.8.1", "react-markdown": "^9.0.0", "react-test-renderer": "^18.2.0", "sass": "^1.77.5", - "shadow-dom-testing-library": "^1.7.1", + "shadow-dom-testing-library": "^1.11.3", "tailwindcss": "^3.3.1", + "vitest": "^2.1.1", "zustand": "4.5.5" } }, @@ -139,6 +128,7 @@ "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -153,48 +143,6 @@ "node": ">=18" } }, - "apps/doop/node_modules/@testing-library/jest-dom": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.5.0.tgz", - "integrity": "sha512-xGGHpBXYSHUUr6XsKBfs85TWlYKpTc37cSBBVrXcib2MkHLboWlkClhWF37JKlDb9KEq3dHs+f2xR7XJEWGBxA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@adobe/css-tools": "^4.4.0", - "aria-query": "^5.0.0", - "chalk": "^3.0.0", - "css.escape": "^1.5.1", - "dom-accessibility-api": "^0.6.3", - "lodash": "^4.17.21", - "redent": "^3.0.0" - }, - "engines": { - "node": ">=14", - "npm": ">=6", - "yarn": ">=1" - } - }, - "apps/doop/node_modules/@testing-library/jest-dom/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "apps/doop/node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", - "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", - "dev": true, - "license": "MIT" - }, "apps/doop/node_modules/@testing-library/react": { "version": "16.0.1", "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.0.1.tgz", @@ -223,6 +171,58 @@ } } }, + "apps/doop/node_modules/jsdom": { + "version": "25.0.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz", + "integrity": "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==", + "dev": true, + "dependencies": { + "cssstyle": "^4.1.0", + "data-urls": "^5.0.0", + "decimal.js": "^10.4.3", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.12", + "parse5": "^7.1.2", + "rrweb-cssom": "^0.7.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^5.0.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0", + "ws": "^8.18.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^2.11.2" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "apps/doop/node_modules/tough-cookie": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.0.0.tgz", + "integrity": "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==", + "dev": true, + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, "apps/example": { "name": "@cloudoperators/juno-app-example", "version": "1.0.10",