From bc48d19d8cdef69fcc52759aba2a3d9d8d07dbef Mon Sep 17 00:00:00 2001 From: Julius Volz Date: Wed, 28 Aug 2024 21:14:21 +0200 Subject: [PATCH] Add initial metrics explorer Signed-off-by: Julius Volz --- web/ui/mantine-ui/package.json | 3 + .../src/api/responseTypes/metadata.ts | 6 + .../src/pages/query/ExpressionInput.tsx | 42 +++- .../query/MetricsExplorer/MetricsExplorer.tsx | 176 +++++++++++++++ web/ui/package-lock.json | 212 ++++++++++++++---- 5 files changed, 393 insertions(+), 46 deletions(-) create mode 100644 web/ui/mantine-ui/src/api/responseTypes/metadata.ts create mode 100644 web/ui/mantine-ui/src/pages/query/MetricsExplorer/MetricsExplorer.tsx diff --git a/web/ui/mantine-ui/package.json b/web/ui/mantine-ui/package.json index 79228d89b1..a76f66270a 100644 --- a/web/ui/mantine-ui/package.json +++ b/web/ui/mantine-ui/package.json @@ -24,12 +24,14 @@ "@mantine/dates": "^7.11.2", "@mantine/hooks": "^7.11.2", "@mantine/notifications": "^7.11.2", + "@nexucis/fuzzy": "^0.5.1", "@nexucis/kvsearch": "^0.9.1", "@prometheus-io/codemirror-promql": "^0.50.0-rc.1", "@reduxjs/toolkit": "^2.2.1", "@tabler/icons-react": "^2.47.0", "@tanstack/react-query": "^5.22.2", "@types/lodash": "^4.17.7", + "@types/sanitize-html": "^2.13.0", "@uiw/react-codemirror": "^4.21.22", "dayjs": "^1.11.10", "lodash": "^4.17.21", @@ -38,6 +40,7 @@ "react-infinite-scroll-component": "^6.1.0", "react-redux": "^9.1.0", "react-router-dom": "^6.22.1", + "sanitize-html": "^2.13.0", "uplot": "^1.6.30", "uplot-react": "^1.2.2", "use-query-params": "^2.2.1" diff --git a/web/ui/mantine-ui/src/api/responseTypes/metadata.ts b/web/ui/mantine-ui/src/api/responseTypes/metadata.ts new file mode 100644 index 0000000000..1bd168455c --- /dev/null +++ b/web/ui/mantine-ui/src/api/responseTypes/metadata.ts @@ -0,0 +1,6 @@ +// Result type for /api/v1/alerts endpoint. +// See: https://prometheus.io/docs/prometheus/latest/querying/api/#querying-target-metadata +export type MetadataResult = Record< + string, + { type: string; help: string; unit: string }[] +>; diff --git a/web/ui/mantine-ui/src/pages/query/ExpressionInput.tsx b/web/ui/mantine-ui/src/pages/query/ExpressionInput.tsx index f7514e93f8..55f98941bb 100644 --- a/web/ui/mantine-ui/src/pages/query/ExpressionInput.tsx +++ b/web/ui/mantine-ui/src/pages/query/ExpressionInput.tsx @@ -5,6 +5,7 @@ import { InputBase, Loader, Menu, + Modal, rem, useComputedColorScheme, } from "@mantine/core"; @@ -13,11 +14,12 @@ import { PromQLExtension, newCompleteStrategy, } from "@prometheus-io/codemirror-promql"; -import { FC, useEffect, useMemo, useState } from "react"; +import { FC, Suspense, useEffect, useMemo, useRef, useState } from "react"; import CodeMirror, { EditorState, EditorView, Prec, + ReactCodeMirrorRef, highlightSpecialChars, keymap, placeholder, @@ -62,6 +64,8 @@ import { import { useAPIQuery } from "../../api/api"; import { notifications } from "@mantine/notifications"; import { useSettings } from "../../state/settingsSlice"; +import MetricsExplorer from "./MetricsExplorer/MetricsExplorer"; +import ErrorBoundary from "../../components/ErrorBoundary"; const promqlExtension = new PromQLExtension(); @@ -165,6 +169,11 @@ const ExpressionInput: FC = ({ } }, [formatResult, formatError]); + const cmRef = useRef(null); + + const [showMetricsExplorer, setShowMetricsExplorer] = useState(false); + + // TODO: Implement query history. // This is just a placeholder until query history is implemented, so disable the linter warning. const queryHistory = useMemo(() => [], []); @@ -214,6 +223,7 @@ const ExpressionInput: FC = ({ leftSection={ } + onClick={() => setShowMetricsExplorer(true)} > Explore metrics @@ -248,6 +258,7 @@ const ExpressionInput: FC = ({ value={expr} onChange={setExpr} autoFocus + ref={cmRef} extensions={[ baseTheme, highlightSpecialChars(), @@ -305,6 +316,35 @@ const ExpressionInput: FC = ({ + setShowMetricsExplorer(false)} + title="Explore metrics" + > + + }> + { + if (cmRef.current && cmRef.current.view) { + const view = cmRef.current.view; + view.dispatch( + view.state.update({ + changes: { + from: view.state.selection.ranges[0].from, + to: view.state.selection.ranges[0].to, + insert: text, + }, + }) + ); + } + }} + close={() => setShowMetricsExplorer(false)} + /> + + + ); }; diff --git a/web/ui/mantine-ui/src/pages/query/MetricsExplorer/MetricsExplorer.tsx b/web/ui/mantine-ui/src/pages/query/MetricsExplorer/MetricsExplorer.tsx new file mode 100644 index 0000000000..ab08e677f1 --- /dev/null +++ b/web/ui/mantine-ui/src/pages/query/MetricsExplorer/MetricsExplorer.tsx @@ -0,0 +1,176 @@ +import { FC, useState } from "react"; +import { useSuspenseAPIQuery } from "../../../api/api"; +import { MetadataResult } from "../../../api/responseTypes/metadata"; +import { + ActionIcon, + Alert, + Anchor, + Group, + Stack, + Table, + TextInput, +} from "@mantine/core"; +import React from "react"; +import { Fuzzy } from "@nexucis/fuzzy"; +import sanitizeHTML from "sanitize-html"; +import { IconCopy, IconTerminal, IconZoomIn } from "@tabler/icons-react"; + +const fuz = new Fuzzy({ + pre: '', + post: "", + shouldSort: true, +}); + +const sanitizeOpts = { + allowedTags: ["b"], + allowedAttributes: { b: ["style"] }, +}; + +type MetricsExplorerProps = { + metricNames: string[]; + insertText: (text: string) => void; + close: () => void; +}; + +const getSearchMatches = (input: string, expressions: string[]) => + fuz.filter(input.replace(/ /g, ""), expressions, { + pre: '', + post: "", + }); + +const MetricsExplorer: FC = ({ + metricNames, + insertText, + close, +}) => { + // const metricMeta = promAPI.useFetchAPI(`/api/v1/metadata`); + // Fetch the alerting rules data. + const { data } = useSuspenseAPIQuery({ + path: `/metadata`, + }); + const [selectedMetric, setSelectedMetric] = useState(null); + + const [filterText, setFilterText] = useState(""); + + const getMeta = (m: string) => + data.data[m.replace(/(_count|_sum|_bucket)$/, "")] || [ + { help: "unknown", type: "unknown", unit: "unknown" }, + ]; + + if (selectedMetric !== null) { + return ( + + TODO: The labels explorer for a metric still needs to be implemented. +
+
+ setSelectedMetric(null)}> + Back to metrics list + +
+ ); + } + + return ( + + ) => + setFilterText(e.target.value) + } + autoFocus + /> + + + + Metric + Type + Help + + + + {(filterText === "" + ? metricNames.map((m) => ({ original: m, rendered: m })) + : getSearchMatches(filterText, metricNames) + ).map((m) => ( + + + +
+ + { + setSelectedMetric(m.original); + }} + > + + + { + insertText(m.original); + close(); + }} + > + + + { + navigator.clipboard.writeText(m.original); + }} + > + + + + + + + {getMeta(m.original).map((meta, idx) => ( + + {meta.type} +
+
+ ))} +
+ + {getMeta(m.original).map((meta, idx) => ( + + {meta.help} +
+
+ ))} +
+ + ))} + +
+
+ ); +}; + +export default MetricsExplorer; diff --git a/web/ui/package-lock.json b/web/ui/package-lock.json index 6074f23085..7f5c0ae2a7 100644 --- a/web/ui/package-lock.json +++ b/web/ui/package-lock.json @@ -121,12 +121,14 @@ "@mantine/dates": "^7.11.2", "@mantine/hooks": "^7.11.2", "@mantine/notifications": "^7.11.2", + "@nexucis/fuzzy": "^0.5.1", "@nexucis/kvsearch": "^0.9.1", "@prometheus-io/codemirror-promql": "^0.50.0-rc.1", "@reduxjs/toolkit": "^2.2.1", "@tabler/icons-react": "^2.47.0", "@tanstack/react-query": "^5.22.2", "@types/lodash": "^4.17.7", + "@types/sanitize-html": "^2.13.0", "@uiw/react-codemirror": "^4.21.22", "dayjs": "^1.11.10", "lodash": "^4.17.21", @@ -135,6 +137,7 @@ "react-infinite-scroll-component": "^6.1.0", "react-redux": "^9.1.0", "react-router-dom": "^6.22.1", + "sanitize-html": "^2.13.0", "uplot": "^1.6.30", "uplot-react": "^1.2.2", "use-query-params": "^2.2.1" @@ -430,9 +433,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.2.tgz", - "integrity": "sha512-bYcppcpKBvX4znYaPEeFau03bp89ShqNMLs+rmdptMw+heSZh9+z84d2YG+K7cYLbWwzdjtDoW/uqZmPjulClQ==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz", + "integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==", "dev": true, "license": "MIT", "engines": { @@ -479,13 +482,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", - "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", + "version": "7.25.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.5.tgz", + "integrity": "sha512-abd43wyLfbWoxC6ahM8xTkqLpGB2iWBVyuKC9/srhFunCd1SDNrV1s72bBpK4hLj8KLzHBBcOblvLQZBNw9r3w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.25.0", + "@babel/types": "^7.25.4", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -731,13 +734,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.25.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.3.tgz", - "integrity": "sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.4.tgz", + "integrity": "sha512-nq+eWrOgdtu3jG5Os4TQP3x3cLA8hR8TvJNjD8vnPa20WGycimcparWnLK4jJhElTK6SDyuJo1weMKO/5LpmLA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.25.2" + "@babel/types": "^7.25.4" }, "bin": { "parser": "bin/babel-parser.js" @@ -996,17 +999,17 @@ } }, "node_modules/@babel/traverse": { - "version": "7.25.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.3.tgz", - "integrity": "sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.4.tgz", + "integrity": "sha512-VJ4XsrD+nOvlXyLzmLzUs/0qjFS4sK30te5yEFlvbbUNEgKaVb2BHZUpAL+ttLPQAHNrsI3zZisbfha5Cvr8vg==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.0", - "@babel/parser": "^7.25.3", + "@babel/generator": "^7.25.4", + "@babel/parser": "^7.25.4", "@babel/template": "^7.25.0", - "@babel/types": "^7.25.2", + "@babel/types": "^7.25.4", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1024,9 +1027,9 @@ } }, "node_modules/@babel/types": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.2.tgz", - "integrity": "sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.4.tgz", + "integrity": "sha512-zQ1ijeeCXVEh+aNL0RlmkPkG8HUiDcU2pzQQFjtbntgAczRASFzj4H+6+bV+dy1ntKR14I/DypeuRG1uma98iQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2189,6 +2192,12 @@ "@lezer/common": "^1.0.0" } }, + "node_modules/@nexucis/fuzzy": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@nexucis/fuzzy/-/fuzzy-0.5.1.tgz", + "integrity": "sha512-+swL9itqBe1rx5Pr8ihaIS7STOeFI90HpOFF8y/3wo3ryTxKs0Hf4xc+wiA4yi9nrY4wo3VC8HJOxNiekSBE4w==", + "license": "MIT" + }, "node_modules/@nexucis/kvsearch": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/@nexucis/kvsearch/-/kvsearch-0.9.1.tgz", @@ -2198,12 +2207,6 @@ "@nexucis/fuzzy": "^0.5.1" } }, - "node_modules/@nexucis/kvsearch/node_modules/@nexucis/fuzzy": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@nexucis/fuzzy/-/fuzzy-0.5.1.tgz", - "integrity": "sha512-+swL9itqBe1rx5Pr8ihaIS7STOeFI90HpOFF8y/3wo3ryTxKs0Hf4xc+wiA4yi9nrY4wo3VC8HJOxNiekSBE4w==", - "license": "MIT" - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2726,6 +2729,15 @@ "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", "dev": true }, + "node_modules/@types/sanitize-html": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-2.13.0.tgz", + "integrity": "sha512-X31WxbvW9TjIhZZNyNBZ/p5ax4ti7qsNDBDEnH4zAgmEh35YnFD1UiS6z9Cd34kKm0LslFW0KPmTQzu/oGtsqQ==", + "license": "MIT", + "dependencies": { + "htmlparser2": "^8.0.0" + } + }, "node_modules/@types/scheduler": { "version": "0.16.8", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", @@ -3338,9 +3350,9 @@ } }, "node_modules/browserslist": { - "version": "4.23.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.2.tgz", - "integrity": "sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", + "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", "dev": true, "funding": [ { @@ -3358,9 +3370,9 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001640", - "electron-to-chromium": "^1.4.820", - "node-releases": "^2.0.14", + "caniuse-lite": "^1.0.30001646", + "electron-to-chromium": "^1.5.4", + "node-releases": "^2.0.18", "update-browserslist-db": "^1.1.0" }, "bin": { @@ -3440,9 +3452,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001645", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001645.tgz", - "integrity": "sha512-GFtY2+qt91kzyMk6j48dJcwJVq5uTkk71XxE3RtScx7XWRLsO7bU44LOFkOZYR8w9YMS0UhPSYpN/6rAMImmLw==", + "version": "1.0.30001653", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001653.tgz", + "integrity": "sha512-XGWQVB8wFQ2+9NZwZ10GxTYC5hk0Fa+q8cSkr0tgvMhYhMHP/QC+WTgrePMDBWiWc/pV+1ik82Al20XOK25Gcw==", "dev": true, "funding": [ { @@ -3765,7 +3777,6 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3827,6 +3838,61 @@ "csstype": "^3.0.2" } }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, "node_modules/ejs": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", @@ -3870,6 +3936,18 @@ "dev": true, "peer": true }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -3931,7 +4009,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, "engines": { "node": ">=10" }, @@ -4608,6 +4685,25 @@ "dev": true, "peer": true }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -4826,6 +4922,15 @@ "node": ">=8" } }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -5820,7 +5925,6 @@ "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "dev": true, "funding": [ { "type": "github", @@ -5883,10 +5987,11 @@ "peer": true }, "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", - "dev": true + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "dev": true, + "license": "MIT" }, "node_modules/normalize-path": { "version": "3.0.0", @@ -6032,6 +6137,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse-srcset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz", + "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==", + "license": "MIT" + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -6078,7 +6189,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", - "dev": true, "license": "ISC" }, "node_modules/picomatch": { @@ -6176,7 +6286,6 @@ "version": "8.4.35", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", - "dev": true, "funding": [ { "type": "opencollective", @@ -6861,6 +6970,20 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/sanitize-html": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.13.0.tgz", + "integrity": "sha512-Xff91Z+4Mz5QiNSLdLWwjgBDm5b1RU6xBT0+12rapjiaR7SwfRdjw8f+6Rir2MXKLrDicRFHdb51hGOAxmsUIA==", + "license": "MIT", + "dependencies": { + "deepmerge": "^4.2.2", + "escape-string-regexp": "^4.0.0", + "htmlparser2": "^8.0.0", + "is-plain-object": "^5.0.0", + "parse-srcset": "^1.0.2", + "postcss": "^8.3.11" + } + }, "node_modules/sass": { "version": "1.69.5", "resolved": "https://registry.npmjs.org/sass/-/sass-1.69.5.tgz", @@ -6967,7 +7090,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "dev": true, "engines": { "node": ">=0.10.0" }