From 27b0467f622486ec697a62b71e3383c301ea7fee Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Tue, 16 Jun 2026 20:09:11 +0200 Subject: [PATCH 01/31] chore: migrate rust/face-recognition to icp-cli Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/face-recognition.yml | 28 + .../rust-face-recognition-example.yaml | 46 - rust/face-recognition/.cargo/config.toml | 5 - rust/face-recognition/.gitignore | 8 +- rust/face-recognition/Cargo.toml | 2 +- rust/face-recognition/Makefile | 34 + rust/face-recognition/README.md | 104 +- .../{src => }/backend/Cargo.toml | 3 +- .../{src => }/backend/backend.did | 0 .../{src => }/backend/src/benchmarking.rs | 0 .../{src => }/backend/src/lib.rs | 0 .../{src => }/backend/src/onnx.rs | 0 .../{src => }/backend/src/storage.rs | 0 rust/face-recognition/build.sh | 8 - rust/face-recognition/dfx.json | 37 - .../frontend/assets => frontend}/default.png | Bin .../frontend/assets => frontend}/favicon.ico | Bin .../{src/frontend/src => frontend}/index.html | 3 +- .../frontend/assets => frontend}/loader.svg | 0 .../assets => frontend}/logo_small.png | Bin .../assets => frontend}/logo_transparent.png | Bin .../frontend/assets => frontend}/main.css | 0 rust/face-recognition/frontend/package.json | 17 + rust/face-recognition/frontend/src/actor.js | 26 + .../{src => }/frontend/src/index.js | 9 +- rust/face-recognition/frontend/vite.config.js | 56 + rust/face-recognition/icp.yaml | 28 + rust/face-recognition/package-lock.json | 4607 ----------------- rust/face-recognition/package.json | 48 +- rust/face-recognition/rust-toolchain.toml | 2 +- .../src/backend/assets/image.png | Bin 61197 -> 0 bytes .../src/declarations/backend/backend.did | 51 - .../src/declarations/backend/backend.did.d.ts | 39 - .../src/declarations/backend/backend.did.js | 31 - .../src/declarations/backend/index.d.ts | 50 - .../src/declarations/backend/index.js | 42 - .../src/frontend/assets/.ic-assets.json5 | 56 - .../src/frontend/assets/logo_transparent.png | Bin 58988 -> 0 bytes .../src/frontend/src/.ic-assets.json5 | 56 - .../upload-models-to-canister.sh | 6 +- rust/face-recognition/webpack.config.js | 96 - 41 files changed, 264 insertions(+), 5234 deletions(-) create mode 100644 .github/workflows/face-recognition.yml delete mode 100644 .github/workflows/rust-face-recognition-example.yaml delete mode 100644 rust/face-recognition/.cargo/config.toml create mode 100644 rust/face-recognition/Makefile rename rust/face-recognition/{src => }/backend/Cargo.toml (99%) rename rust/face-recognition/{src => }/backend/backend.did (100%) rename rust/face-recognition/{src => }/backend/src/benchmarking.rs (100%) rename rust/face-recognition/{src => }/backend/src/lib.rs (100%) rename rust/face-recognition/{src => }/backend/src/onnx.rs (100%) rename rust/face-recognition/{src => }/backend/src/storage.rs (100%) delete mode 100755 rust/face-recognition/build.sh delete mode 100644 rust/face-recognition/dfx.json rename rust/face-recognition/{src/frontend/assets => frontend}/default.png (100%) rename rust/face-recognition/{src/frontend/assets => frontend}/favicon.ico (100%) rename rust/face-recognition/{src/frontend/src => frontend}/index.html (95%) rename rust/face-recognition/{src/frontend/assets => frontend}/loader.svg (100%) rename rust/face-recognition/{src/frontend/assets => frontend}/logo_small.png (100%) rename rust/face-recognition/{src/backend/assets => frontend}/logo_transparent.png (100%) rename rust/face-recognition/{src/frontend/assets => frontend}/main.css (100%) create mode 100644 rust/face-recognition/frontend/package.json create mode 100644 rust/face-recognition/frontend/src/actor.js rename rust/face-recognition/{src => }/frontend/src/index.js (97%) create mode 100644 rust/face-recognition/frontend/vite.config.js create mode 100644 rust/face-recognition/icp.yaml delete mode 100644 rust/face-recognition/package-lock.json delete mode 100644 rust/face-recognition/src/backend/assets/image.png delete mode 100644 rust/face-recognition/src/declarations/backend/backend.did delete mode 100644 rust/face-recognition/src/declarations/backend/backend.did.d.ts delete mode 100644 rust/face-recognition/src/declarations/backend/backend.did.js delete mode 100644 rust/face-recognition/src/declarations/backend/index.d.ts delete mode 100644 rust/face-recognition/src/declarations/backend/index.js delete mode 100644 rust/face-recognition/src/frontend/assets/.ic-assets.json5 delete mode 100644 rust/face-recognition/src/frontend/assets/logo_transparent.png delete mode 100644 rust/face-recognition/src/frontend/src/.ic-assets.json5 delete mode 100644 rust/face-recognition/webpack.config.js diff --git a/.github/workflows/face-recognition.yml b/.github/workflows/face-recognition.yml new file mode 100644 index 0000000000..c8aa87e9bb --- /dev/null +++ b/.github/workflows/face-recognition.yml @@ -0,0 +1,28 @@ +name: face-recognition + +on: + push: + branches: [master] + pull_request: + paths: + - rust/face-recognition/** + - .github/workflows/face-recognition.yml + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + rust-face-recognition: + runs-on: ubuntu-24.04 + container: ghcr.io/dfinity/icp-dev-env-rust:1.0.0 + env: + ICP_CLI_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + - name: Deploy and test + working-directory: rust/face-recognition + run: | + icp network start -d + icp deploy + make test diff --git a/.github/workflows/rust-face-recognition-example.yaml b/.github/workflows/rust-face-recognition-example.yaml deleted file mode 100644 index 6c13bbc757..0000000000 --- a/.github/workflows/rust-face-recognition-example.yaml +++ /dev/null @@ -1,46 +0,0 @@ -name: rust-face-recognition -on: - push: - branches: - - master - pull_request: - paths: - - rust/face-recognition/** - - .github/workflows/provision-darwin.sh - - .github/workflows/provision-linux.sh - - .github/workflows/rust-face-recognition-example.yaml - - .ic-commit -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true -jobs: - rust-face-recognition-example-darwin: - runs-on: macos-15 - steps: - - uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1.2.0 - - name: Provision Darwin - run: bash .github/workflows/provision-darwin.sh - - name: Remove networks.json - run: rm -f ~/.config/dfx/networks.json - - name: Rust Face Recognition Darwin - run: | - dfx start --background - pushd rust/face-recognition - npm install - dfx deploy --yes - popd - rust-face-recognition-example-linux: - runs-on: ubuntu-24.04 - steps: - - uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1.2.0 - - name: Provision Linux - run: bash .github/workflows/provision-linux.sh - - name: Remove networks.json - run: rm -f ~/.config/dfx/networks.json - - name: Rust Face Recognition Linux - run: | - dfx start --background - pushd rust/face-recognition - npm install - dfx deploy --yes - popd diff --git a/rust/face-recognition/.cargo/config.toml b/rust/face-recognition/.cargo/config.toml deleted file mode 100644 index 067cfd27dc..0000000000 --- a/rust/face-recognition/.cargo/config.toml +++ /dev/null @@ -1,5 +0,0 @@ -[build] -target = ["wasm32-wasi"] - -[target.wasm32-wasi] -rustflags = ["-Ctarget-feature=+simd128"] diff --git a/rust/face-recognition/.gitignore b/rust/face-recognition/.gitignore index c3ed99af22..139cd79f69 100644 --- a/rust/face-recognition/.gitignore +++ b/rust/face-recognition/.gitignore @@ -1,4 +1,3 @@ -.dfx/ build/ node_modules/ dist/ @@ -8,6 +7,7 @@ _MACOSX target/ *.old.did .idea -src/backend/assets/version-RFB-320.onnx -src/backend/assets/facerec.onnx -.env +backend/assets/version-RFB-320.onnx +backend/assets/facerec.onnx +frontend/src/bindings/ +.icp/cache/ diff --git a/rust/face-recognition/Cargo.toml b/rust/face-recognition/Cargo.toml index 4c16df46ee..d1e49e317a 100644 --- a/rust/face-recognition/Cargo.toml +++ b/rust/face-recognition/Cargo.toml @@ -1,3 +1,3 @@ [workspace] -members = ["src/backend"] +members = ["backend"] resolver = "2" diff --git a/rust/face-recognition/Makefile b/rust/face-recognition/Makefile new file mode 100644 index 0000000000..bcc9cb7caf --- /dev/null +++ b/rust/face-recognition/Makefile @@ -0,0 +1,34 @@ +.PHONY: test upload-models + +test: + @echo "=== Test 1: clear_face_detection_model_bytes returns unit ===" + @result=$$(icp canister call backend clear_face_detection_model_bytes '()') && \ + echo "$$result" && \ + echo "PASS" || (echo "FAIL" && exit 1) + + @echo "=== Test 2: clear_face_recognition_model_bytes returns unit ===" + @result=$$(icp canister call backend clear_face_recognition_model_bytes '()') && \ + echo "$$result" && \ + echo "PASS" || (echo "FAIL" && exit 1) + + @echo "=== Test 3: append_face_detection_model_bytes accepts bytes ===" + @result=$$(icp canister call backend append_face_detection_model_bytes '(blob "\00\01\02")') && \ + echo "$$result" && \ + echo "PASS" || (echo "FAIL" && exit 1) + + @echo "=== Test 4: run_detection returns Err when model is not set up ===" + @result=$$(icp canister call --query backend run_detection '()') && \ + echo "$$result" && \ + echo "$$result" | grep -q 'Err' && \ + echo "PASS" || (echo "FAIL" && exit 1) + +# Upload ONNX model files to the canister incrementally. +# Requires: ic-file-uploader (cargo install ic-file-uploader) +# First download the models: ./download-face-detection-model.sh +# and obtain face-recognition.onnx (see README), then run: make upload-models +upload-models: + icp canister call backend clear_face_detection_model_bytes '()' + icp canister call backend clear_face_recognition_model_bytes '()' + ic-file-uploader backend append_face_detection_model_bytes version-RFB-320.onnx + ic-file-uploader backend append_face_recognition_model_bytes face-recognition.onnx + icp canister call backend setup_models '()' diff --git a/rust/face-recognition/README.md b/rust/face-recognition/README.md index bf6ea0903c..b8df1551f7 100644 --- a/rust/face-recognition/README.md +++ b/rust/face-recognition/README.md @@ -1,96 +1,94 @@ # ICP face recognition -This is an ICP smart contract runs face detection and face recognition of user's photo that can be uploaded either from a camera or a local file. +This is an ICP smart contract that runs face detection and face recognition on user photos that can be uploaded either from a camera or a local file. The smart contract consists of two canisters: -- The backend canister embeds the [the Tract ONNX inference engine](https://github.com/sonos/tract) with two ONNX models. One model is used to detect a face in the photo and return its bounding box. Another model is used for computing face embeddings. -- The frontend canister contains the Web assets such as HTML, JS, CSS that are served to the browser. +- The **backend canister** embeds the [Tract ONNX inference engine](https://github.com/sonos/tract) with two ONNX models. One model is used to detect a face in the photo and return its bounding box. Another model is used for computing face embeddings. +- The **frontend canister** contains the Web assets such as HTML, JS, and CSS that are served to the browser. -# Models +## Models The smart contract uses two models: one for detecting the face and another for recognizing the face. -## Face detection +Since the models are large, they cannot be embedded into the Wasm binary of the smart contract. Instead they must be uploaded separately after deployment. -A face detection model finds the bounding box of a face in the image. -You can download [Ultraface](https://github.com/onnx/models/tree/main/validated/vision/body_analysis/ultraface) - ultra-lightweight face detection model - [[here](https://github.com/onnx/models/blob/bec48b6a70e5e9042c0badbaafefe4454e072d08/validated/vision/body_analysis/ultraface/models/version-RFB-320.onnx)]. +### Face detection -Alternatively, you can run +A face detection model finds the bounding box of a face in the image. +You can download [Ultraface](https://github.com/onnx/models/tree/main/validated/vision/body_analysis/ultraface) — ultra-lightweight face detection model — by running: -``` +```bash ./download-face-detection-model.sh ``` -## Face recognition +### Face recognition A face recognition model computes a vector embedding of an image with a face. You can obtain a pretrained model from [facenet-pytorch](https://github.com/timesler/facenet-pytorch) as follows. -- #### Step 1: Install `python` and `pip`: https://packaging.python.org/en/latest/tutorials/installing-packages/. +1. Install `python` and `pip`: https://packaging.python.org/en/latest/tutorials/installing-packages/ -- #### Step 2: Install `facenet-pytorch` and `torch`: +2. Install `facenet-pytorch`, `torch`, and `onnx`: -``` -pip install facenet-pytorch -pip install torch -pip install onnx -``` + ```bash + pip install facenet-pytorch + pip install torch + pip install onnx + ``` -- #### Step 3: Export ONNX model. Start a python shell and run the following commands or create a python file and run it: +3. Export the ONNX model. Start a Python shell and run: -``` -import torch -import facenet_pytorch -resnet = facenet_pytorch.InceptionResnetV1(pretrained='vggface2').eval() -input = torch.randn(1, 3, 160, 160) -torch.onnx.export(resnet, input, "face-recognition.onnx", verbose=False, opset_version=11) -``` + ```python + import torch + import facenet_pytorch + resnet = facenet_pytorch.InceptionResnetV1(pretrained='vggface2').eval() + input = torch.randn(1, 3, 160, 160) + torch.onnx.export(resnet, input, "face-recognition.onnx", verbose=False, opset_version=11) + ``` -- #### Step 4: This should produce `face-recognition.onnx`. Copy the file to the root of this repository. + This produces `face-recognition.onnx`. Copy the file to the root of this repository. -## Prerequisites +## Build and deploy from the command line -- [x] Install the [IC - SDK](https://internetcomputer.org/docs/current/developer-docs/getting-started/install). For local testing, `dfx >= 0.22.0` is required. -- [x] Clone the example dapp project: `git clone https://github.com/dfinity/examples` -- [x] Install `wasi2ic`: Follow the steps in https://github.com/wasm-forge/wasi2ic and make sure that `wasi2ic` binary is in your `$PATH`. -- [x] Install `wasm-opt`: `cargo install wasm-opt` +### Prerequisites -## Build the application +- Node.js +- icp-cli: `npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm` -``` -dfx start --background -dfx deploy +### Install + +```bash +git clone https://github.com/dfinity/examples +cd examples/rust/face-recognition ``` -If the deployment is successful, the it will show the `frontend` URL. -Open that URL in browser to interact with the smart contract. +### Deploy and test -## Chunk uploading of models +```bash +icp network start -d +icp deploy +make test +icp network stop +``` -Since the models are large, they cannot be embedded into the Wasm binary of the smart contract. -Instead they should be uploaded separately. +Run `npm run dev` from the `frontend/` directory for hot reload during frontend development. -[DecideAI](https://decideai.xyz/) implemented a tool for incremental uploading of models: https://github.com/modclub-app/ic-file-uploader/tree/main. +### Upload the ONNX models -You can install the tool with +After deploying, upload the ONNX models to the canister using [ic-file-uploader](https://github.com/modclub-app/ic-file-uploader/tree/main): -``` +```bash cargo install ic-file-uploader +make upload-models ``` -Afterwards, execute the `upload-models-to-canister.sh` script, which runs the following commands: - -``` -dfx canister call backend clear_face_detection_model_bytes -dfx canister call backend clear_face_recognition_model_bytes -ic-file-uploader backend append_face_detection_model_bytes version-RFB-320.onnx -ic-file-uploader backend append_face_recognition_model_bytes face-recognition.onnx -dfx canister call backend setup_models -``` +Once the models are uploaded, open the frontend URL in your browser to interact with the smart contract. ## Credits Thanks to [DecideAI](https://decideai.xyz/) for discussions and providing [ic-file-uploader](https://github.com/modclub-app/ic-file-uploader/tree/main). +## Security considerations and best practices + +See the [ICP security best practices](https://docs.internetcomputer.org/guides/security/overview). diff --git a/rust/face-recognition/src/backend/Cargo.toml b/rust/face-recognition/backend/Cargo.toml similarity index 99% rename from rust/face-recognition/src/backend/Cargo.toml rename to rust/face-recognition/backend/Cargo.toml index bf9676ca06..216f90ad70 100644 --- a/rust/face-recognition/src/backend/Cargo.toml +++ b/rust/face-recognition/backend/Cargo.toml @@ -1,7 +1,7 @@ [package] -edition = "2021" name = "backend" version = "1.1.0" +edition = "2021" [lib] crate-type = ["cdylib"] @@ -19,4 +19,3 @@ prost = "0.11.0" prost-types = "0.11.0" serde = { version = "1.0", features = ["derive"] } tract-onnx = { git = "https://github.com/sonos/tract", rev = "2a2914ac29390cc08963301c9f3d437b52dd321a" } - diff --git a/rust/face-recognition/src/backend/backend.did b/rust/face-recognition/backend/backend.did similarity index 100% rename from rust/face-recognition/src/backend/backend.did rename to rust/face-recognition/backend/backend.did diff --git a/rust/face-recognition/src/backend/src/benchmarking.rs b/rust/face-recognition/backend/src/benchmarking.rs similarity index 100% rename from rust/face-recognition/src/backend/src/benchmarking.rs rename to rust/face-recognition/backend/src/benchmarking.rs diff --git a/rust/face-recognition/src/backend/src/lib.rs b/rust/face-recognition/backend/src/lib.rs similarity index 100% rename from rust/face-recognition/src/backend/src/lib.rs rename to rust/face-recognition/backend/src/lib.rs diff --git a/rust/face-recognition/src/backend/src/onnx.rs b/rust/face-recognition/backend/src/onnx.rs similarity index 100% rename from rust/face-recognition/src/backend/src/onnx.rs rename to rust/face-recognition/backend/src/onnx.rs diff --git a/rust/face-recognition/src/backend/src/storage.rs b/rust/face-recognition/backend/src/storage.rs similarity index 100% rename from rust/face-recognition/src/backend/src/storage.rs rename to rust/face-recognition/backend/src/storage.rs diff --git a/rust/face-recognition/build.sh b/rust/face-recognition/build.sh deleted file mode 100755 index 1578306038..0000000000 --- a/rust/face-recognition/build.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -set -ex - -export RUSTFLAGS=$RUSTFLAGS' -C target-feature=+simd128' -cargo build --release --target=wasm32-wasi -wasi2ic ./target/wasm32-wasi/release/backend.wasm ./target/wasm32-wasi/release/backend-ic.wasm -wasm-opt -Os --enable-simd --enable-bulk-memory -o ./target/wasm32-wasi/release/backend-ic.wasm \ - ./target/wasm32-wasi/release/backend-ic.wasm diff --git a/rust/face-recognition/dfx.json b/rust/face-recognition/dfx.json deleted file mode 100644 index 59d45e4c17..0000000000 --- a/rust/face-recognition/dfx.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "canisters": { - "backend": { - "metadata": [ - { - "name": "candid:service" - } - ], - "candid": "src/backend/backend.did", - "package": "backend", - "type": "custom", - "wasm": "target/wasm32-wasi/release/backend-ic.wasm", - "build": [ "bash build.sh" ] - }, - "frontend": { - "dependencies": [ - "backend" - ], - "frontend": { - "entrypoint": "src/frontend/src/index.html" - }, - "source": [ - "src/frontend/assets", - "dist/frontend/" - ], - "type": "assets" - } - }, - "defaults": { - "build": { - "args": "", - "packtool": "" - } - }, - "output_env_file": ".env", - "version": 1 -} diff --git a/rust/face-recognition/src/frontend/assets/default.png b/rust/face-recognition/frontend/default.png similarity index 100% rename from rust/face-recognition/src/frontend/assets/default.png rename to rust/face-recognition/frontend/default.png diff --git a/rust/face-recognition/src/frontend/assets/favicon.ico b/rust/face-recognition/frontend/favicon.ico similarity index 100% rename from rust/face-recognition/src/frontend/assets/favicon.ico rename to rust/face-recognition/frontend/favicon.ico diff --git a/rust/face-recognition/src/frontend/src/index.html b/rust/face-recognition/frontend/index.html similarity index 95% rename from rust/face-recognition/src/frontend/src/index.html rename to rust/face-recognition/frontend/index.html index 3c11059ebc..eee938a0e5 100644 --- a/rust/face-recognition/src/frontend/src/index.html +++ b/rust/face-recognition/frontend/index.html @@ -9,6 +9,7 @@ + @@ -41,4 +42,4 @@

Onchain ICP face recogniti - \ No newline at end of file + diff --git a/rust/face-recognition/src/frontend/assets/loader.svg b/rust/face-recognition/frontend/loader.svg similarity index 100% rename from rust/face-recognition/src/frontend/assets/loader.svg rename to rust/face-recognition/frontend/loader.svg diff --git a/rust/face-recognition/src/frontend/assets/logo_small.png b/rust/face-recognition/frontend/logo_small.png similarity index 100% rename from rust/face-recognition/src/frontend/assets/logo_small.png rename to rust/face-recognition/frontend/logo_small.png diff --git a/rust/face-recognition/src/backend/assets/logo_transparent.png b/rust/face-recognition/frontend/logo_transparent.png similarity index 100% rename from rust/face-recognition/src/backend/assets/logo_transparent.png rename to rust/face-recognition/frontend/logo_transparent.png diff --git a/rust/face-recognition/src/frontend/assets/main.css b/rust/face-recognition/frontend/main.css similarity index 100% rename from rust/face-recognition/src/frontend/assets/main.css rename to rust/face-recognition/frontend/main.css diff --git a/rust/face-recognition/frontend/package.json b/rust/face-recognition/frontend/package.json new file mode 100644 index 0000000000..d0cc35cdb0 --- /dev/null +++ b/rust/face-recognition/frontend/package.json @@ -0,0 +1,17 @@ +{ + "name": "frontend", + "private": true, + "type": "module", + "scripts": { + "prebuild": "npm i --include=dev", + "build": "vite build", + "dev": "vite" + }, + "dependencies": { + "@icp-sdk/core": "~5.0.0" + }, + "devDependencies": { + "@icp-sdk/bindgen": "~0.2.2", + "vite": "5.4.11" + } +} diff --git a/rust/face-recognition/frontend/src/actor.js b/rust/face-recognition/frontend/src/actor.js new file mode 100644 index 0000000000..8d5275670b --- /dev/null +++ b/rust/face-recognition/frontend/src/actor.js @@ -0,0 +1,26 @@ +import { safeGetCanisterEnv } from "@icp-sdk/core/agent/canister-env"; +import { createActor } from "./bindings/backend"; + +// The ic_env cookie is set by the asset canister (SDK >=0.30.2) on all HTML +// responses. It contains the replica root key and any PUBLIC_* canister +// environment variables. In dev mode the vite dev server sets the same cookie +// via Set-Cookie header (see vite.config.js). +const canisterEnv = safeGetCanisterEnv(); + +// Resolve canister ID: cookie (icp-cli + dev server) -> env var (dfx build-time) +const canisterId = + canisterEnv?.["PUBLIC_CANISTER_ID:backend"] ?? + process.env.CANISTER_ID_BACKEND; + +if (!canisterId) { + throw new Error( + "Canister ID for 'backend' not found. Run 'icp deploy' first." + ); +} + +export const backend = createActor(canisterId, { + agentOptions: { + host: window.location.origin, + rootKey: canisterEnv?.IC_ROOT_KEY, + }, +}); diff --git a/rust/face-recognition/src/frontend/src/index.js b/rust/face-recognition/frontend/src/index.js similarity index 97% rename from rust/face-recognition/src/frontend/src/index.js rename to rust/face-recognition/frontend/src/index.js index abb452dec8..d9c6c94b38 100644 --- a/rust/face-recognition/src/frontend/src/index.js +++ b/rust/face-recognition/frontend/src/index.js @@ -1,4 +1,4 @@ -import { backend } from "../../declarations/backend"; +import { backend } from "./actor.js"; window.onload = async () => { elem("recognize").onclick = recognize; @@ -72,6 +72,7 @@ async function capture_image() { } let bytes = await serialize(resized); + const video = elem("video"); if (video.srcObject) { video.srcObject.getTracks().forEach((track) => track.stop()); } @@ -116,7 +117,7 @@ async function render(scaling, box) { // This function performs the following steps: // 1. Capture the image from the camera stream (or from the local file). // 2. Call the backend to detect the bounding box of the face in the image. -// 3. Call the backend to recognize the face. +// 3. Call the backend to recognize the face. async function recognize(event) { event.preventDefault(); hide("buttons"); @@ -187,7 +188,7 @@ async function store(event) { return false; } -// Invoked when a file is selected in the file input element. +// Invoked when a file is selected in the file input element. // Loads the given file as an image to show to the user. async function load_local_image(event) { message(""); @@ -225,13 +226,13 @@ function toDataURL(blob) { async function restart(event) { hide("restart"); message(""); + const video = elem("video"); if (video.srcObject) { event.preventDefault(); } navigator.mediaDevices .getUserMedia({ video: true, audio: false }) .then((stream) => { - const video = elem("video"); video.srcObject = stream; video.play(); show("buttons"); diff --git a/rust/face-recognition/frontend/vite.config.js b/rust/face-recognition/frontend/vite.config.js new file mode 100644 index 0000000000..cfa898e9d0 --- /dev/null +++ b/rust/face-recognition/frontend/vite.config.js @@ -0,0 +1,56 @@ +import { defineConfig, loadEnv } from "vite"; +import { execSync } from "child_process"; +import { icpBindgen } from "@icp-sdk/bindgen/plugins/vite"; + +function getDevServerConfig() { + // Try icp-cli first + try { + const canisterId = execSync("icp canister status backend -e local -i", { + encoding: "utf-8", + stdio: "pipe", + }).trim(); + const networkStatus = JSON.parse( + execSync("icp network status --json", { + encoding: "utf-8", + stdio: "pipe", + }) + ); + return { + headers: { + "Set-Cookie": `ic_env=${encodeURIComponent( + `ic_root_key=${networkStatus.root_key}&PUBLIC_CANISTER_ID:backend=${canisterId}` + )}; SameSite=Lax;`, + }, + proxy: { + "/api": { target: "http://127.0.0.1:8000", changeOrigin: true }, + }, + }; + } catch {} + + throw new Error( + "No local network running. Start with:\n icp network start -d && icp deploy" + ); +} + +export default defineConfig(({ command, mode }) => { + const env = loadEnv(mode, "..", ["CANISTER_"]); + + return { + base: "./", + plugins: [ + icpBindgen({ + didFile: "../backend/backend.did", + outDir: "./src/bindings", + }), + ], + define: { + "process.env.CANISTER_ID_BACKEND": JSON.stringify( + env.CANISTER_ID_BACKEND + ), + }, + optimizeDeps: { + esbuildOptions: { define: { global: "globalThis" } }, + }, + server: command === "serve" ? getDevServerConfig() : undefined, + }; +}); diff --git a/rust/face-recognition/icp.yaml b/rust/face-recognition/icp.yaml new file mode 100644 index 0000000000..4e5178ef5b --- /dev/null +++ b/rust/face-recognition/icp.yaml @@ -0,0 +1,28 @@ +networks: + - name: local + mode: managed + +canisters: + - name: backend + build: + steps: + - type: script + commands: + - rustup target add wasm32-wasip1 + - cargo binstall --no-confirm wasi2ic + - cargo binstall --no-confirm wasm-opt + - export RUSTFLAGS="${RUSTFLAGS} -C target-feature=+simd128" + - cargo build --package backend --target wasm32-wasip1 --release + - TARGET_DIR=$(cargo metadata --format-version 1 --no-deps | sed -n 's/.*"target_directory":"\([^"]*\)".*/\1/p') + - wasi2ic "${TARGET_DIR}/wasm32-wasip1/release/backend.wasm" "$ICP_WASM_OUTPUT_PATH" + - wasm-opt -Os --enable-simd --enable-bulk-memory -o "$ICP_WASM_OUTPUT_PATH" "$ICP_WASM_OUTPUT_PATH" + - ic-wasm "$ICP_WASM_OUTPUT_PATH" -o "$ICP_WASM_OUTPUT_PATH" metadata candid:service -f backend/backend.did -v public --keep-name-section + + - name: frontend + recipe: + type: "@dfinity/asset-canister@v2.2.1" + configuration: + dir: frontend/dist + build: + - npm install --prefix frontend + - npm run build --prefix frontend diff --git a/rust/face-recognition/package-lock.json b/rust/face-recognition/package-lock.json deleted file mode 100644 index 74bf7143fc..0000000000 --- a/rust/face-recognition/package-lock.json +++ /dev/null @@ -1,4607 +0,0 @@ -{ - "name": "icp_detect", - "version": "0.2.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "icp_detect", - "version": "0.2.0", - "dependencies": { - "@icp-sdk/core": "~5.0.0" - }, - "devDependencies": { - "assert": "2.0.0", - "buffer": "6.0.3", - "copy-webpack-plugin": "^11.0.0", - "dotenv": "^16.0.3", - "events": "3.3.0", - "html-webpack-plugin": "5.5.0", - "process": "0.11.10", - "stream-browserify": "3.0.0", - "terser-webpack-plugin": "^5.3.3", - "util": "0.12.4", - "webpack": "^5.73.0", - "webpack-cli": "^4.10.0", - "webpack-dev-server": "^4.8.1" - }, - "engines": { - "node": "^12 || ^14 || ^16 || ^18 || ^21" - } - }, - "node_modules/@dfinity/cbor": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@dfinity/cbor/-/cbor-0.2.2.tgz", - "integrity": "sha512-GPJpH73kDEKbUBdUjY80lz7cq9l0vm1h/7ppejPV6O0ZTqCLrYspssYvqjRmK4aNnJ/SKXsP0rg9LYX7zpegaA==", - "license": "Apache-2.0" - }, - "node_modules/@discoveryjs/json-ext": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", - "dev": true, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@icp-sdk/core": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@icp-sdk/core/-/core-5.0.0.tgz", - "integrity": "sha512-t6iRbdylHG57MicWRpR1uMTFXRW7GCzec6KAg55CBwDHbHLQDKikQ252lmlcEa80DrKa3LPvMKYZEUYjEq5XUQ==", - "license": "Apache-2.0", - "dependencies": { - "@dfinity/cbor": "^0.2.2", - "@noble/curves": "^1.9.2", - "@noble/hashes": "^1.8.0", - "@scure/bip32": "^1.7.0", - "@scure/bip39": "^1.6.0", - "asn1js": "^3.0.5" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", - "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", - "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@leichtgewicht/ip-codec": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", - "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", - "dev": true - }, - "node_modules/@noble/curves": { - "version": "1.9.7", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", - "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", - "license": "MIT", - "dependencies": { - "@noble/hashes": "1.8.0" - }, - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@noble/hashes": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", - "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", - "license": "MIT", - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@scure/base": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", - "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==", - "license": "MIT", - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@scure/bip32": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz", - "integrity": "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==", - "license": "MIT", - "dependencies": { - "@noble/curves": "~1.9.0", - "@noble/hashes": "~1.8.0", - "@scure/base": "~1.2.5" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@scure/bip39": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz", - "integrity": "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==", - "license": "MIT", - "dependencies": { - "@noble/hashes": "~1.8.0", - "@scure/base": "~1.2.5" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@types/body-parser": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", - "dev": true, - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/bonjour": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", - "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/connect-history-api-fallback": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", - "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", - "dev": true, - "dependencies": { - "@types/express-serve-static-core": "*", - "@types/node": "*" - } - }, - "node_modules/@types/eslint": { - "version": "8.44.8", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.8.tgz", - "integrity": "sha512-4K8GavROwhrYl2QXDXm0Rv9epkA8GBFu0EI+XrrnnuCl7u8CWBRusX7fXJfanhZTDWSAL24gDI/UqXyUM0Injw==", - "dev": true, - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dev": true, - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true - }, - "node_modules/@types/express": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", - "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", - "dev": true, - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.17.41", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz", - "integrity": "sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==", - "dev": true, - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", - "dev": true - }, - "node_modules/@types/http-errors": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", - "dev": true - }, - "node_modules/@types/http-proxy": { - "version": "1.17.14", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz", - "integrity": "sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true - }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true - }, - "node_modules/@types/node": { - "version": "20.10.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.4.tgz", - "integrity": "sha512-D08YG6rr8X90YB56tSIuBaddy/UXAA9RKJoFvrsnogAum/0pmjkgi4+2nx96A330FmioegBWmEYQ+syqCFaveg==", - "dev": true, - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/node-forge": { - "version": "1.3.10", - "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.10.tgz", - "integrity": "sha512-y6PJDYN4xYBxwd22l+OVH35N+1fCYWiuC3aiP2SlXVE6Lo7SS+rSx9r89hLxrP4pn6n1lBGhHJ12pj3F3Mpttw==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/qs": { - "version": "6.9.10", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.10.tgz", - "integrity": "sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==", - "dev": true - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true - }, - "node_modules/@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", - "dev": true - }, - "node_modules/@types/send": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", - "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", - "dev": true, - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-index": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", - "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", - "dev": true, - "dependencies": { - "@types/express": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", - "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", - "dev": true, - "dependencies": { - "@types/http-errors": "*", - "@types/mime": "*", - "@types/node": "*" - } - }, - "node_modules/@types/sockjs": { - "version": "0.3.36", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", - "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/ws": { - "version": "8.5.10", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", - "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", - "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", - "dev": true, - "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", - "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", - "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", - "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", - "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", - "dev": true, - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", - "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", - "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", - "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", - "dev": true, - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", - "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", - "dev": true, - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", - "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", - "dev": true - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", - "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-opt": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6", - "@webassemblyjs/wast-printer": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", - "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", - "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", - "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", - "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webpack-cli/configtest": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", - "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", - "dev": true, - "peerDependencies": { - "webpack": "4.x.x || 5.x.x", - "webpack-cli": "4.x.x" - } - }, - "node_modules/@webpack-cli/info": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", - "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", - "dev": true, - "dependencies": { - "envinfo": "^7.7.3" - }, - "peerDependencies": { - "webpack-cli": "4.x.x" - } - }, - "node_modules/@webpack-cli/serve": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", - "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", - "dev": true, - "peerDependencies": { - "webpack-cli": "4.x.x" - }, - "peerDependenciesMeta": { - "webpack-dev-server": { - "optional": true - } - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dev": true, - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", - "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", - "dev": true, - "peer": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-import-assertions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", - "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", - "dev": true, - "peerDependencies": { - "acorn": "^8" - } - }, - "node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dev": true, - "peer": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dev": true, - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/ansi-html-community": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", - "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", - "dev": true, - "engines": [ - "node >= 0.8.0" - ], - "bin": { - "ansi-html": "bin/ansi-html" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", - "dev": true - }, - "node_modules/asn1js": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.7.tgz", - "integrity": "sha512-uLvq6KJu04qoQM6gvBfKFjlh6Gl0vOKQuR5cJMDHQkmwfMOQeN3F3SHCv9SNYSL+CRoHvOGFfllDlVz03GQjvQ==", - "license": "BSD-3-Clause", - "dependencies": { - "pvtsutils": "^1.3.6", - "pvutils": "^1.1.3", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/assert": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", - "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", - "dev": true, - "dependencies": { - "es6-object-assign": "^1.1.0", - "is-nan": "^1.2.1", - "object-is": "^1.0.1", - "util": "^0.12.0" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", - "dev": true - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", - "dev": true, - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/bonjour-service": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.1.1.tgz", - "integrity": "sha512-Z/5lQRMOG9k7W+FkeGTNjh7htqn/2LMnfOvBZ8pynNZCM9MwkQkI3zeI4oz09uWdcgmgHugVvBqxGg4VQJ5PCg==", - "dev": true, - "dependencies": { - "array-flatten": "^2.1.2", - "dns-equal": "^1.0.0", - "fast-deep-equal": "^3.1.3", - "multicast-dns": "^7.2.5" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.22.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", - "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "peer": true, - "dependencies": { - "caniuse-lite": "^1.0.30001565", - "electron-to-chromium": "^1.4.601", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "dev": true, - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/camel-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", - "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "dev": true, - "dependencies": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001566", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001566.tgz", - "integrity": "sha512-ggIhCsTxmITBAMmK8yZjEhCO5/47jKXPu6Dha/wuCS4JePVL+3uiDEBuhu2aIoT+bqTOR8L76Ip1ARL9xYsEJA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "dev": true, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/clean-css": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", - "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", - "dev": true, - "dependencies": { - "source-map": "~0.6.0" - }, - "engines": { - "node": ">= 10.0" - } - }, - "node_modules/clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, - "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true - }, - "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "dev": true, - "dependencies": { - "mime-db": ">= 1.43.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "dev": true, - "dependencies": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/compression/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/connect-history-api-fallback": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", - "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dev": true, - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "dev": true - }, - "node_modules/copy-webpack-plugin": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", - "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", - "dev": true, - "dependencies": { - "fast-glob": "^3.2.11", - "glob-parent": "^6.0.1", - "globby": "^13.1.1", - "normalize-path": "^3.0.0", - "schema-utils": "^4.0.0", - "serialize-javascript": "^6.0.0" - }, - "engines": { - "node": ">= 14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - } - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/css-select": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", - "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", - "dev": true, - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.0.1", - "domhandler": "^4.3.1", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "dev": true, - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/default-gateway": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", - "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", - "dev": true, - "dependencies": { - "execa": "^5.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "dev": true, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "dev": true - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==", - "dev": true - }, - "node_modules/dns-packet": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", - "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", - "dev": true, - "dependencies": { - "@leichtgewicht/ip-codec": "^2.0.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/dom-converter": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", - "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", - "dev": true, - "dependencies": { - "utila": "~0.4" - } - }, - "node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "dev": true, - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.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==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "dev": true, - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "dev": true, - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/dotenv": { - "version": "16.3.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", - "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/motdotla/dotenv?sponsor=1" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "dev": true - }, - "node_modules/electron-to-chromium": { - "version": "1.4.608", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.608.tgz", - "integrity": "sha512-J2f/3iIIm3Mo0npneITZ2UPe4B1bg8fTNrFjD8715F/k1BvbviRuqYGkET1PgprrczXYTHFvotbBOmUp6KE0uA==", - "dev": true - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/enhanced-resolve": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", - "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/envinfo": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.11.0.tgz", - "integrity": "sha512-G9/6xF1FPbIw0TtalAMaVPpiq2aDEuKLXM314jPVAO9r2fo2a4BLqMNkmRS7O/xPPZ+COAhGIz3ETvHEV3eUcg==", - "dev": true, - "bin": { - "envinfo": "dist/cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-module-lexer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz", - "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==", - "dev": true - }, - "node_modules/es6-object-assign": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", - "integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==", - "dev": true - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "dev": true - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", - "dev": true, - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.6.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/express/node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "dev": true - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fastest-levenshtein": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", - "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", - "dev": true, - "engines": { - "node": ">= 4.9.1" - } - }, - "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/faye-websocket": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", - "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", - "dev": true, - "dependencies": { - "websocket-driver": ">=0.5.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", - "dev": true, - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "bin": { - "flat": "cli.js" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-monkey": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.5.tgz", - "integrity": "sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==", - "dev": true - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true - }, - "node_modules/globby": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", - "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", - "dev": true, - "dependencies": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.3.0", - "ignore": "^5.2.4", - "merge2": "^1.4.1", - "slash": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "node_modules/handle-thing": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", - "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", - "dev": true - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "bin": { - "he": "bin/he" - } - }, - "node_modules/hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", - "dev": true, - "dependencies": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" - } - }, - "node_modules/hpack.js/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/hpack.js/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/hpack.js/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/html-entities": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz", - "integrity": "sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/mdevils" - }, - { - "type": "patreon", - "url": "https://patreon.com/mdevils" - } - ] - }, - "node_modules/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", - "dev": true, - "dependencies": { - "camel-case": "^4.1.2", - "clean-css": "^5.2.2", - "commander": "^8.3.0", - "he": "^1.2.0", - "param-case": "^3.0.4", - "relateurl": "^0.2.7", - "terser": "^5.10.0" - }, - "bin": { - "html-minifier-terser": "cli.js" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/html-minifier-terser/node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "dev": true, - "engines": { - "node": ">= 12" - } - }, - "node_modules/html-webpack-plugin": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", - "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==", - "dev": true, - "dependencies": { - "@types/html-minifier-terser": "^6.0.0", - "html-minifier-terser": "^6.0.2", - "lodash": "^4.17.21", - "pretty-error": "^4.0.0", - "tapable": "^2.0.0" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/html-webpack-plugin" - }, - "peerDependencies": { - "webpack": "^5.20.0" - } - }, - "node_modules/htmlparser2": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", - "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "dev": true, - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" - } - }, - "node_modules/http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", - "dev": true - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dev": true, - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-parser-js": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", - "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", - "dev": true - }, - "node_modules/http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "dev": true, - "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/http-proxy-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", - "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", - "dev": true, - "dependencies": { - "@types/http-proxy": "^1.17.8", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "@types/express": "^4.17.13" - }, - "peerDependenciesMeta": { - "@types/express": { - "optional": true - } - } - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/ignore": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", - "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/interpret": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", - "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/ipaddr.js": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz", - "integrity": "sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==", - "dev": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dev": true, - "dependencies": { - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true, - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-nan": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", - "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-plain-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", - "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "dependencies": { - "isobject": "^3.0.1" - }, - "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", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", - "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", - "dev": true, - "dependencies": { - "which-typed-array": "^1.1.11" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dev": true, - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/launch-editor": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.1.tgz", - "integrity": "sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw==", - "dev": true, - "dependencies": { - "picocolors": "^1.0.0", - "shell-quote": "^1.8.1" - } - }, - "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "dev": true, - "engines": { - "node": ">=6.11.5" - } - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "node_modules/lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dev": true, - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/memfs": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", - "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", - "dev": true, - "dependencies": { - "fs-monkey": "^1.0.4" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/multicast-dns": { - "version": "7.2.5", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", - "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", - "dev": true, - "dependencies": { - "dns-packet": "^5.2.2", - "thunky": "^1.0.2" - }, - "bin": { - "multicast-dns": "cli.js" - } - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "node_modules/no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "dev": true, - "dependencies": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, - "node_modules/node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", - "dev": true, - "engines": { - "node": ">= 6.13.0" - } - }, - "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 - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dev": true, - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "dev": true - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dev": true, - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/open": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", - "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", - "dev": true, - "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-retry": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", - "dev": true, - "dependencies": { - "@types/retry": "0.12.0", - "retry": "^0.13.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/param-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", - "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "dev": true, - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/pascal-case": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", - "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "dev": true, - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", - "dev": true - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pretty-error": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", - "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", - "dev": true, - "dependencies": { - "lodash": "^4.17.20", - "renderkid": "^3.0.0" - } - }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "dev": true, - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dev": true, - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/proxy-addr/node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/pvtsutils": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz", - "integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==", - "license": "MIT", - "dependencies": { - "tslib": "^2.8.1" - } - }, - "node_modules/pvutils": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.5.tgz", - "integrity": "sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==", - "license": "MIT", - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "dev": true, - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "dev": true, - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/raw-body/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/rechoir": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", - "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", - "dev": true, - "dependencies": { - "resolve": "^1.9.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/renderkid": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", - "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", - "dev": true, - "dependencies": { - "css-select": "^4.1.3", - "dom-converter": "^0.2.0", - "htmlparser2": "^6.1.0", - "lodash": "^4.17.21", - "strip-ansi": "^6.0.1" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "node_modules/schema-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", - "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", - "dev": true - }, - "node_modules/selfsigned": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", - "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", - "dev": true, - "dependencies": { - "@types/node-forge": "^1.3.0", - "node-forge": "^1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", - "dev": true, - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/serialize-javascript": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", - "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", - "dev": true, - "dependencies": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/serve-index/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-index/node_modules/http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", - "dev": true, - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-index/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", - "dev": true - }, - "node_modules/serve-index/node_modules/setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "dev": true - }, - "node_modules/serve-index/node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", - "dev": true, - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dev": true, - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true - }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/shell-quote": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", - "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/sockjs": { - "version": "0.3.24", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", - "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", - "dev": true, - "dependencies": { - "faye-websocket": "^0.11.3", - "uuid": "^8.3.2", - "websocket-driver": "^0.7.4" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/spdy": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", - "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", - "dev": true, - "dependencies": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/spdy-transport": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", - "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", - "dev": true, - "dependencies": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" - } - }, - "node_modules/spdy-transport/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/spdy-transport/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/spdy/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/spdy/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/stream-browserify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", - "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", - "dev": true, - "dependencies": { - "inherits": "~2.0.4", - "readable-stream": "^3.5.0" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/terser": { - "version": "5.26.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.26.0.tgz", - "integrity": "sha512-dytTGoE2oHgbNV9nTzgBEPaqAWvcJNl66VZ0BkJqlvp71IjO8CxdBx/ykCNb47cLnCmCvRZ6ZR0tLkqvZCdVBQ==", - "dev": true, - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "5.3.9", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", - "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.17", - "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.16.8" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, - "node_modules/terser-webpack-plugin/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "peer": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/terser-webpack-plugin/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", - "dev": true - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dev": true, - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/util": { - "version": "0.12.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", - "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "safe-buffer": "^5.1.2", - "which-typed-array": "^1.1.2" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true - }, - "node_modules/utila": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", - "dev": true - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "dev": true, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", - "dev": true, - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", - "dev": true, - "dependencies": { - "minimalistic-assert": "^1.0.0" - } - }, - "node_modules/webpack": { - "version": "5.89.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz", - "integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==", - "dev": true, - "peer": true, - "dependencies": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^1.0.0", - "@webassemblyjs/ast": "^1.11.5", - "@webassemblyjs/wasm-edit": "^1.11.5", - "@webassemblyjs/wasm-parser": "^1.11.5", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.9.0", - "browserslist": "^4.14.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.15.0", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.7", - "watchpack": "^2.4.0", - "webpack-sources": "^3.2.3" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-cli": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", - "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", - "dev": true, - "peer": true, - "dependencies": { - "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^1.2.0", - "@webpack-cli/info": "^1.5.0", - "@webpack-cli/serve": "^1.7.0", - "colorette": "^2.0.14", - "commander": "^7.0.0", - "cross-spawn": "^7.0.3", - "fastest-levenshtein": "^1.0.12", - "import-local": "^3.0.2", - "interpret": "^2.2.0", - "rechoir": "^0.7.0", - "webpack-merge": "^5.7.3" - }, - "bin": { - "webpack-cli": "bin/cli.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "4.x.x || 5.x.x" - }, - "peerDependenciesMeta": { - "@webpack-cli/generators": { - "optional": true - }, - "@webpack-cli/migrate": { - "optional": true - }, - "webpack-bundle-analyzer": { - "optional": true - }, - "webpack-dev-server": { - "optional": true - } - } - }, - "node_modules/webpack-cli/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "dev": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/webpack-dev-middleware": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", - "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==", - "dev": true, - "dependencies": { - "colorette": "^2.0.10", - "memfs": "^3.4.3", - "mime-types": "^2.1.31", - "range-parser": "^1.2.1", - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/webpack-dev-server": { - "version": "4.15.1", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.1.tgz", - "integrity": "sha512-5hbAst3h3C3L8w6W4P96L5vaV0PxSmJhxZvWKYIdgxOQm8pNZ5dEOmmSLBVpP85ReeyRt6AS1QJNyo/oFFPeVA==", - "dev": true, - "dependencies": { - "@types/bonjour": "^3.5.9", - "@types/connect-history-api-fallback": "^1.3.5", - "@types/express": "^4.17.13", - "@types/serve-index": "^1.9.1", - "@types/serve-static": "^1.13.10", - "@types/sockjs": "^0.3.33", - "@types/ws": "^8.5.5", - "ansi-html-community": "^0.0.8", - "bonjour-service": "^1.0.11", - "chokidar": "^3.5.3", - "colorette": "^2.0.10", - "compression": "^1.7.4", - "connect-history-api-fallback": "^2.0.0", - "default-gateway": "^6.0.3", - "express": "^4.17.3", - "graceful-fs": "^4.2.6", - "html-entities": "^2.3.2", - "http-proxy-middleware": "^2.0.3", - "ipaddr.js": "^2.0.1", - "launch-editor": "^2.6.0", - "open": "^8.0.9", - "p-retry": "^4.5.0", - "rimraf": "^3.0.2", - "schema-utils": "^4.0.0", - "selfsigned": "^2.1.1", - "serve-index": "^1.9.1", - "sockjs": "^0.3.24", - "spdy": "^4.0.2", - "webpack-dev-middleware": "^5.3.1", - "ws": "^8.13.0" - }, - "bin": { - "webpack-dev-server": "bin/webpack-dev-server.js" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.37.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "webpack": { - "optional": true - }, - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-merge": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", - "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", - "dev": true, - "dependencies": { - "clone-deep": "^4.0.1", - "flat": "^5.0.2", - "wildcard": "^2.0.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "dev": true, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "peer": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/webpack/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/webpack/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/webpack/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "dev": true, - "dependencies": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", - "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.4", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/wildcard": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", - "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", - "dev": true - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "dev": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - } - } -} diff --git a/rust/face-recognition/package.json b/rust/face-recognition/package.json index 50fb98e514..eb1f8e9056 100644 --- a/rust/face-recognition/package.json +++ b/rust/face-recognition/package.json @@ -1,46 +1,12 @@ { - "name": "icp_detect", - "version": "0.2.0", - "description": "Internet Computer starter application", - "keywords": [ - "Internet Computer", - "Motoko", - "JavaScript", - "Canister" - ], + "name": "face-recognition", "scripts": { - "build": "webpack", - "prebuild": "dfx generate", - "start": "webpack serve --mode development --env development", - "deploy:local": "dfx deploy --network=local", - "deploy:ic": "dfx deploy --network=ic", - "generate": "dfx generate backend" + "build": "npm run build --workspaces --if-present", + "prebuild": "npm run prebuild --workspaces --if-present", + "dev": "npm run dev --workspaces --if-present" }, - "dependencies": { - "@icp-sdk/core": "~5.0.0" - }, - "devDependencies": { - "assert": "2.0.0", - "buffer": "6.0.3", - "copy-webpack-plugin": "^11.0.0", - "dotenv": "^16.0.3", - "events": "3.3.0", - "html-webpack-plugin": "5.5.0", - "process": "0.11.10", - "stream-browserify": "3.0.0", - "terser-webpack-plugin": "^5.3.3", - "util": "0.12.4", - "webpack": "^5.73.0", - "webpack-cli": "^4.10.0", - "webpack-dev-server": "^4.8.1" - }, - "engines": { - "node": "^12 || ^14 || ^16 || ^18 || ^21" - }, - "browserslist": [ - "last 2 chrome version", - "last 2 firefox version", - "last 2 safari version", - "last 2 edge version" + "type": "module", + "workspaces": [ + "frontend" ] } diff --git a/rust/face-recognition/rust-toolchain.toml b/rust/face-recognition/rust-toolchain.toml index 0ecadb6708..cb53a54b01 100644 --- a/rust/face-recognition/rust-toolchain.toml +++ b/rust/face-recognition/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] channel = "1.79" -targets = ["wasm32-unknown-unknown", "wasm32-wasi"] +targets = ["wasm32-wasip1"] components = ["rustfmt", "clippy"] diff --git a/rust/face-recognition/src/backend/assets/image.png b/rust/face-recognition/src/backend/assets/image.png deleted file mode 100644 index f8b5d2c8a18f436cf1b00e4d921dd3b270adcc95..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61197 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 Hu0mjfvX$wM diff --git a/rust/face-recognition/src/declarations/backend/backend.did b/rust/face-recognition/src/declarations/backend/backend.did deleted file mode 100644 index 944ed2256f..0000000000 --- a/rust/face-recognition/src/declarations/backend/backend.did +++ /dev/null @@ -1,51 +0,0 @@ -type BoundingBox = record { - left: float32; - top: float32; - right: float32; - bottom: float32; -}; - -type Embedding = record { - v0: vec float32; -}; - -type Person = record { - label: text; - score: float32; -}; - -type Detection = variant { - Ok: BoundingBox; - Err: Error; -}; - -type Addition = variant { - Ok: Embedding; - Err: Error; -}; - -type Recognition = variant { - Ok: Person; - Err: Error; -}; - -type Error = record { - message: text; -}; - -service : { - "detect": (image: blob) -> (Detection) query; - "recognize": (image: blob) -> (Recognition); - "add": (label: text, image: blob) -> (Addition); - - // These endpoints are used for incremental uploading of model files. - "clear_face_detection_model_bytes": () -> (); - "clear_face_recognition_model_bytes": () -> (); - "append_face_detection_model_bytes": (bytes: blob) -> (); - "append_face_recognition_model_bytes": (bytes: blob) -> (); - "setup_models": () -> (); - - // These endpoints are used only for testing and benchmarking. - "run_detection": () -> (Detection) query; - "run_recognition": () -> (Recognition); -} \ No newline at end of file diff --git a/rust/face-recognition/src/declarations/backend/backend.did.d.ts b/rust/face-recognition/src/declarations/backend/backend.did.d.ts deleted file mode 100644 index f0f60b9e78..0000000000 --- a/rust/face-recognition/src/declarations/backend/backend.did.d.ts +++ /dev/null @@ -1,39 +0,0 @@ -import type { Principal } from '@icp-sdk/core/principal'; -import type { ActorMethod } from '@icp-sdk/core/agent'; -import type { IDL } from '@icp-sdk/core/candid'; - -export type Addition = { 'Ok' : Embedding } | - { 'Err' : Error }; -export interface BoundingBox { - 'top' : number, - 'left' : number, - 'bottom' : number, - 'right' : number, -} -export type Detection = { 'Ok' : BoundingBox } | - { 'Err' : Error }; -export interface Embedding { 'v0' : Array } -export interface Error { 'message' : string } -export interface Person { 'label' : string, 'score' : number } -export type Recognition = { 'Ok' : Person } | - { 'Err' : Error }; -export interface _SERVICE { - 'add' : ActorMethod<[string, Uint8Array | number[]], Addition>, - 'append_face_detection_model_bytes' : ActorMethod< - [Uint8Array | number[]], - undefined - >, - 'append_face_recognition_model_bytes' : ActorMethod< - [Uint8Array | number[]], - undefined - >, - 'clear_face_detection_model_bytes' : ActorMethod<[], undefined>, - 'clear_face_recognition_model_bytes' : ActorMethod<[], undefined>, - 'detect' : ActorMethod<[Uint8Array | number[]], Detection>, - 'recognize' : ActorMethod<[Uint8Array | number[]], Recognition>, - 'run_detection' : ActorMethod<[], Detection>, - 'run_recognition' : ActorMethod<[], Recognition>, - 'setup_models' : ActorMethod<[], undefined>, -} -export declare const idlFactory: IDL.InterfaceFactory; -export declare const init: (args: { IDL: typeof IDL }) => IDL.Type[]; diff --git a/rust/face-recognition/src/declarations/backend/backend.did.js b/rust/face-recognition/src/declarations/backend/backend.did.js deleted file mode 100644 index d026c3663c..0000000000 --- a/rust/face-recognition/src/declarations/backend/backend.did.js +++ /dev/null @@ -1,31 +0,0 @@ -export const idlFactory = ({ IDL }) => { - const Embedding = IDL.Record({ 'v0' : IDL.Vec(IDL.Float32) }); - const Error = IDL.Record({ 'message' : IDL.Text }); - const Addition = IDL.Variant({ 'Ok' : Embedding, 'Err' : Error }); - const BoundingBox = IDL.Record({ - 'top' : IDL.Float32, - 'left' : IDL.Float32, - 'bottom' : IDL.Float32, - 'right' : IDL.Float32, - }); - const Detection = IDL.Variant({ 'Ok' : BoundingBox, 'Err' : Error }); - const Person = IDL.Record({ 'label' : IDL.Text, 'score' : IDL.Float32 }); - const Recognition = IDL.Variant({ 'Ok' : Person, 'Err' : Error }); - return IDL.Service({ - 'add' : IDL.Func([IDL.Text, IDL.Vec(IDL.Nat8)], [Addition], []), - 'append_face_detection_model_bytes' : IDL.Func([IDL.Vec(IDL.Nat8)], [], []), - 'append_face_recognition_model_bytes' : IDL.Func( - [IDL.Vec(IDL.Nat8)], - [], - [], - ), - 'clear_face_detection_model_bytes' : IDL.Func([], [], []), - 'clear_face_recognition_model_bytes' : IDL.Func([], [], []), - 'detect' : IDL.Func([IDL.Vec(IDL.Nat8)], [Detection], ['query']), - 'recognize' : IDL.Func([IDL.Vec(IDL.Nat8)], [Recognition], []), - 'run_detection' : IDL.Func([], [Detection], ['query']), - 'run_recognition' : IDL.Func([], [Recognition], []), - 'setup_models' : IDL.Func([], [], []), - }); -}; -export const init = ({ IDL }) => { return []; }; diff --git a/rust/face-recognition/src/declarations/backend/index.d.ts b/rust/face-recognition/src/declarations/backend/index.d.ts deleted file mode 100644 index 27c1ab6cba..0000000000 --- a/rust/face-recognition/src/declarations/backend/index.d.ts +++ /dev/null @@ -1,50 +0,0 @@ -import type { - ActorSubclass, - HttpAgentOptions, - ActorConfig, - Agent, -} from "@icp-sdk/core/agent"; -import type { Principal } from "@icp-sdk/core/principal"; -import type { IDL } from "@icp-sdk/core/candid"; - -import { _SERVICE } from './backend.did'; - -export declare const idlFactory: IDL.InterfaceFactory; -export declare const canisterId: string; - -export declare interface CreateActorOptions { - /** - * @see {@link Agent} - */ - agent?: Agent; - /** - * @see {@link HttpAgentOptions} - */ - agentOptions?: HttpAgentOptions; - /** - * @see {@link ActorConfig} - */ - actorOptions?: ActorConfig; -} - -/** - * Intializes an {@link ActorSubclass}, configured with the provided SERVICE interface of a canister. - * @constructs {@link ActorSubClass} - * @param {string | Principal} canisterId - ID of the canister the {@link Actor} will talk to - * @param {CreateActorOptions} options - see {@link CreateActorOptions} - * @param {CreateActorOptions["agent"]} options.agent - a pre-configured agent you'd like to use. Supercedes agentOptions - * @param {CreateActorOptions["agentOptions"]} options.agentOptions - options to set up a new agent - * @see {@link HttpAgentOptions} - * @param {CreateActorOptions["actorOptions"]} options.actorOptions - options for the Actor - * @see {@link ActorConfig} - */ -export declare const createActor: ( - canisterId: string | Principal, - options?: CreateActorOptions -) => ActorSubclass<_SERVICE>; - -/** - * Intialized Actor using default settings, ready to talk to a canister using its candid interface - * @constructs {@link ActorSubClass} - */ -export declare const backend: ActorSubclass<_SERVICE>; diff --git a/rust/face-recognition/src/declarations/backend/index.js b/rust/face-recognition/src/declarations/backend/index.js deleted file mode 100644 index 4e6d330699..0000000000 --- a/rust/face-recognition/src/declarations/backend/index.js +++ /dev/null @@ -1,42 +0,0 @@ -import { Actor, HttpAgent } from "@icp-sdk/core/agent"; - -// Imports and re-exports candid interface -import { idlFactory } from "./backend.did.js"; -export { idlFactory } from "./backend.did.js"; - -/* CANISTER_ID is replaced by webpack based on node environment - * Note: canister environment variable will be standardized as - * process.env.CANISTER_ID_ - * beginning in dfx 0.15.0 - */ -export const canisterId = - process.env.CANISTER_ID_BACKEND; - -export const createActor = (canisterId, options = {}) => { - const agent = options.agent || new HttpAgent({ ...options.agentOptions }); - - if (options.agent && options.agentOptions) { - console.warn( - "Detected both agent and agentOptions passed to createActor. Ignoring agentOptions and proceeding with the provided agent." - ); - } - - // Fetch root key for certificate validation during development - if (process.env.DFX_NETWORK !== "ic") { - agent.fetchRootKey().catch((err) => { - console.warn( - "Unable to fetch root key. Check to ensure that your local replica is running" - ); - console.error(err); - }); - } - - // Creates an actor with using the candid interface and the HttpAgent - return Actor.createActor(idlFactory, { - agent, - canisterId, - ...options.actorOptions, - }); -}; - -export const backend = canisterId ? createActor(canisterId) : undefined; diff --git a/rust/face-recognition/src/frontend/assets/.ic-assets.json5 b/rust/face-recognition/src/frontend/assets/.ic-assets.json5 deleted file mode 100644 index 577df191a4..0000000000 --- a/rust/face-recognition/src/frontend/assets/.ic-assets.json5 +++ /dev/null @@ -1,56 +0,0 @@ -[ - { - "match": "**/*", - "headers": { - // Security: The Content Security Policy (CSP) given below aims at working with many apps rather than providing maximal security. - // We recommend tightening the CSP for your specific application. Some recommendations are as follows: - // - Use the CSP Evaluator (https://csp-evaluator.withgoogle.com/) to validate the CSP you define. - // - Follow the “Strict CSP” recommendations (https://csp.withgoogle.com/docs/strict-csp.html). However, note that in the context of the IC, - // nonces cannot be used because the response bodies must be static to work well with HTTP asset certification. - // Thus, we recommend to include script hashes (in combination with strict-dynamic) in the CSP as described - // in https://csp.withgoogle.com/docs/faq.html in section “What if my site is static and I can't add nonces to scripts?”. - // See for example the II CSP (https://github.com/dfinity/internet-identity/blob/main/src/internet_identity/src/http.rs). - // - It is recommended to tighten the connect-src directive. With the current CSP configuration the browser can - // make requests to https://*.icp0.io, hence being able to call any canister via https://icp0.io/api/v2/canister/{canister-ID}. - // This could potentially be used in combination with another vulnerability (e.g. XSS) to exfiltrate private data. - // The developer can configure this policy to only allow requests to their specific canisters, - // e.g: connect-src 'self' https://icp-api.io/api/v2/canister/{my-canister-ID}, where {my-canister-ID} has the following format: aaaaa-aaaaa-aaaaa-aaaaa-aaa - // - It is recommended to configure style-src, style-src-elem and font-src directives with the resources your canister is going to use - // instead of using the wild card (*) option. Normally this will include 'self' but also other third party styles or fonts resources (e.g: https://fonts.googleapis.com or other CDNs) - - // Notes about the CSP below: - // - script-src 'unsafe-eval' is currently required because agent-js uses a WebAssembly module for the validation of bls signatures. - // There is currently no other way to allow execution of WebAssembly modules with CSP. - // See: https://github.com/WebAssembly/content-security-policy/blob/main/proposals/CSP.md. - // - We added img-src data: because data: images are used often. - // - frame-ancestors: none mitigates clickjacking attacks. See https://owasp.org/www-community/attacks/Clickjacking. - "Content-Security-Policy": "default-src 'self';script-src 'self' 'unsafe-eval';connect-src 'self' https://icp0.io https://*.icp0.io;img-src 'self' data:;style-src * 'unsafe-inline';style-src-elem * 'unsafe-inline';font-src *;object-src 'none';base-uri 'self';frame-ancestors 'none';form-action 'self';upgrade-insecure-requests;", - - // Security: The permissions policy disables all features for security reasons. If your site needs such permissions, activate them. - // To configure permissions go here https://www.permissionspolicy.com/ - "Permissions-Policy": "accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=self, cross-origin-isolated=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), geolocation=(), gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), navigation-override=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=(), clipboard-read=(), clipboard-write=(), gamepad=(), speaker-selection=(), conversion-measurement=(), focus-without-user-activation=(), hid=(), idle-detection=(), interest-cohort=(), serial=(), sync-script=(), trust-token-redemption=(), window-placement=(), vertical-scroll=()", - - // Security: Mitigates clickjacking attacks. - // See: https://owasp.org/www-community/attacks/Clickjacking. - "X-Frame-Options": "DENY", - - // Security: Avoids forwarding referrer information to other origins. - // See: https://owasp.org/www-project-secure-headers/#referrer-policy. - "Referrer-Policy": "same-origin", - - // Security: Tells the user’s browser that it must always use HTTPS with your site. - // See: https://owasp.org/www-project-secure-headers/#http-strict-transport-security - "Strict-Transport-Security": "max-age=31536000; includeSubDomains", - - // Security: Prevents the browser from interpreting files as a different MIME type to what is specified in the Content-Type header. - // See: https://owasp.org/www-project-secure-headers/#x-content-type-options - "X-Content-Type-Options": "nosniff", - - // Security: Enables browser features to mitigate some of the XSS attacks. Note that it has to be in mode=block. - // See: https://owasp.org/www-community/attacks/xss/ - "X-XSS-Protection": "1; mode=block" - }, - // redirect all requests from .raw.icp0.io to .icp0.io (this redirection is the default) - "allow_raw_access": false - }, -] diff --git a/rust/face-recognition/src/frontend/assets/logo_transparent.png b/rust/face-recognition/src/frontend/assets/logo_transparent.png deleted file mode 100644 index 94b22ca99cd8d19bc61dc1df5f003f0f9d15e848..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 58988 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 z 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 diff --git a/rust/face-recognition/src/frontend/src/.ic-assets.json5 b/rust/face-recognition/src/frontend/src/.ic-assets.json5 deleted file mode 100644 index 577df191a4..0000000000 --- a/rust/face-recognition/src/frontend/src/.ic-assets.json5 +++ /dev/null @@ -1,56 +0,0 @@ -[ - { - "match": "**/*", - "headers": { - // Security: The Content Security Policy (CSP) given below aims at working with many apps rather than providing maximal security. - // We recommend tightening the CSP for your specific application. Some recommendations are as follows: - // - Use the CSP Evaluator (https://csp-evaluator.withgoogle.com/) to validate the CSP you define. - // - Follow the “Strict CSP” recommendations (https://csp.withgoogle.com/docs/strict-csp.html). However, note that in the context of the IC, - // nonces cannot be used because the response bodies must be static to work well with HTTP asset certification. - // Thus, we recommend to include script hashes (in combination with strict-dynamic) in the CSP as described - // in https://csp.withgoogle.com/docs/faq.html in section “What if my site is static and I can't add nonces to scripts?”. - // See for example the II CSP (https://github.com/dfinity/internet-identity/blob/main/src/internet_identity/src/http.rs). - // - It is recommended to tighten the connect-src directive. With the current CSP configuration the browser can - // make requests to https://*.icp0.io, hence being able to call any canister via https://icp0.io/api/v2/canister/{canister-ID}. - // This could potentially be used in combination with another vulnerability (e.g. XSS) to exfiltrate private data. - // The developer can configure this policy to only allow requests to their specific canisters, - // e.g: connect-src 'self' https://icp-api.io/api/v2/canister/{my-canister-ID}, where {my-canister-ID} has the following format: aaaaa-aaaaa-aaaaa-aaaaa-aaa - // - It is recommended to configure style-src, style-src-elem and font-src directives with the resources your canister is going to use - // instead of using the wild card (*) option. Normally this will include 'self' but also other third party styles or fonts resources (e.g: https://fonts.googleapis.com or other CDNs) - - // Notes about the CSP below: - // - script-src 'unsafe-eval' is currently required because agent-js uses a WebAssembly module for the validation of bls signatures. - // There is currently no other way to allow execution of WebAssembly modules with CSP. - // See: https://github.com/WebAssembly/content-security-policy/blob/main/proposals/CSP.md. - // - We added img-src data: because data: images are used often. - // - frame-ancestors: none mitigates clickjacking attacks. See https://owasp.org/www-community/attacks/Clickjacking. - "Content-Security-Policy": "default-src 'self';script-src 'self' 'unsafe-eval';connect-src 'self' https://icp0.io https://*.icp0.io;img-src 'self' data:;style-src * 'unsafe-inline';style-src-elem * 'unsafe-inline';font-src *;object-src 'none';base-uri 'self';frame-ancestors 'none';form-action 'self';upgrade-insecure-requests;", - - // Security: The permissions policy disables all features for security reasons. If your site needs such permissions, activate them. - // To configure permissions go here https://www.permissionspolicy.com/ - "Permissions-Policy": "accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=self, cross-origin-isolated=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), geolocation=(), gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), navigation-override=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=(), clipboard-read=(), clipboard-write=(), gamepad=(), speaker-selection=(), conversion-measurement=(), focus-without-user-activation=(), hid=(), idle-detection=(), interest-cohort=(), serial=(), sync-script=(), trust-token-redemption=(), window-placement=(), vertical-scroll=()", - - // Security: Mitigates clickjacking attacks. - // See: https://owasp.org/www-community/attacks/Clickjacking. - "X-Frame-Options": "DENY", - - // Security: Avoids forwarding referrer information to other origins. - // See: https://owasp.org/www-project-secure-headers/#referrer-policy. - "Referrer-Policy": "same-origin", - - // Security: Tells the user’s browser that it must always use HTTPS with your site. - // See: https://owasp.org/www-project-secure-headers/#http-strict-transport-security - "Strict-Transport-Security": "max-age=31536000; includeSubDomains", - - // Security: Prevents the browser from interpreting files as a different MIME type to what is specified in the Content-Type header. - // See: https://owasp.org/www-project-secure-headers/#x-content-type-options - "X-Content-Type-Options": "nosniff", - - // Security: Enables browser features to mitigate some of the XSS attacks. Note that it has to be in mode=block. - // See: https://owasp.org/www-community/attacks/xss/ - "X-XSS-Protection": "1; mode=block" - }, - // redirect all requests from .raw.icp0.io to .icp0.io (this redirection is the default) - "allow_raw_access": false - }, -] diff --git a/rust/face-recognition/upload-models-to-canister.sh b/rust/face-recognition/upload-models-to-canister.sh index bc60fde04a..fb4057e2f9 100755 --- a/rust/face-recognition/upload-models-to-canister.sh +++ b/rust/face-recognition/upload-models-to-canister.sh @@ -1,8 +1,8 @@ #!/bin/bash set -e -dfx canister call backend clear_face_detection_model_bytes -dfx canister call backend clear_face_recognition_model_bytes +icp canister call backend clear_face_detection_model_bytes '()' +icp canister call backend clear_face_recognition_model_bytes '()' ic-file-uploader backend append_face_detection_model_bytes version-RFB-320.onnx ic-file-uploader backend append_face_recognition_model_bytes face-recognition.onnx -dfx canister call backend setup_models +icp canister call backend setup_models '()' diff --git a/rust/face-recognition/webpack.config.js b/rust/face-recognition/webpack.config.js deleted file mode 100644 index 4792c054a4..0000000000 --- a/rust/face-recognition/webpack.config.js +++ /dev/null @@ -1,96 +0,0 @@ -require("dotenv").config(); -const path = require("path"); -const webpack = require("webpack"); -const HtmlWebpackPlugin = require("html-webpack-plugin"); -const TerserPlugin = require("terser-webpack-plugin"); -const CopyPlugin = require("copy-webpack-plugin"); - -const isDevelopment = process.env.NODE_ENV !== "production"; - -const frontendDirectory = "frontend"; - -const frontend_entry = path.join("src", frontendDirectory, "src", "index.html"); - -module.exports = { - target: "web", - mode: isDevelopment ? "development" : "production", - entry: { - // The frontend.entrypoint points to the HTML file for this build, so we need - // to replace the extension to `.js`. - index: path.join(__dirname, frontend_entry).replace(/\.html$/, ".js"), - }, - devtool: isDevelopment ? "source-map" : false, - optimization: { - minimize: !isDevelopment, - minimizer: [new TerserPlugin()], - }, - resolve: { - extensions: [".js", ".ts", ".jsx", ".tsx"], - fallback: { - assert: require.resolve("assert/"), - buffer: require.resolve("buffer/"), - events: require.resolve("events/"), - stream: require.resolve("stream-browserify/"), - util: require.resolve("util/"), - }, - }, - output: { - filename: "index.js", - path: path.join(__dirname, "dist", frontendDirectory), - }, - - // Depending in the language or framework you are using for - // front-end development, add module loaders to the default - // webpack configuration. For example, if you are using React - // modules and CSS as described in the "Adding a stylesheet" - // tutorial, uncomment the following lines: - // module: { - // rules: [ - // { test: /\.(ts|tsx|jsx)$/, loader: "ts-loader" }, - // { test: /\.css$/, use: ['style-loader','css-loader'] } - // ] - // }, - plugins: [ - new HtmlWebpackPlugin({ - template: path.join(__dirname, frontend_entry), - cache: false, - }), - new webpack.EnvironmentPlugin([ - ...Object.keys(process.env).filter((key) => { - if (key.includes("CANISTER")) return true; - if (key.includes("DFX")) return true; - return false; - }), - ]), - new webpack.ProvidePlugin({ - Buffer: [require.resolve("buffer/"), "Buffer"], - process: require.resolve("process/browser"), - }), - new CopyPlugin({ - patterns: [ - { - from: `src/${frontendDirectory}/src/.ic-assets.json*`, - to: ".ic-assets.json5", - noErrorOnMissing: true, - }, - ], - }), - ], - // proxy /api to port 4943 during development. - // if you edit dfx.json to define a project-specific local network, change the port to match. - devServer: { - proxy: { - "/api": { - target: "http://127.0.0.1:4943", - changeOrigin: true, - pathRewrite: { - "^/api": "/api", - }, - }, - }, - static: path.resolve(__dirname, "src", frontendDirectory, "assets"), - hot: true, - watchFiles: [path.resolve(__dirname, "src", frontendDirectory)], - liveReload: true, - }, -}; From 2f7a5b0389c7403d65dff4740ccdb77c3667b689 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Wed, 17 Jun 2026 19:04:28 +0200 Subject: [PATCH 02/31] chore(rust/face-recognition): test.sh over Makefile, bump CI image to 1.0.1 Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/face-recognition.yml | 4 +-- rust/face-recognition/Makefile | 34 -------------------------- rust/face-recognition/README.md | 2 +- rust/face-recognition/test.sh | 23 +++++++++++++++++ 4 files changed, 26 insertions(+), 37 deletions(-) delete mode 100644 rust/face-recognition/Makefile create mode 100755 rust/face-recognition/test.sh diff --git a/.github/workflows/face-recognition.yml b/.github/workflows/face-recognition.yml index c8aa87e9bb..d320f39dca 100644 --- a/.github/workflows/face-recognition.yml +++ b/.github/workflows/face-recognition.yml @@ -15,7 +15,7 @@ concurrency: jobs: rust-face-recognition: runs-on: ubuntu-24.04 - container: ghcr.io/dfinity/icp-dev-env-rust:1.0.0 + container: ghcr.io/dfinity/icp-dev-env-rust:1.0.1 env: ICP_CLI_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: @@ -25,4 +25,4 @@ jobs: run: | icp network start -d icp deploy - make test + bash test.sh diff --git a/rust/face-recognition/Makefile b/rust/face-recognition/Makefile deleted file mode 100644 index bcc9cb7caf..0000000000 --- a/rust/face-recognition/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -.PHONY: test upload-models - -test: - @echo "=== Test 1: clear_face_detection_model_bytes returns unit ===" - @result=$$(icp canister call backend clear_face_detection_model_bytes '()') && \ - echo "$$result" && \ - echo "PASS" || (echo "FAIL" && exit 1) - - @echo "=== Test 2: clear_face_recognition_model_bytes returns unit ===" - @result=$$(icp canister call backend clear_face_recognition_model_bytes '()') && \ - echo "$$result" && \ - echo "PASS" || (echo "FAIL" && exit 1) - - @echo "=== Test 3: append_face_detection_model_bytes accepts bytes ===" - @result=$$(icp canister call backend append_face_detection_model_bytes '(blob "\00\01\02")') && \ - echo "$$result" && \ - echo "PASS" || (echo "FAIL" && exit 1) - - @echo "=== Test 4: run_detection returns Err when model is not set up ===" - @result=$$(icp canister call --query backend run_detection '()') && \ - echo "$$result" && \ - echo "$$result" | grep -q 'Err' && \ - echo "PASS" || (echo "FAIL" && exit 1) - -# Upload ONNX model files to the canister incrementally. -# Requires: ic-file-uploader (cargo install ic-file-uploader) -# First download the models: ./download-face-detection-model.sh -# and obtain face-recognition.onnx (see README), then run: make upload-models -upload-models: - icp canister call backend clear_face_detection_model_bytes '()' - icp canister call backend clear_face_recognition_model_bytes '()' - ic-file-uploader backend append_face_detection_model_bytes version-RFB-320.onnx - ic-file-uploader backend append_face_recognition_model_bytes face-recognition.onnx - icp canister call backend setup_models '()' diff --git a/rust/face-recognition/README.md b/rust/face-recognition/README.md index b8df1551f7..cd8a17df78 100644 --- a/rust/face-recognition/README.md +++ b/rust/face-recognition/README.md @@ -68,7 +68,7 @@ cd examples/rust/face-recognition ```bash icp network start -d icp deploy -make test +bash test.sh icp network stop ``` diff --git a/rust/face-recognition/test.sh b/rust/face-recognition/test.sh new file mode 100755 index 0000000000..8bc59f47a0 --- /dev/null +++ b/rust/face-recognition/test.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +set -e + +echo "=== Test 1: clear_face_detection_model_bytes returns unit ===" +result=$(icp canister call backend clear_face_detection_model_bytes '()') && \ + echo "$result" && \ + echo "PASS" || (echo "FAIL" && exit 1) + +echo "=== Test 2: clear_face_recognition_model_bytes returns unit ===" +result=$(icp canister call backend clear_face_recognition_model_bytes '()') && \ + echo "$result" && \ + echo "PASS" || (echo "FAIL" && exit 1) + +echo "=== Test 3: append_face_detection_model_bytes accepts bytes ===" +result=$(icp canister call backend append_face_detection_model_bytes '(blob "\00\01\02")') && \ + echo "$result" && \ + echo "PASS" || (echo "FAIL" && exit 1) + +echo "=== Test 4: run_detection returns Err when model is not set up ===" +result=$(icp canister call --query backend run_detection '()') && \ + echo "$result" && \ + echo "$result" | grep -q 'Err' && \ + echo "PASS" || (echo "FAIL" && exit 1) From 800a3598f59103f16f82f842100174a0461146a6 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Mon, 6 Jul 2026 10:52:45 +0200 Subject: [PATCH 03/31] fix(rust/face-recognition): fix build, update deps, align frontend with canonical pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - rust-toolchain.toml: remove channel = "1.79" pin (Cargo 1.79 can't parse edition 2024 in transitive deps; removing pin uses system stable Rust 1.85+) - icp.yaml: replace cargo binstall with "which || cargo install" (cargo-binstall is not installed by default); remove redundant network definition - backend/Cargo.toml: ic-cdk 0.14→0.20, ic-stable-structures 0.6→0.7, ic-wasi-polyfill 0.4.1→0.13; edition 2021→2024; add cdylib comment - download-face-detection-model.sh: force IPv4 to avoid hanging IPv6 connections - frontend: align vite.config.js and actor.js with canonical icp-cli pattern (no dfx fallbacks); bump @icp-sdk/core 5.0→5.4, @icp-sdk/bindgen 0.2.2→0.4.0, vite 5.4.11→8.1.3 - CI: add apt-get install build-essential (required by wasi2ic's C++ dependency) - README: add Node.js v18+, Rust v1.85+, wasi2ic prerequisites; replace make upload-models with bash upload-models-to-canister.sh Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/face-recognition.yml | 2 + rust/face-recognition/README.md | 10 +- rust/face-recognition/backend/Cargo.toml | 9 +- .../download-face-detection-model.sh | 4 +- rust/face-recognition/frontend/package.json | 6 +- rust/face-recognition/frontend/src/actor.js | 8 +- rust/face-recognition/frontend/vite.config.js | 93 ++++++++++--------- rust/face-recognition/icp.yaml | 9 +- rust/face-recognition/rust-toolchain.toml | 2 - 9 files changed, 73 insertions(+), 70 deletions(-) diff --git a/.github/workflows/face-recognition.yml b/.github/workflows/face-recognition.yml index d320f39dca..2e4314e8d9 100644 --- a/.github/workflows/face-recognition.yml +++ b/.github/workflows/face-recognition.yml @@ -20,6 +20,8 @@ jobs: ICP_CLI_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + - name: Install build dependencies + run: apt-get update && apt-get install -y --no-install-recommends build-essential - name: Deploy and test working-directory: rust/face-recognition run: | diff --git a/rust/face-recognition/README.md b/rust/face-recognition/README.md index cd8a17df78..d243021c64 100644 --- a/rust/face-recognition/README.md +++ b/rust/face-recognition/README.md @@ -53,8 +53,12 @@ You can obtain a pretrained model from [facenet-pytorch](https://github.com/time ### Prerequisites -- Node.js -- icp-cli: `npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm` +- [Node.js](https://nodejs.org/) v18+ +- [icp-cli](https://cli.internetcomputer.org/): `npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm` +- [Rust](https://www.rust-lang.org/tools/install) v1.85+ with `wasm32-wasip1` target: `rustup target add wasm32-wasip1` +- [wasi2ic](https://github.com/wasm-forge/wasi2ic): `cargo install wasi2ic` + +`wasm-opt` is installed automatically on first deploy if not already present. ### Install @@ -80,7 +84,7 @@ After deploying, upload the ONNX models to the canister using [ic-file-uploader] ```bash cargo install ic-file-uploader -make upload-models +bash upload-models-to-canister.sh ``` Once the models are uploaded, open the frontend URL in your browser to interact with the smart contract. diff --git a/rust/face-recognition/backend/Cargo.toml b/rust/face-recognition/backend/Cargo.toml index 216f90ad70..1f7b6fc92e 100644 --- a/rust/face-recognition/backend/Cargo.toml +++ b/rust/face-recognition/backend/Cargo.toml @@ -1,9 +1,10 @@ [package] name = "backend" version = "1.1.0" -edition = "2021" +edition = "2024" [lib] +# cdylib produces a .wasm binary suitable for deployment as an ICP canister. crate-type = ["cdylib"] [dependencies] @@ -11,9 +12,9 @@ anyhow = "1.0" bytes = "1.5.0" candid = "0.10" colorgrad = "0.6" -ic-cdk = "0.14.0" -ic-stable-structures = "0.6" -ic-wasi-polyfill = "0.4.1" +ic-cdk = "0.20" +ic-stable-structures = "0.7" +ic-wasi-polyfill = "0.13" image = { version = "0.25.1", features = ["png"], default-features = false } prost = "0.11.0" prost-types = "0.11.0" diff --git a/rust/face-recognition/download-face-detection-model.sh b/rust/face-recognition/download-face-detection-model.sh index 3aea8a061f..e7262278ba 100755 --- a/rust/face-recognition/download-face-detection-model.sh +++ b/rust/face-recognition/download-face-detection-model.sh @@ -11,9 +11,9 @@ if [ -s "${ULTRAFACE_TGT}" ]; then fi if which wget >/dev/null; then - wget $ULTRAFACE_URL -O $ULTRAFACE_TGT + wget --inet4-only $ULTRAFACE_URL -O $ULTRAFACE_TGT elif which curl >/dev/null; then - curl -vL $ULTRAFACE_URL -o $ULTRAFACE_TGT + curl -4 -L $ULTRAFACE_URL -o $ULTRAFACE_TGT else echo "Couldn't find wget or curl." echo "Please download manually from \"$ULTRAFACE_URL\" and save the file in $ULTRAFACE_TGT." diff --git a/rust/face-recognition/frontend/package.json b/rust/face-recognition/frontend/package.json index d0cc35cdb0..87cf2ed1a3 100644 --- a/rust/face-recognition/frontend/package.json +++ b/rust/face-recognition/frontend/package.json @@ -8,10 +8,10 @@ "dev": "vite" }, "dependencies": { - "@icp-sdk/core": "~5.0.0" + "@icp-sdk/core": "~5.4.0" }, "devDependencies": { - "@icp-sdk/bindgen": "~0.2.2", - "vite": "5.4.11" + "@icp-sdk/bindgen": "~0.4.0", + "vite": "8.1.3" } } diff --git a/rust/face-recognition/frontend/src/actor.js b/rust/face-recognition/frontend/src/actor.js index 8d5275670b..f5a230e432 100644 --- a/rust/face-recognition/frontend/src/actor.js +++ b/rust/face-recognition/frontend/src/actor.js @@ -1,16 +1,14 @@ import { safeGetCanisterEnv } from "@icp-sdk/core/agent/canister-env"; import { createActor } from "./bindings/backend"; -// The ic_env cookie is set by the asset canister (SDK >=0.30.2) on all HTML +// The ic_env cookie is set by the asset canister (SDK ≥0.30.2) on all HTML // responses. It contains the replica root key and any PUBLIC_* canister // environment variables. In dev mode the vite dev server sets the same cookie // via Set-Cookie header (see vite.config.js). const canisterEnv = safeGetCanisterEnv(); -// Resolve canister ID: cookie (icp-cli + dev server) -> env var (dfx build-time) -const canisterId = - canisterEnv?.["PUBLIC_CANISTER_ID:backend"] ?? - process.env.CANISTER_ID_BACKEND; +// Resolve canister ID from the ic_env cookie (icp-cli + dev server). +const canisterId = canisterEnv?.["PUBLIC_CANISTER_ID:backend"]; if (!canisterId) { throw new Error( diff --git a/rust/face-recognition/frontend/vite.config.js b/rust/face-recognition/frontend/vite.config.js index cfa898e9d0..8923e7e64c 100644 --- a/rust/face-recognition/frontend/vite.config.js +++ b/rust/face-recognition/frontend/vite.config.js @@ -1,56 +1,61 @@ -import { defineConfig, loadEnv } from "vite"; -import { execSync } from "child_process"; +import { defineConfig } from "vite"; import { icpBindgen } from "@icp-sdk/bindgen/plugins/vite"; +import { execSync } from "child_process"; -function getDevServerConfig() { - // Try icp-cli first - try { - const canisterId = execSync("icp canister status backend -e local -i", { +export default defineConfig(({ command }) => { + const plugins = [ + icpBindgen({ + didFile: "../backend/backend.did", + outDir: "./src/bindings", + }), + ]; + + if (command !== "serve") { + return { plugins }; + } + + const environment = process.env.ICP_ENVIRONMENT || "local"; + const CANISTER_NAME = "backend"; + + const networkStatus = JSON.parse( + execSync(`icp network status -e ${environment} --json`, { encoding: "utf-8", - stdio: "pipe", - }).trim(); - const networkStatus = JSON.parse( - execSync("icp network status --json", { - encoding: "utf-8", - stdio: "pipe", - }) - ); - return { + }) + ); + const rootKey = networkStatus.root_key; + const proxyTarget = networkStatus.api_url; + + let canisterId; + try { + canisterId = execSync( + `icp canister status ${CANISTER_NAME} -e ${environment} -i`, + { encoding: "utf-8" } + ).trim(); + } catch { + console.error(` + Backend canister "${CANISTER_NAME}" not found in environment "${environment}" + + Before running the dev server, deploy the backend canister: + + icp deploy ${CANISTER_NAME} -e ${environment} + `); + process.exit(1); + } + + return { + plugins, + server: { headers: { "Set-Cookie": `ic_env=${encodeURIComponent( - `ic_root_key=${networkStatus.root_key}&PUBLIC_CANISTER_ID:backend=${canisterId}` + `PUBLIC_CANISTER_ID:${CANISTER_NAME}=${canisterId}&ic_root_key=${rootKey}` )}; SameSite=Lax;`, }, proxy: { - "/api": { target: "http://127.0.0.1:8000", changeOrigin: true }, + "/api": { + target: proxyTarget, + changeOrigin: true, + }, }, - }; - } catch {} - - throw new Error( - "No local network running. Start with:\n icp network start -d && icp deploy" - ); -} - -export default defineConfig(({ command, mode }) => { - const env = loadEnv(mode, "..", ["CANISTER_"]); - - return { - base: "./", - plugins: [ - icpBindgen({ - didFile: "../backend/backend.did", - outDir: "./src/bindings", - }), - ], - define: { - "process.env.CANISTER_ID_BACKEND": JSON.stringify( - env.CANISTER_ID_BACKEND - ), - }, - optimizeDeps: { - esbuildOptions: { define: { global: "globalThis" } }, }, - server: command === "serve" ? getDevServerConfig() : undefined, }; }); diff --git a/rust/face-recognition/icp.yaml b/rust/face-recognition/icp.yaml index 4e5178ef5b..8f069295f9 100644 --- a/rust/face-recognition/icp.yaml +++ b/rust/face-recognition/icp.yaml @@ -1,16 +1,11 @@ -networks: - - name: local - mode: managed - canisters: - name: backend build: steps: - type: script commands: - - rustup target add wasm32-wasip1 - - cargo binstall --no-confirm wasi2ic - - cargo binstall --no-confirm wasm-opt + - which wasi2ic || cargo install wasi2ic + - which wasm-opt || cargo install wasm-opt - export RUSTFLAGS="${RUSTFLAGS} -C target-feature=+simd128" - cargo build --package backend --target wasm32-wasip1 --release - TARGET_DIR=$(cargo metadata --format-version 1 --no-deps | sed -n 's/.*"target_directory":"\([^"]*\)".*/\1/p') diff --git a/rust/face-recognition/rust-toolchain.toml b/rust/face-recognition/rust-toolchain.toml index cb53a54b01..eeaa46c3d6 100644 --- a/rust/face-recognition/rust-toolchain.toml +++ b/rust/face-recognition/rust-toolchain.toml @@ -1,4 +1,2 @@ [toolchain] -channel = "1.79" targets = ["wasm32-wasip1"] -components = ["rustfmt", "clippy"] From f154be45fc579618973c2d802f236da456684e72 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Mon, 6 Jul 2026 11:15:55 +0200 Subject: [PATCH 04/31] fix(rust/face-recognition): add missing test image asset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit backend/src/benchmarking.rs embeds image.png via include_bytes! at compile time; the file was not carried over during the src/backend/ → backend/ restructure. Co-Authored-By: Claude Sonnet 4.6 --- rust/face-recognition/backend/assets/image.png | Bin 0 -> 61197 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 rust/face-recognition/backend/assets/image.png diff --git a/rust/face-recognition/backend/assets/image.png b/rust/face-recognition/backend/assets/image.png new file mode 100644 index 0000000000000000000000000000000000000000..f8b5d2c8a18f436cf1b00e4d921dd3b270adcc95 GIT binary patch literal 61197 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 Hu0mjfvX$wM literal 0 HcmV?d00001 From e5df64ae8a660fb4136bee659b2cab68f6b31aaf Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Mon, 6 Jul 2026 11:18:59 +0200 Subject: [PATCH 05/31] fix(rust/face-recognition): use relative path in wasi2ic step TARGET_DIR set in one icp.yaml command line is not visible in the next since each command runs in its own shell. Use ./target/wasm32-wasip1/release/ directly. Co-Authored-By: Claude Sonnet 4.6 --- rust/face-recognition/icp.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rust/face-recognition/icp.yaml b/rust/face-recognition/icp.yaml index 8f069295f9..eb73973e12 100644 --- a/rust/face-recognition/icp.yaml +++ b/rust/face-recognition/icp.yaml @@ -8,8 +8,7 @@ canisters: - which wasm-opt || cargo install wasm-opt - export RUSTFLAGS="${RUSTFLAGS} -C target-feature=+simd128" - cargo build --package backend --target wasm32-wasip1 --release - - TARGET_DIR=$(cargo metadata --format-version 1 --no-deps | sed -n 's/.*"target_directory":"\([^"]*\)".*/\1/p') - - wasi2ic "${TARGET_DIR}/wasm32-wasip1/release/backend.wasm" "$ICP_WASM_OUTPUT_PATH" + - wasi2ic ./target/wasm32-wasip1/release/backend.wasm "$ICP_WASM_OUTPUT_PATH" - wasm-opt -Os --enable-simd --enable-bulk-memory -o "$ICP_WASM_OUTPUT_PATH" "$ICP_WASM_OUTPUT_PATH" - ic-wasm "$ICP_WASM_OUTPUT_PATH" -o "$ICP_WASM_OUTPUT_PATH" metadata candid:service -f backend/backend.did -v public --keep-name-section From 2e14a85fed462eb449b90b815476e76e3bad741b Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Mon, 6 Jul 2026 11:31:31 +0200 Subject: [PATCH 06/31] fix(rust/face-recognition): return Err instead of panicking when models not loaded Replace unwrap() with ok_or_else() in detect() and embedding() so callers get a descriptive Err when setup_models() hasn't been called yet. Co-Authored-By: Claude Sonnet 4.6 --- rust/face-recognition/backend/src/onnx.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/face-recognition/backend/src/onnx.rs b/rust/face-recognition/backend/src/onnx.rs index 0df91951d5..68ab9ff353 100644 --- a/rust/face-recognition/backend/src/onnx.rs +++ b/rust/face-recognition/backend/src/onnx.rs @@ -92,7 +92,7 @@ pub fn setup(facedetect: Bytes, facerec: Bytes) -> TractResult<()> { /// Returns a bounding box around the face detected in the given image. pub fn detect(image: Vec) -> Result<(BoundingBox, f32), anyhow::Error> { FACE_DETECTION.with_borrow(|model| { - let model = model.as_ref().unwrap(); + let model = model.as_ref().ok_or_else(|| anyhow::anyhow!("Face detection model not loaded — call setup_models() first"))?; let image = image::load_from_memory(&image)?.to_rgb8(); // The model accepts an image of size 320x240px. @@ -129,7 +129,7 @@ pub fn detect(image: Vec) -> Result<(BoundingBox, f32), anyhow::Error> { /// Computes a face embedding corresponding to the given image of a face. pub fn embedding(image: Vec) -> Result { FACE_RECOGNITION.with_borrow(|model| { - let model = model.as_ref().unwrap(); + let model = model.as_ref().ok_or_else(|| anyhow::anyhow!("Face recognition model not loaded — call setup_models() first"))?; let image = image::load_from_memory(&image)?.to_rgb8(); // The model accepts an image of size 160x160px. From 3668537e5291e16653443856ebe6ac395a619329 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Mon, 6 Jul 2026 11:35:27 +0200 Subject: [PATCH 07/31] docs(rust/face-recognition): clarify that UI requires model upload before use The previous structure put "Upload the ONNX models" after "Deploy and test" which implied the frontend was usable after deploy. Add an explicit note that the frontend won't work until models are uploaded and setup_models is called. Co-Authored-By: Claude Sonnet 4.6 --- rust/face-recognition/README.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/rust/face-recognition/README.md b/rust/face-recognition/README.md index d243021c64..7e19e30706 100644 --- a/rust/face-recognition/README.md +++ b/rust/face-recognition/README.md @@ -76,18 +76,24 @@ bash test.sh icp network stop ``` -Run `npm run dev` from the `frontend/` directory for hot reload during frontend development. +`bash test.sh` exercises the model management API (clear, append, run without models). The frontend requires models to be uploaded first — see the next step. -### Upload the ONNX models +### Upload the ONNX models and open the frontend -After deploying, upload the ONNX models to the canister using [ic-file-uploader](https://github.com/modclub-app/ic-file-uploader/tree/main): +The frontend will not work until both ONNX models are uploaded to the canister. After deploying, install [ic-file-uploader](https://github.com/modclub-app/ic-file-uploader/tree/main) and run the upload script: ```bash cargo install ic-file-uploader bash upload-models-to-canister.sh ``` -Once the models are uploaded, open the frontend URL in your browser to interact with the smart contract. +This uploads the models chunk by chunk and calls `setup_models` to load them into memory. Once complete, open the frontend URL printed by `icp deploy` in your browser to interact with the smart contract. + +For frontend development with hot reload: + +```bash +npm run dev --prefix frontend +``` ## Credits From c55ba56453fd77a6dab02f3857b55eb9ae15b08d Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Mon, 6 Jul 2026 11:39:12 +0200 Subject: [PATCH 08/31] fix(rust/face-recognition): add models_ready() query, show setup warning in UI - Add models_ready() -> bool query to backend/onnx.rs; exposed via lib.rs - On page load, frontend calls models_ready() and shows an actionable warning banner if models haven't been uploaded yet, rather than failing silently after the user clicks a button - Update ic-file-uploader link to current repo: github.com/decide-ai/ic-file-uploader Co-Authored-By: Claude Sonnet 4.6 --- rust/face-recognition/README.md | 4 ++-- rust/face-recognition/backend/src/lib.rs | 7 +++++++ rust/face-recognition/backend/src/onnx.rs | 4 ++++ rust/face-recognition/frontend/src/index.js | 14 ++++++++++++++ 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/rust/face-recognition/README.md b/rust/face-recognition/README.md index 7e19e30706..14359b7851 100644 --- a/rust/face-recognition/README.md +++ b/rust/face-recognition/README.md @@ -80,7 +80,7 @@ icp network stop ### Upload the ONNX models and open the frontend -The frontend will not work until both ONNX models are uploaded to the canister. After deploying, install [ic-file-uploader](https://github.com/modclub-app/ic-file-uploader/tree/main) and run the upload script: +The frontend will not work until both ONNX models are uploaded to the canister. After deploying, install [ic-file-uploader](https://github.com/decide-ai/ic-file-uploader) and run the upload script: ```bash cargo install ic-file-uploader @@ -97,7 +97,7 @@ npm run dev --prefix frontend ## Credits -Thanks to [DecideAI](https://decideai.xyz/) for discussions and providing [ic-file-uploader](https://github.com/modclub-app/ic-file-uploader/tree/main). +Thanks to [DecideAI](https://decideai.xyz/) for discussions and providing [ic-file-uploader](https://github.com/decide-ai/ic-file-uploader). ## Security considerations and best practices diff --git a/rust/face-recognition/backend/src/lib.rs b/rust/face-recognition/backend/src/lib.rs index 21202ee358..755f098553 100644 --- a/rust/face-recognition/backend/src/lib.rs +++ b/rust/face-recognition/backend/src/lib.rs @@ -119,6 +119,13 @@ fn append_face_recognition_model_bytes(bytes: Vec) { storage::append_bytes(FACE_RECOGNITION_FILE, bytes); } +/// Returns true if both ONNX models have been loaded via setup_models(). +/// The frontend uses this on page load to show a setup warning if models are missing. +#[ic_cdk::query] +fn models_ready() -> bool { + onnx::models_ready() +} + /// Once the model files have been incrementally uploaded, /// this function loads them into in-memory models. #[ic_cdk::update] diff --git a/rust/face-recognition/backend/src/onnx.rs b/rust/face-recognition/backend/src/onnx.rs index 68ab9ff353..9099dfaa00 100644 --- a/rust/face-recognition/backend/src/onnx.rs +++ b/rust/face-recognition/backend/src/onnx.rs @@ -89,6 +89,10 @@ pub fn setup(facedetect: Bytes, facerec: Bytes) -> TractResult<()> { setup_facerec(facerec) } +pub fn models_ready() -> bool { + FACE_DETECTION.with_borrow(|m| m.is_some()) && FACE_RECOGNITION.with_borrow(|m| m.is_some()) +} + /// Returns a bounding box around the face detected in the given image. pub fn detect(image: Vec) -> Result<(BoundingBox, f32), anyhow::Error> { FACE_DETECTION.with_borrow(|model| { diff --git a/rust/face-recognition/frontend/src/index.js b/rust/face-recognition/frontend/src/index.js index d9c6c94b38..5445ff97a1 100644 --- a/rust/face-recognition/frontend/src/index.js +++ b/rust/face-recognition/frontend/src/index.js @@ -1,6 +1,20 @@ import { backend } from "./actor.js"; window.onload = async () => { + // Check if models have been uploaded; show a warning banner if not. + try { + const ready = await backend.models_ready(); + if (!ready) { + message( + "⚠ Models not loaded. Run 'bash upload-models-to-canister.sh' after deploying, then reload this page." + ); + return; + } + } catch (err) { + message("⚠ Could not reach the backend: " + err.toString()); + return; + } + elem("recognize").onclick = recognize; elem("store").onclick = store; elem("file").onchange = load_local_image; From cd375b225c22adfcff838c2a975162938df234c5 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Mon, 6 Jul 2026 11:40:58 +0200 Subject: [PATCH 09/31] fix(rust/face-recognition): add models_ready to backend.did Co-Authored-By: Claude Sonnet 4.6 --- rust/face-recognition/backend/backend.did | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rust/face-recognition/backend/backend.did b/rust/face-recognition/backend/backend.did index 944ed2256f..a52a51e168 100644 --- a/rust/face-recognition/backend/backend.did +++ b/rust/face-recognition/backend/backend.did @@ -38,6 +38,9 @@ service : { "recognize": (image: blob) -> (Recognition); "add": (label: text, image: blob) -> (Addition); + // Returns true once both ONNX models have been loaded via setup_models(). + "models_ready": () -> (bool) query; + // These endpoints are used for incremental uploading of model files. "clear_face_detection_model_bytes": () -> (); "clear_face_recognition_model_bytes": () -> (); From c830998e492ccf8b3917aa54f9da679a26e5a745 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Mon, 6 Jul 2026 11:41:53 +0200 Subject: [PATCH 10/31] docs(rust/face-recognition): add export_candid!() and Candid interface update instructions Co-Authored-By: Claude Sonnet 4.6 --- rust/face-recognition/README.md | 8 ++++++++ rust/face-recognition/backend/src/lib.rs | 2 ++ 2 files changed, 10 insertions(+) diff --git a/rust/face-recognition/README.md b/rust/face-recognition/README.md index 14359b7851..a9817a63eb 100644 --- a/rust/face-recognition/README.md +++ b/rust/face-recognition/README.md @@ -95,6 +95,14 @@ For frontend development with hot reload: npm run dev --prefix frontend ``` +## Updating the Candid interface + +Only needed if you change the backend endpoints. Requires `candid-extractor` (`cargo install candid-extractor`) and `ic_cdk::export_candid!()` at the end of `backend/src/lib.rs` (already present): + +```bash +icp build backend && candid-extractor ./target/wasm32-wasip1/release/backend.wasm > backend/backend.did +``` + ## Credits Thanks to [DecideAI](https://decideai.xyz/) for discussions and providing [ic-file-uploader](https://github.com/decide-ai/ic-file-uploader). diff --git a/rust/face-recognition/backend/src/lib.rs b/rust/face-recognition/backend/src/lib.rs index 755f098553..be0dfcd196 100644 --- a/rust/face-recognition/backend/src/lib.rs +++ b/rust/face-recognition/backend/src/lib.rs @@ -148,3 +148,5 @@ fn post_upgrade() { let wasi_memory = MEMORY_MANAGER.with(|m| m.borrow().get(WASI_MEMORY_ID)); ic_wasi_polyfill::init_with_memory(&[0u8; 32], &[], wasi_memory); } + +ic_cdk::export_candid!(); From 11b68d25e39999169b49000eb5914b2a1efaa2b3 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Mon, 6 Jul 2026 11:43:14 +0200 Subject: [PATCH 11/31] fix(rust/face-recognition): auto-install ic-file-uploader if not present Co-Authored-By: Claude Sonnet 4.6 --- rust/face-recognition/README.md | 3 ++- rust/face-recognition/upload-models-to-canister.sh | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/rust/face-recognition/README.md b/rust/face-recognition/README.md index a9817a63eb..f66d94cc67 100644 --- a/rust/face-recognition/README.md +++ b/rust/face-recognition/README.md @@ -83,10 +83,11 @@ icp network stop The frontend will not work until both ONNX models are uploaded to the canister. After deploying, install [ic-file-uploader](https://github.com/decide-ai/ic-file-uploader) and run the upload script: ```bash -cargo install ic-file-uploader bash upload-models-to-canister.sh ``` +`ic-file-uploader` is installed automatically if not already present. + This uploads the models chunk by chunk and calls `setup_models` to load them into memory. Once complete, open the frontend URL printed by `icp deploy` in your browser to interact with the smart contract. For frontend development with hot reload: diff --git a/rust/face-recognition/upload-models-to-canister.sh b/rust/face-recognition/upload-models-to-canister.sh index fb4057e2f9..f0be82a39f 100755 --- a/rust/face-recognition/upload-models-to-canister.sh +++ b/rust/face-recognition/upload-models-to-canister.sh @@ -1,6 +1,8 @@ #!/bin/bash set -e +which ic-file-uploader || cargo install ic-file-uploader + icp canister call backend clear_face_detection_model_bytes '()' icp canister call backend clear_face_recognition_model_bytes '()' ic-file-uploader backend append_face_detection_model_bytes version-RFB-320.onnx From 8c5c7bc2cb840768971bff47a10f947de8db25e8 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Mon, 6 Jul 2026 11:43:58 +0200 Subject: [PATCH 12/31] fix(rust/face-recognition): check model files exist before uploading Co-Authored-By: Claude Sonnet 4.6 --- rust/face-recognition/upload-models-to-canister.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/rust/face-recognition/upload-models-to-canister.sh b/rust/face-recognition/upload-models-to-canister.sh index f0be82a39f..e5a7040023 100755 --- a/rust/face-recognition/upload-models-to-canister.sh +++ b/rust/face-recognition/upload-models-to-canister.sh @@ -1,6 +1,16 @@ #!/bin/bash set -e +if [ ! -f "version-RFB-320.onnx" ]; then + echo "Face detection model not found. Run ./download-face-detection-model.sh first." + exit 1 +fi + +if [ ! -f "face-recognition.onnx" ]; then + echo "Face recognition model not found. Follow the 'Face recognition' section in README.md to generate face-recognition.onnx." + exit 1 +fi + which ic-file-uploader || cargo install ic-file-uploader icp canister call backend clear_face_detection_model_bytes '()' From 4915e08839026a9518ba75b6dd9c9b0e7f810783 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Mon, 6 Jul 2026 11:48:43 +0200 Subject: [PATCH 13/31] fix(rust/face-recognition): auto-upload models via icp.yaml sync step MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add sync step to icp.yaml that runs upload-models-to-canister.sh after deploy - upload-models-to-canister.sh now: - exits 0 gracefully if model files are missing (CI-safe) - skips upload if models_ready() returns true (idempotent on redeployment) - re-uploads if models were cleared (e.g. after reinstall) - README: remove the separate "Upload models" manual step — icp deploy handles it Co-Authored-By: Claude Sonnet 4.6 --- rust/face-recognition/README.md | 16 +++----------- rust/face-recognition/icp.yaml | 5 +++++ .../upload-models-to-canister.sh | 21 +++++++++++++------ 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/rust/face-recognition/README.md b/rust/face-recognition/README.md index f66d94cc67..a1d0cb6b77 100644 --- a/rust/face-recognition/README.md +++ b/rust/face-recognition/README.md @@ -69,6 +69,8 @@ cd examples/rust/face-recognition ### Deploy and test +If the ONNX model files are present before running `icp deploy`, they are uploaded automatically to the canister as part of the sync phase. `icp deploy` skips the upload on redeployment if the models are already loaded. + ```bash icp network start -d icp deploy @@ -76,19 +78,7 @@ bash test.sh icp network stop ``` -`bash test.sh` exercises the model management API (clear, append, run without models). The frontend requires models to be uploaded first — see the next step. - -### Upload the ONNX models and open the frontend - -The frontend will not work until both ONNX models are uploaded to the canister. After deploying, install [ic-file-uploader](https://github.com/decide-ai/ic-file-uploader) and run the upload script: - -```bash -bash upload-models-to-canister.sh -``` - -`ic-file-uploader` is installed automatically if not already present. - -This uploads the models chunk by chunk and calls `setup_models` to load them into memory. Once complete, open the frontend URL printed by `icp deploy` in your browser to interact with the smart contract. +`bash test.sh` exercises the model management API. The frontend requires the models to be loaded — open the URL printed by `icp deploy` in your browser. If the models are not yet uploaded, the frontend shows a setup instruction. For frontend development with hot reload: diff --git a/rust/face-recognition/icp.yaml b/rust/face-recognition/icp.yaml index eb73973e12..b598e54b5b 100644 --- a/rust/face-recognition/icp.yaml +++ b/rust/face-recognition/icp.yaml @@ -11,6 +11,11 @@ canisters: - wasi2ic ./target/wasm32-wasip1/release/backend.wasm "$ICP_WASM_OUTPUT_PATH" - wasm-opt -Os --enable-simd --enable-bulk-memory -o "$ICP_WASM_OUTPUT_PATH" "$ICP_WASM_OUTPUT_PATH" - ic-wasm "$ICP_WASM_OUTPUT_PATH" -o "$ICP_WASM_OUTPUT_PATH" metadata candid:service -f backend/backend.did -v public --keep-name-section + sync: + steps: + - type: script + commands: + - bash upload-models-to-canister.sh - name: frontend recipe: diff --git a/rust/face-recognition/upload-models-to-canister.sh b/rust/face-recognition/upload-models-to-canister.sh index e5a7040023..d3e068e2c4 100755 --- a/rust/face-recognition/upload-models-to-canister.sh +++ b/rust/face-recognition/upload-models-to-canister.sh @@ -1,14 +1,22 @@ #!/bin/bash set -e -if [ ! -f "version-RFB-320.onnx" ]; then - echo "Face detection model not found. Run ./download-face-detection-model.sh first." - exit 1 +# Skip gracefully if model files haven't been downloaded/generated yet. +# This keeps icp deploy working in CI without the large model files. +if [ ! -f "version-RFB-320.onnx" ] || [ ! -f "face-recognition.onnx" ]; then + echo "Model files not found — skipping upload." + echo "To enable face recognition, first:" + echo " 1. Run ./download-face-detection-model.sh" + echo " 2. Generate face-recognition.onnx (see README.md)" + echo " 3. Run icp deploy again (or bash upload-models-to-canister.sh)" + exit 0 fi -if [ ! -f "face-recognition.onnx" ]; then - echo "Face recognition model not found. Follow the 'Face recognition' section in README.md to generate face-recognition.onnx." - exit 1 +# Skip if models are already loaded (e.g. on redeployment without reinstall). +result=$(icp canister call --query backend models_ready '()' 2>/dev/null || echo "(false)") +if echo "$result" | grep -q 'true'; then + echo "Models already loaded — skipping upload." + exit 0 fi which ic-file-uploader || cargo install ic-file-uploader @@ -18,3 +26,4 @@ icp canister call backend clear_face_recognition_model_bytes '()' ic-file-uploader backend append_face_detection_model_bytes version-RFB-320.onnx ic-file-uploader backend append_face_recognition_model_bytes face-recognition.onnx icp canister call backend setup_models '()' +echo "Models uploaded and loaded successfully." From df1623e3ba8554955821e45b70d3bce47b4e17f1 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Mon, 6 Jul 2026 11:53:08 +0200 Subject: [PATCH 14/31] fix(rust/face-recognition): add debug output and verify setup_models result - Print models_ready result and upload progress so sync phase output is clear - Check setup_models() return value for Err variant (icp canister call exits 0 even when the canister returns Err, so set -e alone doesn't catch failures) Co-Authored-By: Claude Sonnet 4.6 --- .../upload-models-to-canister.sh | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/rust/face-recognition/upload-models-to-canister.sh b/rust/face-recognition/upload-models-to-canister.sh index d3e068e2c4..1b2534247f 100755 --- a/rust/face-recognition/upload-models-to-canister.sh +++ b/rust/face-recognition/upload-models-to-canister.sh @@ -14,6 +14,7 @@ fi # Skip if models are already loaded (e.g. on redeployment without reinstall). result=$(icp canister call --query backend models_ready '()' 2>/dev/null || echo "(false)") +echo "models_ready: $result" if echo "$result" | grep -q 'true'; then echo "Models already loaded — skipping upload." exit 0 @@ -21,9 +22,20 @@ fi which ic-file-uploader || cargo install ic-file-uploader +echo "Uploading face detection model..." icp canister call backend clear_face_detection_model_bytes '()' -icp canister call backend clear_face_recognition_model_bytes '()' ic-file-uploader backend append_face_detection_model_bytes version-RFB-320.onnx + +echo "Uploading face recognition model..." +icp canister call backend clear_face_recognition_model_bytes '()' ic-file-uploader backend append_face_recognition_model_bytes face-recognition.onnx -icp canister call backend setup_models '()' + +echo "Running setup_models..." +setup_result=$(icp canister call backend setup_models '()') +echo "$setup_result" +if echo "$setup_result" | grep -q 'Err'; then + echo "Error: setup_models failed: $setup_result" + exit 1 +fi + echo "Models uploaded and loaded successfully." From 57d09ff495f41dc172d48ec9168001448893ec9f Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Mon, 6 Jul 2026 11:57:35 +0200 Subject: [PATCH 15/31] fix(rust/face-recognition): auto-download face detection model in sync step MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove explicit CI check — the script is naturally CI-safe: - face detection model is auto-downloaded (~1MB, cached on re-runs) - face-recognition.onnx absent in CI → exits 0 with clear message - CI tests pass since they work without loaded models Co-Authored-By: Claude Sonnet 4.6 --- .../upload-models-to-canister.sh | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/rust/face-recognition/upload-models-to-canister.sh b/rust/face-recognition/upload-models-to-canister.sh index 1b2534247f..7086b3ab24 100755 --- a/rust/face-recognition/upload-models-to-canister.sh +++ b/rust/face-recognition/upload-models-to-canister.sh @@ -1,14 +1,17 @@ #!/bin/bash set -e -# Skip gracefully if model files haven't been downloaded/generated yet. -# This keeps icp deploy working in CI without the large model files. -if [ ! -f "version-RFB-320.onnx" ] || [ ! -f "face-recognition.onnx" ]; then - echo "Model files not found — skipping upload." - echo "To enable face recognition, first:" - echo " 1. Run ./download-face-detection-model.sh" - echo " 2. Generate face-recognition.onnx (see README.md)" - echo " 3. Run icp deploy again (or bash upload-models-to-canister.sh)" +# Auto-download the face detection model if not already present. +if [ ! -f "version-RFB-320.onnx" ]; then + echo "Face detection model not found — downloading..." + bash download-face-detection-model.sh +fi + +# The face recognition model must be generated manually (requires Python + PyTorch). +# See the README for instructions. +if [ ! -f "face-recognition.onnx" ]; then + echo "Face recognition model not found — skipping upload." + echo "Generate face-recognition.onnx (see README.md) and run icp deploy again." exit 0 fi From fa2dd2ef589d545bd4f2849473ce565846992f8e Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Mon, 6 Jul 2026 12:03:39 +0200 Subject: [PATCH 16/31] fix(rust/face-recognition): fully automate model generation in sync step If python3 is available, auto-install facenet-pytorch/torch/onnx and generate face-recognition.onnx. If python3 is absent (e.g. minimal CI container), skip gracefully. README updated to reflect that icp deploy handles everything. Co-Authored-By: Claude Sonnet 4.6 --- rust/face-recognition/README.md | 41 ++----------------- .../upload-models-to-canister.sh | 21 +++++++--- 2 files changed, 20 insertions(+), 42 deletions(-) diff --git a/rust/face-recognition/README.md b/rust/face-recognition/README.md index a1d0cb6b77..c81224b69a 100644 --- a/rust/face-recognition/README.md +++ b/rust/face-recognition/README.md @@ -9,45 +9,12 @@ The smart contract consists of two canisters: ## Models -The smart contract uses two models: one for detecting the face and another for recognizing the face. +The smart contract uses two models: one for detecting faces and another for recognizing them. -Since the models are large, they cannot be embedded into the Wasm binary of the smart contract. Instead they must be uploaded separately after deployment. +Since the models are large they cannot be embedded in the Wasm binary — they are uploaded to the canister after deployment. The `icp deploy` sync phase handles this automatically: -### Face detection - -A face detection model finds the bounding box of a face in the image. -You can download [Ultraface](https://github.com/onnx/models/tree/main/validated/vision/body_analysis/ultraface) — ultra-lightweight face detection model — by running: - -```bash -./download-face-detection-model.sh -``` - -### Face recognition - -A face recognition model computes a vector embedding of an image with a face. -You can obtain a pretrained model from [facenet-pytorch](https://github.com/timesler/facenet-pytorch) as follows. - -1. Install `python` and `pip`: https://packaging.python.org/en/latest/tutorials/installing-packages/ - -2. Install `facenet-pytorch`, `torch`, and `onnx`: - - ```bash - pip install facenet-pytorch - pip install torch - pip install onnx - ``` - -3. Export the ONNX model. Start a Python shell and run: - - ```python - import torch - import facenet_pytorch - resnet = facenet_pytorch.InceptionResnetV1(pretrained='vggface2').eval() - input = torch.randn(1, 3, 160, 160) - torch.onnx.export(resnet, input, "face-recognition.onnx", verbose=False, opset_version=11) - ``` - - This produces `face-recognition.onnx`. Copy the file to the root of this repository. +- **Face detection** ([Ultraface](https://github.com/onnx/models/tree/main/validated/vision/body_analysis/ultraface)) — downloaded automatically. +- **Face recognition** ([facenet-pytorch](https://github.com/timesler/facenet-pytorch) InceptionResnetV1) — generated automatically if `python3` is available (installs `facenet-pytorch`, `torch`, and `onnx` via pip). If `python3` is not available, place `face-recognition.onnx` in the project root manually and run `icp deploy` again. ## Build and deploy from the command line diff --git a/rust/face-recognition/upload-models-to-canister.sh b/rust/face-recognition/upload-models-to-canister.sh index 7086b3ab24..16ad6d329f 100755 --- a/rust/face-recognition/upload-models-to-canister.sh +++ b/rust/face-recognition/upload-models-to-canister.sh @@ -7,12 +7,23 @@ if [ ! -f "version-RFB-320.onnx" ]; then bash download-face-detection-model.sh fi -# The face recognition model must be generated manually (requires Python + PyTorch). -# See the README for instructions. +# Auto-generate the face recognition model if not already present. if [ ! -f "face-recognition.onnx" ]; then - echo "Face recognition model not found — skipping upload." - echo "Generate face-recognition.onnx (see README.md) and run icp deploy again." - exit 0 + if which python3 >/dev/null 2>&1; then + echo "Face recognition model not found — generating (this may take a few minutes)..." + python3 -m pip install --quiet facenet-pytorch torch onnx + python3 << 'PYEOF' +import torch +import facenet_pytorch +resnet = facenet_pytorch.InceptionResnetV1(pretrained='vggface2').eval() +torch.onnx.export(resnet, torch.randn(1, 3, 160, 160), 'face-recognition.onnx', verbose=False, opset_version=11) +print("face-recognition.onnx generated.") +PYEOF + else + echo "Face recognition model not found and python3 is not available — skipping upload." + echo "Install Python3 or generate face-recognition.onnx manually (see README.md)." + exit 0 + fi fi # Skip if models are already loaded (e.g. on redeployment without reinstall). From 666b1091205a09a65e2d3126c50ec24a3d763332 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Mon, 6 Jul 2026 12:14:54 +0200 Subject: [PATCH 17/31] fix(rust/face-recognition): use --prefer-binary for pip install Python 3.14 can't build Pillow from source (facenet-pytorch dep); --prefer-binary downloads pre-built wheels instead. Co-Authored-By: Claude Sonnet 4.6 --- rust/face-recognition/upload-models-to-canister.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/face-recognition/upload-models-to-canister.sh b/rust/face-recognition/upload-models-to-canister.sh index 16ad6d329f..3ab6eb1723 100755 --- a/rust/face-recognition/upload-models-to-canister.sh +++ b/rust/face-recognition/upload-models-to-canister.sh @@ -11,7 +11,7 @@ fi if [ ! -f "face-recognition.onnx" ]; then if which python3 >/dev/null 2>&1; then echo "Face recognition model not found — generating (this may take a few minutes)..." - python3 -m pip install --quiet facenet-pytorch torch onnx + python3 -m pip install --quiet --prefer-binary facenet-pytorch torch onnx python3 << 'PYEOF' import torch import facenet_pytorch From c1e62a388135ec812a87f0421f4faa78d85f2fcc Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Mon, 6 Jul 2026 13:02:43 +0200 Subject: [PATCH 18/31] fix(rust/face-recognition): stable memory reload, full model automation, UI alignment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - post_upgrade: reload models from stable memory automatically so upgrades don't require re-uploading 100MB+ of model data - upload-models-to-canister.sh: try setup_models() before re-uploading (fast path for upgrades); get replica URL from icp network status; pass canister ID to ic-file-uploader (no dfx.json needed); detect Python 3.9-3.12 for torch compat; use certifi for SSL on macOS python.org installs; auto-download face detection model; auto-generate face recognition model if compatible Python available - .gitignore: replace stale backend/assets/*.onnx entries with *.onnx (models live in project root and should never be committed) - vite.config.js: align exactly with canonical hello_world pattern (const server) - onnx.rs: add comment that DB is heap memory, cleared on upgrade - README: remove "smart contract" jargon; add optional Python 3.9-3.12 prerequisite; document the 3 deploy phases; explain model persistence vs persons database - test.sh: make Test 4 context-aware — asserts Ok when models are loaded (local) and Err when they are not (CI) Co-Authored-By: Claude Sonnet 4.6 --- rust/face-recognition/.gitignore | 4 +- rust/face-recognition/README.md | 44 +++++++++++----- rust/face-recognition/backend/src/lib.rs | 6 +++ rust/face-recognition/backend/src/onnx.rs | 2 + rust/face-recognition/frontend/vite.config.js | 32 +++++++----- rust/face-recognition/test.sh | 17 +++++-- .../upload-models-to-canister.sh | 51 +++++++++++++++---- 7 files changed, 113 insertions(+), 43 deletions(-) diff --git a/rust/face-recognition/.gitignore b/rust/face-recognition/.gitignore index 139cd79f69..c7fc1f0666 100644 --- a/rust/face-recognition/.gitignore +++ b/rust/face-recognition/.gitignore @@ -7,7 +7,7 @@ _MACOSX target/ *.old.did .idea -backend/assets/version-RFB-320.onnx -backend/assets/facerec.onnx +# ONNX model files are downloaded/generated at runtime — do not commit +*.onnx frontend/src/bindings/ .icp/cache/ diff --git a/rust/face-recognition/README.md b/rust/face-recognition/README.md index c81224b69a..e8445d13cd 100644 --- a/rust/face-recognition/README.md +++ b/rust/face-recognition/README.md @@ -1,25 +1,27 @@ # ICP face recognition -This is an ICP smart contract that runs face detection and face recognition on user photos that can be uploaded either from a camera or a local file. +This example demonstrates running face detection and face recognition inside an ICP canister using the [Tract ONNX inference engine](https://github.com/sonos/tract). Users can upload photos from a camera or local file, detect faces, and identify people by name. -The smart contract consists of two canisters: +The example consists of two canisters: -- The **backend canister** embeds the [Tract ONNX inference engine](https://github.com/sonos/tract) with two ONNX models. One model is used to detect a face in the photo and return its bounding box. Another model is used for computing face embeddings. -- The **frontend canister** contains the Web assets such as HTML, JS, and CSS that are served to the browser. +- **backend** — embeds the Tract ONNX inference engine. Exposes endpoints for uploading ONNX model files in chunks, loading them into memory, detecting faces, computing face embeddings, and recognizing people. +- **frontend** — serves the web UI (HTML/JS/CSS). ## Models -The smart contract uses two models: one for detecting faces and another for recognizing them. - -Since the models are large they cannot be embedded in the Wasm binary — they are uploaded to the canister after deployment. The `icp deploy` sync phase handles this automatically: +The backend uses two ONNX models that are too large to embed in the Wasm binary and must be uploaded after deployment. `icp deploy` handles this automatically via its sync phase: - **Face detection** ([Ultraface](https://github.com/onnx/models/tree/main/validated/vision/body_analysis/ultraface)) — downloaded automatically. -- **Face recognition** ([facenet-pytorch](https://github.com/timesler/facenet-pytorch) InceptionResnetV1) — generated automatically if `python3` is available (installs `facenet-pytorch`, `torch`, and `onnx` via pip). If `python3` is not available, place `face-recognition.onnx` in the project root manually and run `icp deploy` again. +- **Face recognition** ([facenet-pytorch](https://github.com/timesler/facenet-pytorch) InceptionResnetV1) — generated automatically if Python 3.9–3.12 is available (`facenet-pytorch`, `torch`, and `onnx` are installed via pip). If no compatible Python is found, place `face-recognition.onnx` in the project root manually and run `icp deploy` again. + +Models are stored in stable memory and survive canister upgrades — they reload automatically without re-uploading. Note: the persons database (added via the frontend) is stored in heap memory and is cleared on upgrade. ## Build and deploy from the command line ### Prerequisites +**Required:** + - [Node.js](https://nodejs.org/) v18+ - [icp-cli](https://cli.internetcomputer.org/): `npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm` - [Rust](https://www.rust-lang.org/tools/install) v1.85+ with `wasm32-wasip1` target: `rustup target add wasm32-wasip1` @@ -27,6 +29,10 @@ Since the models are large they cannot be embedded in the Wasm binary — they a `wasm-opt` is installed automatically on first deploy if not already present. +**Optional (for automatic face recognition model generation):** + +- Python 3.9–3.12 with pip — the sync phase auto-installs `facenet-pytorch`, `torch`, and `onnx` and generates `face-recognition.onnx`. Python 3.13+ is not yet supported by torch. + ### Install ```bash @@ -34,18 +40,28 @@ git clone https://github.com/dfinity/examples cd examples/rust/face-recognition ``` -### Deploy and test - -If the ONNX model files are present before running `icp deploy`, they are uploaded automatically to the canister as part of the sync phase. `icp deploy` skips the upload on redeployment if the models are already loaded. +### Deploy ```bash icp network start -d icp deploy -bash test.sh icp network stop ``` -`bash test.sh` exercises the model management API. The frontend requires the models to be loaded — open the URL printed by `icp deploy` in your browser. If the models are not yet uploaded, the frontend shows a setup instruction. +`icp deploy` runs three phases: +1. **Build** — compiles the Rust backend to WASM (via wasm32-wasip1 + wasi2ic). +2. **Deploy** — installs the backend and frontend canisters. +3. **Sync** — downloads the face detection model, generates the face recognition model (if Python 3.9–3.12 is available), and uploads both to the canister. Skipped on redeployment if models are already loaded. + +After deployment the CLI prints the frontend URL. Open it in a browser to interact with the canister. + +### Test + +```bash +bash test.sh +``` + +`test.sh` exercises the model management API without requiring models to be loaded. The frontend shows a setup instruction if models are not yet uploaded. For frontend development with hot reload: @@ -55,7 +71,7 @@ npm run dev --prefix frontend ## Updating the Candid interface -Only needed if you change the backend endpoints. Requires `candid-extractor` (`cargo install candid-extractor`) and `ic_cdk::export_candid!()` at the end of `backend/src/lib.rs` (already present): +Only needed if you change the backend endpoints. Requires `candid-extractor` (`cargo install candid-extractor`): ```bash icp build backend && candid-extractor ./target/wasm32-wasip1/release/backend.wasm > backend/backend.did diff --git a/rust/face-recognition/backend/src/lib.rs b/rust/face-recognition/backend/src/lib.rs index be0dfcd196..7b3906261d 100644 --- a/rust/face-recognition/backend/src/lib.rs +++ b/rust/face-recognition/backend/src/lib.rs @@ -147,6 +147,12 @@ fn init() { fn post_upgrade() { let wasi_memory = MEMORY_MANAGER.with(|m| m.borrow().get(WASI_MEMORY_ID)); ic_wasi_polyfill::init_with_memory(&[0u8; 32], &[], wasi_memory); + // Reload models from stable memory if they were uploaded before this upgrade. + // Silently ignored if the files don't exist yet (first deployment). + let _ = setup( + storage::bytes(FACE_DETECTION_FILE), + storage::bytes(FACE_RECOGNITION_FILE), + ); } ic_cdk::export_candid!(); diff --git a/rust/face-recognition/backend/src/onnx.rs b/rust/face-recognition/backend/src/onnx.rs index 9099dfaa00..05dbad2bd9 100644 --- a/rust/face-recognition/backend/src/onnx.rs +++ b/rust/face-recognition/backend/src/onnx.rs @@ -15,6 +15,8 @@ type Model = SimplePlan, Graph> = RefCell::new(None); static FACE_RECOGNITION: RefCell> = RefCell::new(None); + // Stored in heap memory — cleared on canister upgrade. For a production + // system, persist DB to stable memory via ic-stable-structures. static DB: RefCell> = RefCell::new(vec![]); } diff --git a/rust/face-recognition/frontend/vite.config.js b/rust/face-recognition/frontend/vite.config.js index 8923e7e64c..d804fa696d 100644 --- a/rust/face-recognition/frontend/vite.config.js +++ b/rust/face-recognition/frontend/vite.config.js @@ -10,10 +10,13 @@ export default defineConfig(({ command }) => { }), ]; + // If we're only building this is enough if (command !== "serve") { return { plugins }; } + // If we're running the local npm dev server, we're going to look up the + // local network's root key and the relevant canister ids. const environment = process.env.ICP_ENVIRONMENT || "local"; const CANISTER_NAME = "backend"; @@ -25,6 +28,7 @@ export default defineConfig(({ command }) => { const rootKey = networkStatus.root_key; const proxyTarget = networkStatus.api_url; + // Backend must be deployed before starting dev server let canisterId; try { canisterId = execSync( @@ -42,20 +46,22 @@ export default defineConfig(({ command }) => { process.exit(1); } - return { - plugins, - server: { - headers: { - "Set-Cookie": `ic_env=${encodeURIComponent( - `PUBLIC_CANISTER_ID:${CANISTER_NAME}=${canisterId}&ic_root_key=${rootKey}` - )}; SameSite=Lax;`, - }, - proxy: { - "/api": { - target: proxyTarget, - changeOrigin: true, - }, + const server = { + headers: { + "Set-Cookie": `ic_env=${encodeURIComponent( + `PUBLIC_CANISTER_ID:${CANISTER_NAME}=${canisterId}&ic_root_key=${rootKey}` + )}; SameSite=Lax;`, + }, + proxy: { + "/api": { + target: proxyTarget, + changeOrigin: true, }, }, }; + + return { + plugins, + server, + }; }); diff --git a/rust/face-recognition/test.sh b/rust/face-recognition/test.sh index 8bc59f47a0..fec511c352 100755 --- a/rust/face-recognition/test.sh +++ b/rust/face-recognition/test.sh @@ -16,8 +16,15 @@ result=$(icp canister call backend append_face_detection_model_bytes '(blob "\00 echo "$result" && \ echo "PASS" || (echo "FAIL" && exit 1) -echo "=== Test 4: run_detection returns Err when model is not set up ===" -result=$(icp canister call --query backend run_detection '()') && \ - echo "$result" && \ - echo "$result" | grep -q 'Err' && \ - echo "PASS" || (echo "FAIL" && exit 1) +echo "=== Test 4: run_detection result depends on whether models are loaded ===" +# models_ready() is false in CI (no model files) and true locally after sync. +# Clearing the byte storage does not unload in-memory models — so locally +# models remain loaded and detection succeeds; in CI it returns Err. +ready=$(icp canister call --query backend models_ready '()' | grep -c 'true' || true) +result=$(icp canister call --query backend run_detection '()') +echo "$result" +if [ "$ready" -gt 0 ]; then + echo "$result" | grep -q 'Ok' && echo "PASS (models loaded — detection succeeded)" || (echo "FAIL" && exit 1) +else + echo "$result" | grep -q 'Err' && echo "PASS (no models — expected error returned)" || (echo "FAIL" && exit 1) +fi diff --git a/rust/face-recognition/upload-models-to-canister.sh b/rust/face-recognition/upload-models-to-canister.sh index 3ab6eb1723..f523fbf130 100755 --- a/rust/face-recognition/upload-models-to-canister.sh +++ b/rust/face-recognition/upload-models-to-canister.sh @@ -9,10 +9,27 @@ fi # Auto-generate the face recognition model if not already present. if [ ! -f "face-recognition.onnx" ]; then - if which python3 >/dev/null 2>&1; then - echo "Face recognition model not found — generating (this may take a few minutes)..." - python3 -m pip install --quiet --prefer-binary facenet-pytorch torch onnx - python3 << 'PYEOF' + # torch only supports Python 3.9-3.12; 3.13+ has no wheels yet. + PYTHON="" + for py in python3.12 python3.11 python3.10 python3.9 python3; do + if which "$py" >/dev/null 2>&1; then + minor=$("$py" -c "import sys; print(sys.version_info.minor)" 2>/dev/null || echo "99") + if [ "$minor" -le 12 ]; then + PYTHON="$py" + break + fi + fi + done + + if [ -n "$PYTHON" ]; then + echo "Face recognition model not found — generating with $PYTHON (this may take a few minutes)..." + "$PYTHON" -m pip install --quiet --prefer-binary facenet-pytorch torch onnx certifi + "$PYTHON" << 'PYEOF' +import os, ssl, certifi +# Python from python.org on macOS doesn't ship with root certificates; +# point urllib at the certifi bundle so pretrained weights can be downloaded. +os.environ['SSL_CERT_FILE'] = certifi.where() +ssl.create_default_context = lambda *a, **kw: ssl.create_default_context(cafile=certifi.where()) import torch import facenet_pytorch resnet = facenet_pytorch.InceptionResnetV1(pretrained='vggface2').eval() @@ -20,13 +37,14 @@ torch.onnx.export(resnet, torch.randn(1, 3, 160, 160), 'face-recognition.onnx', print("face-recognition.onnx generated.") PYEOF else - echo "Face recognition model not found and python3 is not available — skipping upload." - echo "Install Python3 or generate face-recognition.onnx manually (see README.md)." + echo "Face recognition model not found — skipping upload." + echo "torch requires Python 3.9-3.12. Install it (e.g. 'brew install python@3.12')" + echo "or generate face-recognition.onnx manually (see README.md)." exit 0 fi fi -# Skip if models are already loaded (e.g. on redeployment without reinstall). +# Skip if models are already loaded. result=$(icp canister call --query backend models_ready '()' 2>/dev/null || echo "(false)") echo "models_ready: $result" if echo "$result" | grep -q 'true'; then @@ -34,15 +52,30 @@ if echo "$result" | grep -q 'true'; then exit 0 fi +# If the canister was upgraded (not reinstalled), model files are still in stable +# memory but heap state was cleared. Calling setup_models() reloads them without +# re-uploading 100MB+. +setup_result=$(icp canister call backend setup_models '()' 2>/dev/null || echo "(variant { Err })") +if ! echo "$setup_result" | grep -q 'Err'; then + echo "Models reloaded from stable memory (no re-upload needed)." + exit 0 +fi + +echo "Models not in stable memory — uploading..." + which ic-file-uploader || cargo install ic-file-uploader +REPLICA_URL=$(icp network status --json | python3 -c "import sys,json; print(json.load(sys.stdin)['api_url'])") +BACKEND_ID=$(icp canister status backend -i) +echo "Backend canister: $BACKEND_ID" + echo "Uploading face detection model..." icp canister call backend clear_face_detection_model_bytes '()' -ic-file-uploader backend append_face_detection_model_bytes version-RFB-320.onnx +ic-file-uploader -n "$REPLICA_URL" "$BACKEND_ID" append_face_detection_model_bytes version-RFB-320.onnx echo "Uploading face recognition model..." icp canister call backend clear_face_recognition_model_bytes '()' -ic-file-uploader backend append_face_recognition_model_bytes face-recognition.onnx +ic-file-uploader -n "$REPLICA_URL" "$BACKEND_ID" append_face_recognition_model_bytes face-recognition.onnx echo "Running setup_models..." setup_result=$(icp canister call backend setup_models '()') From 09d79994577d3555569259eb5d37c0dd0c74056e Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Mon, 6 Jul 2026 13:03:44 +0200 Subject: [PATCH 19/31] fix(rust/face-recognition): remove "Onchain" jargon from frontend title and heading Co-Authored-By: Claude Sonnet 4.6 --- rust/face-recognition/frontend/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/face-recognition/frontend/index.html b/rust/face-recognition/frontend/index.html index eee938a0e5..7f121382ca 100644 --- a/rust/face-recognition/frontend/index.html +++ b/rust/face-recognition/frontend/index.html @@ -4,7 +4,7 @@ - Onchain ICP face recognition + ICP face recognition @@ -14,7 +14,7 @@
-

Onchain ICP face recognition

+

ICP face recognition