diff --git a/.gitignore b/.gitignore index 555677f2..3b1fba9d 100644 --- a/.gitignore +++ b/.gitignore @@ -26,4 +26,8 @@ docker-compose.yaml *.png *.jpg +# Certificates for localhost SSL +*.pem + + .vite diff --git a/README.md b/README.md index a1baf7cd..4bd186eb 100644 --- a/README.md +++ b/README.md @@ -2,16 +2,20 @@ Macrostrat's map interface is web portal to a geologic model of the Earth's crust. -Version 5 of the application transitions to using [Vite](https://vitejs.dev/) for bundling and [Vike](https://vike.dev/) for server-side rendering. We are working on updating this version for performance and stability. +Version 5 of the application transitions to using [Vite](https://vitejs.dev/) for bundling and [Vike](https://vike.dev/) +for server-side rendering. We are working on updating this version for performance and stability. ## Installation for local development 1. Clone the repository 2. Pull down submodules (`git submodule update --init --recursive`) -3. Create and populate a `.env` file with the appropriate environment variables (See [`.env.example`](https://github.com/UW-Macrostrat/web/blob/main/.env.example) for more information.) -4. Verify that you have access to recent versions of Node.js and the Yarn package manager ( `node >= 16.0.0` and `yarn >= 4.0.0`; run `node -v` and `yarn -v` to check) +3. Create and populate a `.env` file with the appropriate environment variables (See [ + `.env.example`](https://github.com/UW-Macrostrat/web/blob/main/.env.example) for more information.) +4. Verify that you have access to recent versions of Node.js and the Yarn package manager ( `node >= 16.0.0` and + `yarn >= 4.0.0`; run `node -v` and `yarn -v` to check) 5. Run `yarn install` to update packages -6. Start the live-reloading development server with `yarn run dev`. The server will be available at `http://localhost:3000` by default. +6. Start the live-reloading development server with `yarn run dev`. The server will be available at + `http://localhost:3000` by default. ## Contributing @@ -53,4 +57,27 @@ To deploy to kubernetes there is two steps. 2. Update the deployment in Kubernetes - You do this by updating the image tag here to whatever you tagged above: https://github.com/UW-Macrostrat/tiger-macrostrat-config/blob/main/manifests/development/web/deployment-patch.yaml + You do this by updating the image tag here to whatever you tagged + above: https://github.com/UW-Macrostrat/tiger-macrostrat-config/blob/main/manifests/development/web/deployment-patch.yaml + +## Local development with SSL + +You can generate self-signed SSL certificates for local development using the following command: + +```bash +openssl req -x509 -newkey rsa:2048 -nodes -sha256 -subj '/CN=localhost' \ + -keyout private-key.pem -out certificate.pem +``` + +To run the development server with SSL, you can use the following command: + +## Testing authentication on localhost + +If you are developing locally and need to test authentication, you can +use a plugin like **CookieSync** to automatically pull cookies from the production or development +site into your local environment. This will allow you to use the same session +information locally. The cookie that must be copied is called `access_token`. + +We will eventually build in a shim authentication service to allow for easier +local development. + diff --git a/deps/web-components b/deps/web-components index c0748972..36482135 160000 --- a/deps/web-components +++ b/deps/web-components @@ -1 +1 @@ -Subproject commit c07489727c56873e1460ae2e4c26909694707a1c +Subproject commit 36482135555ede3551c758354a52a11b8e4c47bc diff --git a/package.json b/package.json index a87d4734..6c868535 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,8 @@ "@typescript-eslint/eslint-plugin": "^6.3.0", "@typescript-eslint/parser": "^6.3.0", "@yarnpkg/sdks": "^3.1.0", + "chalk": "^5.3.0", + "http-proxy-middleware": "^3.0.3", "prettier": "^2.7.1", "react-arborist": "^3.4.0", "react-text-annotate-blend": "^1.2.0", diff --git a/packages/settings/index.ts b/packages/settings/index.ts index e5e29a65..d0c79280 100644 --- a/packages/settings/index.ts +++ b/packages/settings/index.ts @@ -28,7 +28,7 @@ export const apiV2Prefix = getRuntimeConfig( export const ingestPrefix = getRuntimeConfig( "MACROSTRAT_INGEST_API", - apiDomain + "/api/ingest" + apiDomain + "/api/v3" ); export const cdrPrefix = getRuntimeConfig("CDR_API_URL"); diff --git a/pages/integrations/xdd/+Page.mdx b/pages/integrations/xdd/+Page.mdx index a105e91f..4f71cafd 100644 --- a/pages/integrations/xdd/+Page.mdx +++ b/pages/integrations/xdd/+Page.mdx @@ -7,6 +7,7 @@ for discovering data from the scientific literature. - Stratigraphic units linked to papers in Rockd and Macrostrat - Extracting [structured data](/integrations/xdd/extractions) from papers +- [Data types](/integrations/xdd/types) for feedback - [Model runs](/integrations/xdd/runs) - [Feedback](/integrations/xdd/feedback) on extractions - Training machine learning models for [Map legend affinity](/dev/legend-affinity) diff --git a/pages/integrations/xdd/feedback/@sourceTextID/+Page.client.ts b/pages/integrations/xdd/feedback/@sourceTextID/+Page.client.ts index 18f301f5..38bd4375 100644 --- a/pages/integrations/xdd/feedback/@sourceTextID/+Page.client.ts +++ b/pages/integrations/xdd/feedback/@sourceTextID/+Page.client.ts @@ -9,7 +9,9 @@ import { usePostgresQuery, } from "../../extractions/lib/data-service"; import { FeedbackComponent } from "./lib"; -import { OverlaysProvider } from "@blueprintjs/core"; +import { Intent, NonIdealState, OverlaysProvider } from "@blueprintjs/core"; +import { ErrorBoundary, Pagination } from "@macrostrat/ui-components"; +import { useState } from "react"; /** * Get a single text window for feedback purposes @@ -42,25 +44,57 @@ function ExtractionIndex() { return h("div", "Loading..."); } + console.log(data); + return h( - "div.feedback-windows", - data.map((d) => { - console.log(data); - const window = enhanceData(d, models, entityTypes); - const { entities = [], paragraph_text, model } = window; - //h("h1", paper.citation?.title ?? "Model extractions"), - return h(FeedbackComponent, { - entities, - text: paragraph_text, - model, - entityTypes, - sourceTextID: window.source_text, - runID: window.model_run, - }); - }) + ErrorBoundary, + h(MultiFeedbackInterface, { data, models, entityTypes }) ); } +function MultiFeedbackInterface({ data, models, entityTypes }) { + const [ix, setIX] = useState(0); + const currentData = data[ix]; + const count = data.length; + + return h("div.feedback", [ + h.if(data.length > 1)([ + h(NonIdealState, { + icon: "warning-sign", + title: "Multiple model runs for feedback", + description: `Showing entities from ${ + ix + 1 + } of ${count} model runs. Merging several runs is not yet supported.`, + }), + h(Pagination, { + count, + page: ix, + setPage: setIX, + nextDisabled: ix >= count - 1, + }), + ]), + h(FeedbackInterface, { + data: currentData, + models, + entityTypes, + }), + ]); +} + +function FeedbackInterface({ data, models, entityTypes }) { + const window = enhanceData(data, models, entityTypes); + const { entities = [], paragraph_text, model } = window; + return h(FeedbackComponent, { + entities, + text: paragraph_text, + model, + entityTypes, + sourceTextID: window.source_text, + runID: window.model_run, + }); +} + + // function FeedbackDevTool() { // const entities = useStore((state) => state.entities); // if (entities == null) diff --git a/pages/integrations/xdd/feedback/@sourceTextID/+config.ts b/pages/integrations/xdd/feedback/@sourceTextID/+config.ts new file mode 100644 index 00000000..0e07b269 --- /dev/null +++ b/pages/integrations/xdd/feedback/@sourceTextID/+config.ts @@ -0,0 +1,11 @@ +export default { + meta: { + Page: { + env: { + client: true, + server: false, + }, + }, + }, + clientRouting: false, +}; diff --git a/pages/integrations/xdd/feedback/@sourceTextID/lib/edit-state.ts b/pages/integrations/xdd/feedback/@sourceTextID/lib/edit-state.ts index a37e8cbc..52513ea3 100644 --- a/pages/integrations/xdd/feedback/@sourceTextID/lib/edit-state.ts +++ b/pages/integrations/xdd/feedback/@sourceTextID/lib/edit-state.ts @@ -315,7 +315,6 @@ function prepareGraphForServer(tree: TreeData[]): GraphData { }; nodeMap.set(node.id, node); - nodes.push(nodeData); if (node.children) { diff --git a/pages/integrations/xdd/feedback/@sourceTextID/lib/index.ts b/pages/integrations/xdd/feedback/@sourceTextID/lib/index.ts index 14ada7e1..9400b11a 100644 --- a/pages/integrations/xdd/feedback/@sourceTextID/lib/index.ts +++ b/pages/integrations/xdd/feedback/@sourceTextID/lib/index.ts @@ -11,10 +11,6 @@ import { ButtonGroup, Card } from "@blueprintjs/core"; import { OmniboxSelector } from "./type-selector"; import { CancelButton, SaveButton } from "@macrostrat/ui-components"; -export interface FeedbackComponentProps { - // Add props here -} - function setsAreTheSame(a: Set, b: Set) { if (a.size !== b.size) return false; for (const item of a) { diff --git a/pages/integrations/xdd/types/+Page.client.ts b/pages/integrations/xdd/types/+Page.client.ts new file mode 100644 index 00000000..d32ff03b --- /dev/null +++ b/pages/integrations/xdd/types/+Page.client.ts @@ -0,0 +1,55 @@ +import { FullscreenPage } from "~/layouts"; +import h from "@macrostrat/hyper"; +import { PageBreadcrumbs } from "~/components"; +import { PostgRESTTableView } from "~/components/legend-table"; + +import { + ColorCell, + EditableTextArea, + ColorPicker, +} from "@macrostrat/data-sheet2"; +import { asChromaColor } from "@macrostrat/color-utils"; +import { LoginButton } from "#/maps/ingestion/components/navbar"; +import { AuthStatus } from "@macrostrat/auth-components"; + +const colorField = { + name: "Color", + key: "color", + required: false, + transform: (d) => d, + dataEditor: ColorPicker, + valueRenderer: (d) => { + let color = asChromaColor(d); + return color?.name() ?? ""; + }, + // Maybe this should be changed to CellProps? + cellComponent: ColorCell, +}; + +export function Page() { + return h(FullscreenPage, { className: "main" }, [ + h(PageBreadcrumbs), + h("div.header", [h("h1", "Entity types"), h("div.spacer"), h(AuthStatus)]), + h(PostgRESTTableView, { + table: "kg_entity_type", + editable: true, + columnOptions: { + omitColumns: ["id"], + overrides: { + color: colorField, + name: { + name: "Name", + style: { fontFamily: "monospace" }, + }, + description: { + name: "Description", + editable: true, + //inlineEditor: true, + dataEditor: EditableTextArea, + }, + }, + }, + order: { key: "id", ascending: true }, + }), + ]); +} diff --git a/server/index.ts b/server/index.ts index f1f15fd1..dd531957 100644 --- a/server/index.ts +++ b/server/index.ts @@ -8,6 +8,7 @@ import { createMiddleware } from "@universal-middleware/express"; import { createMacrostratQlrAPI } from "@macrostrat-web/qgis-integration"; import express from "express"; import sirv from "sirv"; +import chalk from "chalk"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); @@ -65,6 +66,7 @@ async function startServer() { const app = express(); app.use(compression()); + // if (isProduction) { app.use(sirv(`${root}/dist/client`)); @@ -73,6 +75,34 @@ async function startServer() { // Ideally we'd be able to remove this fix. app.use("/cesium", sirv(`${root}/dist/cesium`)); } else { + // For localhost development: create a proxy to the API server to enable + // API requests with the appropriate authorization cookies or headers. + const proxyDomain = process.env.MACROSTRAT_API_PROXY_DOMAIN; + if (proxyDomain) { + const target = proxyDomain + "/api"; + console.log("Proxying API requests to", target); + const { createProxyMiddleware } = await import("http-proxy-middleware"); + app.use( + "/api", + createProxyMiddleware({ + target, + changeOrigin: true, + on: { + proxyReq: (proxyReq) => { + const parsedPath = new URL(proxyReq.path, proxyDomain); + console.log( + chalk.bold.green(`[${proxyReq.method}]`), + chalk.dim(proxyDomain) + + parsedPath.pathname + + chalk.dim(parsedPath.hash) + + chalk.dim(parsedPath.search) + ); + }, + }, + }) + ); + } + // Instantiate Vite's development server and integrate its middleware to our server. // ⚠️ We should instantiate it *only* in development. (It isn't needed in production // and would unnecessarily bloat our server in production.) diff --git a/server/vike-handler.ts b/server/vike-handler.ts index e4e913e9..bf50f31a 100644 --- a/server/vike-handler.ts +++ b/server/vike-handler.ts @@ -34,25 +34,21 @@ export async function vikeHandler< } async function getUserFromCookie(cookies: Record) { - const isProduction = process.env.NODE_ENV === "production"; // Pull out the authorization cookie and decrypt it let user: any = undefined; try { - const authHeader = cookies?.Authorization; + const authHeader = cookies?.["access_token"]; const secret = new TextEncoder().encode(process.env.SECRET_KEY); const jwt = authHeader.substring(7, authHeader.length); // We probably don't need to verify the JWT on each request. // OR we can pass the user obju user = (await jose.jwtVerify(jwt, secret)).payload; + console.log("User", user); } catch (e) { // I don't care if it fails, it just means the user isn't logged in console.log("Anonymous user"); } - if (!isProduction && process.env.DEV_ENABLE_AUTH !== "true") { - // Haha wow this is sketchy...this needs to be stopped. - user = { groups: [1] }; - } return user; } diff --git a/src/_providers/auth.ts b/src/_providers/auth.ts index ff5125e2..073a2949 100644 --- a/src/_providers/auth.ts +++ b/src/_providers/auth.ts @@ -21,6 +21,7 @@ async function authTransformer( case "login": // Assemble the return URL on click based on the current page const return_url = window.location.origin + window.location.pathname; + console.log("Returning to", return_url); window.location.href = `${ingestPrefix}/security/login?return_url=${return_url}`; case "logout": // Delete the token from the session diff --git a/src/components/legend-table/data-loaders.ts b/src/components/legend-table/data-loaders.ts index f91d98d3..795d293b 100644 --- a/src/components/legend-table/data-loaders.ts +++ b/src/components/legend-table/data-loaders.ts @@ -3,7 +3,8 @@ import { useAsyncEffect } from "@macrostrat/ui-components"; import { debounce } from "underscore"; import { postgrest } from "~/_providers"; -import { useReducer } from "react"; +import { useCallback, useReducer } from "react"; +import update, { Spec } from "immutability-helper"; interface ChunkIndex { startRow: number; @@ -16,13 +17,15 @@ interface LazyLoaderState { loading: boolean; error: Error | null; visibleRegion: RowRegion; + initialized: boolean; } type LazyLoaderAction = | { type: "start-loading" } | { type: "loaded"; data: T[]; offset: number; totalSize: number } | { type: "error"; error: Error } - | { type: "set-visible"; region: RowRegion }; + | { type: "set-visible"; region: RowRegion } + | { type: "update-data"; changes: Spec }; function adjustArraySize(arr: T[], newSize: number) { if (newSize == null || arr.length === newSize) { @@ -44,12 +47,19 @@ function lazyLoadingReducer( return { ...state, loading: true, + initialized: true, }; case "set-visible": return { ...state, visibleRegion: action.region, }; + case "update-data": + return { + ...state, + loading: false, + data: update(state.data, action.changes), + }; case "loaded": let data = adjustArraySize(state.data, action.totalSize); data = [ @@ -145,7 +155,7 @@ function buildQuery(endpoint: string, config: QueryConfig) { return query; } -function loadMoreData( +function _loadMoreData( endpoint: string, config: QueryConfig & { chunkSize: number }, state: LazyLoaderState, @@ -153,11 +163,11 @@ function loadMoreData( ) { const rowIndex = indexOfFirstNullInRegion(state.data, state.visibleRegion); if (state.loading || rowIndex == null) { - return; + if (state.initialized) { + return; + } } - dispatch({ type: "start-loading" }); - const { chunkSize = 100, ...rest } = config; const sortKey = config.order?.key ?? "id"; @@ -169,7 +179,7 @@ function loadMoreData( }; // Allows random seeking - const isInitialQuery = state.data.length === 0; + const isInitialQuery = !state.initialized; if (isInitialQuery) { cfg.count = "exact"; } @@ -182,9 +192,11 @@ function loadMoreData( } } + dispatch({ type: "start-loading" }); + const query = buildQuery(endpoint, cfg); - const res = query.then((res) => { + query.then((res) => { const { data, count } = res; dispatch({ type: "loaded", @@ -195,6 +207,9 @@ function loadMoreData( }); } +// Ensure only one data load is in progress at a time +const loadMoreData = debounce(_loadMoreData, 100); + type LazyLoaderOptions = Omit & { chunkSize?: number; sortKey?: string; @@ -208,7 +223,8 @@ export function usePostgRESTLazyLoader( data: [], loading: false, error: null, - visibleRegion: { rowIndexStart: 0, rowIndexEnd: 1 }, + visibleRegion: { rowIndexStart: 0, rowIndexEnd: 0 }, + initialized: false, }; const [state, dispatch] = useReducer(lazyLoadingReducer, initialState); @@ -216,19 +232,27 @@ export function usePostgRESTLazyLoader( useAsyncEffect(async () => { loadMoreData(endpoint, config, state, dispatch); - }, [data, state.visibleRegion]); - - const onScroll = debounce((visibleCells: RowRegion) => { - dispatch({ - type: "set-visible", - region: visibleCells, - }); - }, 500); + }, [ + data, + state.visibleRegion.rowIndexStart, + state.visibleRegion.rowIndexEnd, + ]); + + const onScroll = useCallback( + debounce((visibleCells: RowRegion) => { + dispatch({ + type: "set-visible", + region: visibleCells, + }); + }, 500), + [dispatch] + ); return { data, loading, onScroll, + dispatch, }; } diff --git a/src/components/legend-table/index.ts b/src/components/legend-table/index.ts index ccaa4370..d3d2ddf4 100644 --- a/src/components/legend-table/index.ts +++ b/src/components/legend-table/index.ts @@ -1,37 +1,126 @@ -import { Tag } from "@blueprintjs/core"; +import { OverlayToaster, Tag } from "@blueprintjs/core"; import hyper from "@macrostrat/hyper"; import styles from "./main.module.sass"; -import DataSheet, { ColorCell } from "@macrostrat/data-sheet2"; +import DataSheet, { ColorCell, getRowsToDelete } from "@macrostrat/data-sheet2"; import { LithologyTag } from "~/components"; import { usePostgRESTLazyLoader } from "~/components/legend-table/data-loaders"; import { HotkeysProvider } from "@blueprintjs/core"; import { Spinner } from "@blueprintjs/core"; export * from "./data-loaders"; +import { postgrest } from "~/_providers"; +import { useCallback, useRef } from "react"; +import { ErrorBoundary } from "@macrostrat/ui-components"; +import { Spec } from "immutability-helper"; const h = hyper.styled(styles); -export function PostgRESTTableView({ table, columnOptions, order, columns }) { - const { data, onScroll } = usePostgRESTLazyLoader(table, { +interface PostgRESTTableViewProps { + table: string; + columnOptions?: any; + order?: any; + columns?: string; + editable?: boolean; +} + +export function PostgRESTTableView(props: PostgRESTTableViewProps) { + return h(ErrorBoundary, h(_PostgRESTTableView, props)); +} + +const successResponses = [200, 201]; + +export function _PostgRESTTableView({ + table, + columnOptions, + order, + columns, + editable = false, +}: PostgRESTTableViewProps) { + const { data, onScroll, dispatch } = usePostgRESTLazyLoader(table, { order, columns, }); + const toasterRef = useRef(null); + + const finishResponse = useCallback( + (promisedResult, changes) => { + promisedResult + .then((res) => { + if (!successResponses.includes(res.status)) { + // Throw an error with the status code + let err = new Error(res.error.message); + err["status"] = res.status; + throw err; + } + + // Merge new data with old data + dispatch({ type: "update-data", changes }); + }) + .catch((err: Error) => { + const status = err["status"]; + toasterRef.current?.show({ + message: h([ + h.if(status != null)([h("code", status), " "]), + err.message, + ]), + intent: "danger", + }); + }); + }, + [dispatch] + ); + if (data == null) { return h(Spinner); } - return h( - HotkeysProvider, + return h(HotkeysProvider, [ + h(OverlayToaster, { usePortal: false, ref: toasterRef }), h(DataSheet, { data, - columnSpecOptions: columnOptions, - editable: false, - onVisibleCellsChange(visibleCells) { - onScroll(visibleCells); + columnSpecOptions: columnOptions ?? {}, + editable, + onVisibleCellsChange: onScroll, + onDeleteRows(selection) { + if (!editable) return; + + const rowIndices = getRowsToDelete(selection); + + console.log(rowIndices); + + const ids = rowIndices.map((i) => data[i].id); + + dispatch({ type: "start-loading" }); + + const query = postgrest.from(table).delete().in("id", ids); + + finishResponse(query, { $delete: Array.from(rowIndices.keys()) }); }, - }) - ); + onSaveData(updates, data) { + if (!editable) return; + + // Augment updates with primary key + + let changes: Spec = {}; + let updateRows: any[] = []; + for (let [key, update] of Object.entries(updates)) { + const value = { ...data[key], ...update }; + updateRows.push(value); + changes[key] = { $set: value }; + } + + dispatch({ type: "start-loading" }); + + // Save data + const query = postgrest + .from(table) + .upsert(updateRows, { defaultToNull: false }); + + finishResponse(query, changes); + }, + }), + ]); } export function LongTextViewer({ value, onChange }) { @@ -49,7 +138,6 @@ export function lithologyRenderer(value) { } export function ExpandedLithologies({ value, onChange }) { - console.log(value); if (value == null) return h("div.basis-panel", "No lithologies"); return h("div.basis-panel", [ h("table", [ diff --git a/src/components/legend-table/main.module.sass b/src/components/legend-table/main.module.sass index 0463ac21..d818b3a2 100644 --- a/src/components/legend-table/main.module.sass +++ b/src/components/legend-table/main.module.sass @@ -10,8 +10,10 @@ .basis-panel :global(.bp5-tag) margin: 0 0.5em 0.5em 0 + td vertical-align: top + th text-align: left font-weight: 400 @@ -41,4 +43,9 @@ .liths :global(.bp5-tag) padding: 0px 4px - min-height: 16px \ No newline at end of file + min-height: 16px + +.data-sheet-outer + position: relative + flex: 1 + display: flex diff --git a/vite.config.ts b/vite.config.ts index 0b2043bb..2ddf4f11 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -31,6 +31,8 @@ for (const [key, value] of Object.entries(gitEnv)) { process.env["VITE_" + key] = value; } +// Rewrite API URLs to localhost + const cesiumRoot = require.resolve("cesium").replace("/index.cjs", "/Build"); const cesiumBuildPath = path.resolve(cesiumRoot, "Cesium"); diff --git a/yarn.lock b/yarn.lock index d32b1483..7f0f9072 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6616,6 +6616,7 @@ __metadata: immutability-helper: "npm:^3.1.1" react: "npm:^17.0.2||^18" react-color: "npm:^2.19.3" + react-colorful: "npm:^5.6.1" typescript: "npm:^5.6.2" languageName: unknown linkType: soft @@ -7052,6 +7053,7 @@ __metadata: "@vitejs/plugin-react": "npm:^4.0.4" "@yarnpkg/sdks": "npm:^3.1.0" axios: "npm:^0.25.0" + chalk: "npm:^5.3.0" chroma-js: "npm:^3.0.0" classnames: "npm:^2.2.6" compression: "npm:^1.7.4" @@ -7072,6 +7074,7 @@ __metadata: fuse.js: "npm:^7.0.0" hex-to-css-filter: "npm:^5.4.0" history: "npm:^5.3.0" + http-proxy-middleware: "npm:^3.0.3" immutability-helper: "npm:^3.1.1" jose: "npm:^5.1.2" mapbox-gl: "npm:^2.15.0" @@ -11530,6 +11533,15 @@ __metadata: languageName: node linkType: hard +"@types/http-proxy@npm:^1.17.15": + version: 1.17.15 + resolution: "@types/http-proxy@npm:1.17.15" + dependencies: + "@types/node": "npm:*" + checksum: 10/fa86d5397c021f6c824d1143a206009bfb64ff703da32fb30f6176c603daf6c24ce3a28daf26b3945c94dd10f9d76f07ea7a6a2c3e9b710e00ff42da32e08dea + languageName: node + linkType: hard + "@types/http-proxy@npm:^1.17.8": version: 1.17.14 resolution: "@types/http-proxy@npm:1.17.14" @@ -14369,6 +14381,15 @@ __metadata: languageName: node linkType: hard +"braces@npm:^3.0.3": + version: 3.0.3 + resolution: "braces@npm:3.0.3" + dependencies: + fill-range: "npm:^7.1.1" + checksum: 10/fad11a0d4697a27162840b02b1fad249c1683cbc510cd5bf1a471f2f8085c046d41094308c577a50a03a579dd99d5a6b3724c4b5e8b14df2c4443844cfcda2c6 + languageName: node + linkType: hard + "brfs@npm:^1.3.0": version: 1.6.1 resolution: "brfs@npm:1.6.1" @@ -14899,6 +14920,13 @@ __metadata: languageName: node linkType: hard +"chalk@npm:^5.3.0": + version: 5.3.0 + resolution: "chalk@npm:5.3.0" + checksum: 10/6373caaab21bd64c405bfc4bd9672b145647fc9482657b5ea1d549b3b2765054e9d3d928870cdf764fb4aad67555f5061538ff247b8310f110c5c888d92397ea + languageName: node + linkType: hard + "chalk@npm:~0.4.0": version: 0.4.0 resolution: "chalk@npm:0.4.0" @@ -17055,6 +17083,18 @@ __metadata: languageName: node linkType: hard +"debug@npm:*, debug@npm:^4.3.6": + version: 4.3.7 + resolution: "debug@npm:4.3.7" + dependencies: + ms: "npm:^2.1.3" + peerDependenciesMeta: + supports-color: + optional: true + checksum: 10/71168908b9a78227ab29d5d25fe03c5867750e31ce24bf2c44a86efc5af041758bb56569b0a3d48a9b5344c00a24a777e6f4100ed6dfd9534a42c1dde285125a + languageName: node + linkType: hard + "debug@npm:2.6.9": version: 2.6.9 resolution: "debug@npm:2.6.9" @@ -19468,6 +19508,15 @@ __metadata: languageName: node linkType: hard +"fill-range@npm:^7.1.1": + version: 7.1.1 + resolution: "fill-range@npm:7.1.1" + dependencies: + to-regex-range: "npm:^5.0.1" + checksum: 10/a7095cb39e5bc32fada2aa7c7249d3f6b01bd1ce461a61b0adabacccabd9198500c6fb1f68a7c851a657e273fce2233ba869638897f3d7ed2e87a2d89b4436ea + languageName: node + linkType: hard + "filter-obj@npm:^1.1.0": version: 1.1.0 resolution: "filter-obj@npm:1.1.0" @@ -21255,6 +21304,20 @@ __metadata: languageName: node linkType: hard +"http-proxy-middleware@npm:^3.0.3": + version: 3.0.3 + resolution: "http-proxy-middleware@npm:3.0.3" + dependencies: + "@types/http-proxy": "npm:^1.17.15" + debug: "npm:^4.3.6" + http-proxy: "npm:^1.18.1" + is-glob: "npm:^4.0.3" + is-plain-object: "npm:^5.0.0" + micromatch: "npm:^4.0.8" + checksum: 10/32f58c29288ca63e109909fb998bd0f6f50eb15a98dec9487eac07dfc4f09d8507dbfa00b44442d868bafa904bd633c8bbd55686bb13b4d4af4f5c5b3bbca430 + languageName: node + linkType: hard + "http-proxy@npm:^1.18.1": version: 1.18.1 resolution: "http-proxy@npm:1.18.1" @@ -25444,6 +25507,16 @@ __metadata: languageName: node linkType: hard +"micromatch@npm:^4.0.8": + version: 4.0.8 + resolution: "micromatch@npm:4.0.8" + dependencies: + braces: "npm:^3.0.3" + picomatch: "npm:^2.3.1" + checksum: 10/6bf2a01672e7965eb9941d1f02044fad2bd12486b5553dc1116ff24c09a8723157601dc992e74c911d896175918448762df3b3fd0a6b61037dd1a9766ddfbf58 + languageName: node + linkType: hard + "mime-db@npm:1.52.0, mime-db@npm:>= 1.43.0 < 2": version: 1.52.0 resolution: "mime-db@npm:1.52.0" @@ -25859,7 +25932,7 @@ __metadata: languageName: node linkType: hard -"ms@npm:2.1.3, ms@npm:^2.0.0, ms@npm:^2.1.1": +"ms@npm:2.1.3, ms@npm:^2.0.0, ms@npm:^2.1.1, ms@npm:^2.1.3": version: 2.1.3 resolution: "ms@npm:2.1.3" checksum: 10/aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d @@ -29318,7 +29391,7 @@ __metadata: languageName: node linkType: hard -"react-colorful@npm:^5.1.2": +"react-colorful@npm:^5.1.2, react-colorful@npm:^5.6.1": version: 5.6.1 resolution: "react-colorful@npm:5.6.1" peerDependencies: