diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 41066910a..d709fddd7 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -20,7 +20,7 @@ jobs:
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
- uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
+ uses: docker/metadata-action@v5
with:
images: joepmeneer/atomic-server
github-token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
index 2b0f37c7f..df9483cdc 100644
--- a/.vscode/extensions.json
+++ b/.vscode/extensions.json
@@ -4,6 +4,8 @@
"vadimcn.vscode-lldb",
"rust-lang.rust-analyzer",
"styled-components.vscode-styled-components",
- "svelte.svelte-vscode"
+ "svelte.svelte-vscode",
+ "vadimcn.vscode-lldb",
+ "rust-lang.rust-analyzer"
]
}
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 6ef8e7ba9..04efc0c7c 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,4 +1,8 @@
{
+ // This is used to prevent `build.rs` from running every time you make a change to a file.
+ "rust-analyzer.check.extraEnv": {
+ "IS_RUST_ANALYZER": "true"
+ },
// The linter in the CI is quite strict, so running `cargo fmt` on save is probably a good idea!
"editor.formatOnSave": true,
"files.autoSave": "onFocusChange",
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index 223a84826..1e14f6722 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -15,6 +15,23 @@
"group": "build",
"problemMatcher": []
},
+ {
+ "label": "watch atomic-server (cargo watch)",
+ "type": "shell",
+ "command": "~/.cargo/bin/cargo-watch",
+ "args": [
+ "--",
+ "cargo",
+ "run",
+ "--bin",
+ "atomic-server",
+ "--",
+ "--env-file",
+ "server/.env",
+ ],
+ "group": "build",
+ "problemMatcher": []
+ },
{
"label": "test atomic-server (cargo nextest run)",
"type": "shell",
@@ -26,9 +43,9 @@
"group": "test"
},
{
- "label": "test end-to-end / E2E (npm playwright)",
+ "label": "test end-to-end / E2E (pnpm playwright)",
"type": "shell",
- "command": "cd server/e2e_tests/ && npm i && npm run test",
+ "command": "cd server/e2e_tests/ && pnpm i && pnpm run test",
"group": "test"
},
{
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 55defc6a0..79591ae07 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -86,17 +86,25 @@ See [STATUS.md](server/STATUS.md) to learn more about which features will remain
- Refactor `Endpoint` handlers, uses a Context now #592
- Re-build store + invite when adjusting server url #607
- Use local atomic-server for properties and classes, improves atomic-server #604
+- New sign up / register flow. Add `/register` Endpoint #489 #254
+- New sign up / register flow. Add `/register`, `/confirm-email`, `/add-public-key` endpoints #489 #254
+- Add multi-tenancy support. Users can create their own `Drives` on subdomains. #288
+- Refactor URLs. `store.self_url()` returns an `AtomicUrl`, which provides methods to easily add paths, find subdomains and more.
+- Add support for subdomains, use a Wildcard TLS certificate #502
## [v0.34.1] - 2023-02-11
-
- Improve query performance, refactor indexes. The `.tpf` API is deprecated in favor of the more powerful `.query`. #529
- Replace `acme_lib` with `instant-acme`, drop OpenSSL dependency, add DNS verification for TLS option with `--https-dns` #192
- Improved error handling for HTTPS initialization #530
- Add `--force` to `atomic-server import` #536
+- Email support. Connect to external SMTP servers. #276
+- Basic plugin support through Endpoints. For now only works if you use `**Atomic**-Lib` as a library. Add your plugins by calling `Db::register_endpoint`.
+- Allow parsing `.env` files from custom locations using the `--env-file` flag.
+- Plugins support `tokio`, so you can spawn async tasks from plugins.
+- Add JWT token support, used for emails and registration #544
- Fix index issue happening when deleting a single property in a sorted collection #545
- Update JS assets & playwright
- Fix initial indexing bug #560
-- Fix errors on succesful export / import #565
- Fix envs for store path, change `ATOMIC_STORE_DIR` to `ATOMIC_DATA_DIR` #567
- Refactor static file asset hosting #578
- Meta tags server side #577
@@ -104,6 +112,10 @@ See [STATUS.md](server/STATUS.md) to learn more about which features will remain
- Remove feature to index external RDF files and search them #579
- Add staging environment #588
- Add systemd instructions to readme #271
+- Improve check_append error #558
+- Fix errors on successful export / import #565
+- Most Collection routes now live under `/collections`, e.g. `/collections/agents` instead of `/agents`. #556
+- Constrain new URLs. Commits for new Resources are now only valid if their parent is part of the current URL. So it's no longer possible to create `/some-path/new-resource` if `new-resource` is its parent is not in its URL. #556
## [v0.34.0] - 2022-10-31
@@ -114,6 +126,7 @@ See [STATUS.md](server/STATUS.md) to learn more about which features will remain
- `Store::all_resources` returns `Iterator` instead of `Vec` #522 #487
- Change authentication order #525
- Fix cookie subject check #525
+- Refactored Subject URLs to use `AtomicUrl`
## [v0.33.1] - 2022-09-25
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 53fa38035..ce41983eb 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -49,6 +49,8 @@ TL;DR Clone the repo and run `cargo run` from each folder (e.g. `cli` or `server
- Go to `browser`, run `pnpm install` (if you haven't already), and run `pnpm dev` to start the browser
- Visit your `localhost` in your locally running `atomic-data-browser` instance: (e.g. `http://localhost:5173/app/show?subject=http%3A%2F%2Flocalhost`)
- use `cargo watch -- cargo run` to automatically recompile `atomic-server` when you update JS assets in `browser`
+- use `cargo watch -- cargo run --bin atomic-server -- --env-file server/.env` to automatically recompile `atomic-server` when you update code or JS assets.
+- If you want to debug emails: `brew install mailhog` => `mailhog` => `http://localhost:8025` and add `ATOMIC_SMTP_HOST=localhost` `ATOMIC_SMTP_PORT=1025` to your `.env`.
### IDE setup (VSCode)
diff --git a/Cargo.lock b/Cargo.lock
index 1ede40ce3..0082432cc 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -541,6 +541,15 @@ dependencies = [
"wait-timeout",
]
+[[package]]
+name = "async-mutex"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e"
+dependencies = [
+ "event-listener",
+]
+
[[package]]
name = "async-trait"
version = "0.1.83"
@@ -617,6 +626,7 @@ dependencies = [
"tracing-opentelemetry",
"tracing-subscriber",
"ureq",
+ "url",
"urlencoding",
"walkdir",
"webp",
@@ -626,15 +636,18 @@ dependencies = [
name = "atomic_lib"
version = "0.40.0"
dependencies = [
+ "async-mutex",
"base64 0.21.7",
"bincode",
"criterion",
"directories",
"html2md",
"iai",
+ "jwt-simple",
"kuchikiki",
"lazy_static",
"lol_html",
+ "mail-send",
"ntest",
"rand 0.8.5",
"regex",
@@ -645,6 +658,7 @@ dependencies = [
"serde_jcs",
"serde_json",
"sled",
+ "tokio",
"toml",
"tracing",
"ulid",
@@ -697,6 +711,18 @@ dependencies = [
"windows-targets 0.52.6",
]
+[[package]]
+name = "base16ct"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
+
+[[package]]
+name = "base64"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
+
[[package]]
name = "base64"
version = "0.21.7"
@@ -709,6 +735,12 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+[[package]]
+name = "base64ct"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
+
[[package]]
name = "bincode"
version = "1.3.3"
@@ -718,6 +750,12 @@ dependencies = [
"serde",
]
+[[package]]
+name = "binstring"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e0d60973d9320722cb1206f412740e162a33b8547ea8d6be75d7cff237c7a85"
+
[[package]]
name = "bit_field"
version = "0.10.2"
@@ -985,6 +1023,17 @@ dependencies = [
"winapi",
]
+[[package]]
+name = "coarsetime"
+version = "0.1.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13b3839cf01bb7960114be3ccf2340f541b6d0c81f8690b007b2b39f750f7e5d"
+dependencies = [
+ "libc",
+ "wasix",
+ "wasm-bindgen",
+]
+
[[package]]
name = "color_quant"
version = "1.1.0"
@@ -1030,6 +1079,18 @@ dependencies = [
"windows-sys 0.52.0",
]
+[[package]]
+name = "const-oid"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3"
+
+[[package]]
+name = "const-oid"
+version = "0.9.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
+
[[package]]
name = "convert_case"
version = "0.4.0"
@@ -1179,6 +1240,28 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
+[[package]]
+name = "crypto-bigint"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21"
+dependencies = [
+ "generic-array",
+ "subtle",
+]
+
+[[package]]
+name = "crypto-bigint"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
+dependencies = [
+ "generic-array",
+ "rand_core 0.6.4",
+ "subtle",
+ "zeroize",
+]
+
[[package]]
name = "crypto-common"
version = "0.1.6"
@@ -1216,6 +1299,12 @@ dependencies = [
"syn 2.0.79",
]
+[[package]]
+name = "ct-codecs"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "026ac6ceace6298d2c557ef5ed798894962296469ec7842288ea64674201a2d1"
+
[[package]]
name = "darling"
version = "0.20.10"
@@ -1251,6 +1340,39 @@ dependencies = [
"syn 2.0.79",
]
+[[package]]
+name = "der"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c"
+dependencies = [
+ "const-oid 0.7.1",
+ "crypto-bigint 0.3.2",
+ "pem-rfc7468 0.3.1",
+]
+
+[[package]]
+name = "der"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de"
+dependencies = [
+ "const-oid 0.9.6",
+ "pem-rfc7468 0.6.0",
+ "zeroize",
+]
+
+[[package]]
+name = "der"
+version = "0.7.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0"
+dependencies = [
+ "const-oid 0.9.6",
+ "pem-rfc7468 0.7.0",
+ "zeroize",
+]
+
[[package]]
name = "deranged"
version = "0.3.11"
@@ -1300,7 +1422,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
+ "const-oid 0.9.6",
"crypto-common",
+ "subtle",
]
[[package]]
@@ -1397,6 +1521,30 @@ dependencies = [
"dtoa",
]
+[[package]]
+name = "ecdsa"
+version = "0.16.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca"
+dependencies = [
+ "der 0.7.9",
+ "digest",
+ "elliptic-curve",
+ "rfc6979",
+ "signature 2.2.0",
+ "spki 0.7.3",
+]
+
+[[package]]
+name = "ed25519-compact"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e9b3460f44bea8cd47f45a0c70892f1eff856d97cd55358b2f73f663789f6190"
+dependencies = [
+ "ct-codecs",
+ "getrandom 0.2.15",
+]
+
[[package]]
name = "edit"
version = "0.1.5"
@@ -1413,6 +1561,27 @@ version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
+[[package]]
+name = "elliptic-curve"
+version = "0.13.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
+dependencies = [
+ "base16ct",
+ "crypto-bigint 0.5.5",
+ "digest",
+ "ff",
+ "generic-array",
+ "group",
+ "hkdf",
+ "pem-rfc7468 0.7.0",
+ "pkcs8 0.10.2",
+ "rand_core 0.6.4",
+ "sec1",
+ "subtle",
+ "zeroize",
+]
+
[[package]]
name = "encode_unicode"
version = "0.3.6"
@@ -1460,6 +1629,12 @@ dependencies = [
"str-buf",
]
+[[package]]
+name = "event-listener"
+version = "2.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
+
[[package]]
name = "exr"
version = "1.72.0"
@@ -1508,6 +1683,16 @@ dependencies = [
"simd-adler32",
]
+[[package]]
+name = "ff"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449"
+dependencies = [
+ "rand_core 0.6.4",
+ "subtle",
+]
+
[[package]]
name = "flate2"
version = "1.0.34"
@@ -1678,6 +1863,27 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
+ "zeroize",
+]
+
+[[package]]
+name = "gethostname"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "gethostname"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818"
+dependencies = [
+ "libc",
+ "windows-targets 0.48.5",
]
[[package]]
@@ -1726,6 +1932,17 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+[[package]]
+name = "group"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
+dependencies = [
+ "ff",
+ "rand_core 0.6.4",
+ "subtle",
+]
+
[[package]]
name = "h2"
version = "0.3.26"
@@ -1810,6 +2027,48 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+[[package]]
+name = "hkdf"
+version = "0.12.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7"
+dependencies = [
+ "hmac",
+]
+
+[[package]]
+name = "hmac"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
+dependencies = [
+ "digest",
+]
+
+[[package]]
+name = "hmac-sha1-compact"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dff9d405ec732fa3fcde87264e54a32a84956a377b3e3107de96e59b798c84a7"
+
+[[package]]
+name = "hmac-sha256"
+version = "1.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3688e69b38018fec1557254f64c8dc2cc8ec502890182f395dbb0aa997aa5735"
+dependencies = [
+ "digest",
+]
+
+[[package]]
+name = "hmac-sha512"
+version = "1.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e4ce1f4656bae589a3fab938f9f09bf58645b7ed01a2c5f8a3c238e01a4ef78a"
+dependencies = [
+ "digest",
+]
+
[[package]]
name = "home"
version = "0.5.9"
@@ -2190,6 +2449,46 @@ dependencies = [
"rayon",
]
+[[package]]
+name = "jwt-simple"
+version = "0.11.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "357892bb32159d763abdea50733fadcb9a8e1c319a9aa77592db8555d05af83e"
+dependencies = [
+ "anyhow",
+ "binstring",
+ "coarsetime",
+ "ct-codecs",
+ "ed25519-compact",
+ "hmac-sha1-compact",
+ "hmac-sha256",
+ "hmac-sha512",
+ "k256",
+ "p256",
+ "p384",
+ "rand 0.8.5",
+ "rsa 0.7.2",
+ "serde",
+ "serde_json",
+ "spki 0.6.0",
+ "thiserror",
+ "zeroize",
+]
+
+[[package]]
+name = "k256"
+version = "0.13.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b"
+dependencies = [
+ "cfg-if",
+ "ecdsa",
+ "elliptic-curve",
+ "once_cell",
+ "sha2",
+ "signature 2.2.0",
+]
+
[[package]]
name = "kuchikiki"
version = "0.8.2"
@@ -2214,6 +2513,9 @@ name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+dependencies = [
+ "spin 0.9.8",
+]
[[package]]
name = "lazycell"
@@ -2364,6 +2666,34 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
+[[package]]
+name = "mail-builder"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8390bb0f68168cabc59999706c2199c9b556d7dfd94e65a26e8840fa1d58b238"
+dependencies = [
+ "gethostname 0.4.3",
+]
+
+[[package]]
+name = "mail-send"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f17009bb6737cd4b3e2422a0046dc18a07b8ca8f3fa87e59ea3e8408cb0d2e8f"
+dependencies = [
+ "base64 0.13.1",
+ "gethostname 0.2.3",
+ "mail-builder",
+ "md5",
+ "rand 0.8.5",
+ "rsa 0.6.1",
+ "rustls 0.20.9",
+ "sha2",
+ "tokio",
+ "tokio-rustls 0.23.4",
+ "webpki-roots 0.22.6",
+]
+
[[package]]
name = "markup5ever"
version = "0.11.0"
@@ -2415,6 +2745,12 @@ dependencies = [
"rayon",
]
+[[package]]
+name = "md5"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
+
[[package]]
name = "measure_time"
version = "0.8.3"
@@ -2627,6 +2963,23 @@ dependencies = [
"num-traits",
]
+[[package]]
+name = "num-bigint-dig"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151"
+dependencies = [
+ "byteorder",
+ "lazy_static",
+ "libm",
+ "num-integer",
+ "num-iter",
+ "num-traits",
+ "rand 0.8.5",
+ "smallvec",
+ "zeroize",
+]
+
[[package]]
name = "num-conv"
version = "0.1.0"
@@ -2653,6 +3006,17 @@ dependencies = [
"num-traits",
]
+[[package]]
+name = "num-iter"
+version = "0.1.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
[[package]]
name = "num-rational"
version = "0.4.2"
@@ -2825,6 +3189,30 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1584efc0f222d0fc8f31e6c229b8fd93f69280aab398828f0dbce3957178a1ac"
+[[package]]
+name = "p256"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b"
+dependencies = [
+ "ecdsa",
+ "elliptic-curve",
+ "primeorder",
+ "sha2",
+]
+
+[[package]]
+name = "p384"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209"
+dependencies = [
+ "ecdsa",
+ "elliptic-curve",
+ "primeorder",
+ "sha2",
+]
+
[[package]]
name = "parking_lot"
version = "0.11.2"
@@ -2910,6 +3298,33 @@ dependencies = [
"serde",
]
+[[package]]
+name = "pem-rfc7468"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01de5d978f34aa4b2296576379fcc416034702fd94117c56ffd8a1a767cefb30"
+dependencies = [
+ "base64ct",
+]
+
+[[package]]
+name = "pem-rfc7468"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d159833a9105500e0398934e205e0773f0b27529557134ecfc51c27646adac"
+dependencies = [
+ "base64ct",
+]
+
+[[package]]
+name = "pem-rfc7468"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
+dependencies = [
+ "base64ct",
+]
+
[[package]]
name = "percent-encoding"
version = "2.3.1"
@@ -3040,6 +3455,60 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+[[package]]
+name = "pkcs1"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a78f66c04ccc83dd4486fd46c33896f4e17b24a7a3a6400dedc48ed0ddd72320"
+dependencies = [
+ "der 0.5.1",
+ "pkcs8 0.8.0",
+ "zeroize",
+]
+
+[[package]]
+name = "pkcs1"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eff33bdbdfc54cc98a2eca766ebdec3e1b8fb7387523d5c9c9a2891da856f719"
+dependencies = [
+ "der 0.6.1",
+ "pkcs8 0.9.0",
+ "spki 0.6.0",
+ "zeroize",
+]
+
+[[package]]
+name = "pkcs8"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0"
+dependencies = [
+ "der 0.5.1",
+ "spki 0.5.4",
+ "zeroize",
+]
+
+[[package]]
+name = "pkcs8"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba"
+dependencies = [
+ "der 0.6.1",
+ "spki 0.6.0",
+]
+
+[[package]]
+name = "pkcs8"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
+dependencies = [
+ "der 0.7.9",
+ "spki 0.7.3",
+]
+
[[package]]
name = "pkg-config"
version = "0.3.31"
@@ -3135,6 +3604,15 @@ dependencies = [
"termtree",
]
+[[package]]
+name = "primeorder"
+version = "0.13.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6"
+dependencies = [
+ "elliptic-curve",
+]
+
[[package]]
name = "proc-macro-crate"
version = "3.2.0"
@@ -3475,6 +3953,16 @@ version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
+[[package]]
+name = "rfc6979"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2"
+dependencies = [
+ "hmac",
+ "subtle",
+]
+
[[package]]
name = "rgb"
version = "0.8.50"
@@ -3531,6 +4019,47 @@ dependencies = [
"rio_api",
]
+[[package]]
+name = "rsa"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4cf22754c49613d2b3b119f0e5d46e34a2c628a937e3024b8762de4e7d8c710b"
+dependencies = [
+ "byteorder",
+ "digest",
+ "num-bigint-dig",
+ "num-integer",
+ "num-iter",
+ "num-traits",
+ "pkcs1 0.3.3",
+ "pkcs8 0.8.0",
+ "rand_core 0.6.4",
+ "smallvec",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "rsa"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "094052d5470cbcef561cb848a7209968c9f12dfa6d668f4bca048ac5de51099c"
+dependencies = [
+ "byteorder",
+ "digest",
+ "num-bigint-dig",
+ "num-integer",
+ "num-iter",
+ "num-traits",
+ "pkcs1 0.4.1",
+ "pkcs8 0.9.0",
+ "rand_core 0.6.4",
+ "signature 1.6.4",
+ "smallvec",
+ "subtle",
+ "zeroize",
+]
+
[[package]]
name = "rust-stemmers"
version = "1.2.0"
@@ -3741,6 +4270,20 @@ dependencies = [
"untrusted 0.9.0",
]
+[[package]]
+name = "sec1"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
+dependencies = [
+ "base16ct",
+ "der 0.7.9",
+ "generic-array",
+ "pkcs8 0.10.2",
+ "subtle",
+ "zeroize",
+]
+
[[package]]
name = "security-framework"
version = "2.11.1"
@@ -3914,6 +4457,17 @@ dependencies = [
"digest",
]
+[[package]]
+name = "sha2"
+version = "0.10.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
[[package]]
name = "sharded-slab"
version = "0.1.7"
@@ -3944,6 +4498,26 @@ dependencies = [
"libc",
]
+[[package]]
+name = "signature"
+version = "1.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c"
+dependencies = [
+ "digest",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "signature"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
+dependencies = [
+ "digest",
+ "rand_core 0.6.4",
+]
+
[[package]]
name = "simd-adler32"
version = "0.3.7"
@@ -4036,6 +4610,36 @@ dependencies = [
"lock_api",
]
+[[package]]
+name = "spki"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27"
+dependencies = [
+ "base64ct",
+ "der 0.5.1",
+]
+
+[[package]]
+name = "spki"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b"
+dependencies = [
+ "base64ct",
+ "der 0.6.1",
+]
+
+[[package]]
+name = "spki"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
+dependencies = [
+ "base64ct",
+ "der 0.7.9",
+]
+
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
@@ -4448,9 +5052,21 @@ dependencies = [
"pin-project-lite",
"signal-hook-registry",
"socket2",
+ "tokio-macros",
"windows-sys 0.52.0",
]
+[[package]]
+name = "tokio-macros"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.79",
+]
+
[[package]]
name = "tokio-rustls"
version = "0.23.4"
@@ -4842,6 +5458,15 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+[[package]]
+name = "wasix"
+version = "0.12.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1fbb4ef9bbca0c1170e0b00dd28abc9e3b68669821600cad1caaed606583c6d"
+dependencies = [
+ "wasi 0.11.0+wasi-snapshot-preview1",
+]
+
[[package]]
name = "wasm-bindgen"
version = "0.2.93"
diff --git a/browser/CHANGELOG.md b/browser/CHANGELOG.md
index 41b45e0f5..7204e5cfe 100644
--- a/browser/CHANGELOG.md
+++ b/browser/CHANGELOG.md
@@ -243,11 +243,18 @@ This changelog covers all five packages, as they are (for now) updated as a whol
- Add `store.getResourceAncestry` method, which returns the ancestry of a resource, including the resource itself.
- Add `resource.title` property, which returns the name of a resource, or the first property that is can be used to name the resource.
- `store.createSubject` now accepts a `parent` argument, which allows creating nested subjects.
+- Add `store.getServerSupports` to know which features a Server supports
+
+### @tomic/react
+
+- Add `useServerSupports` hook to see supported features of the server
## v0.35.0
### @tomic/browser
+- Let users register using e-mail address, improve sign-up UX.
+- Add `Store.parseMetaTags` to load JSON-AD objects stored in the DOM. Speeds up initial page load by allowing server to set JSON-AD objects in the initial HTML response.
- Move static assets around, align build with server and fix PWA #292
- Add `useChildren` hook and `Store.getChildren` method
- Add new file preview UI for images, audio, text and PDF files.
@@ -255,8 +262,14 @@ This changelog covers all five packages, as they are (for now) updated as a whol
- Fix Dialogue form #308
- Refactor search, escape query strings for Tantivy
- Add `import` context menu, allows importing anywhere
+- Let users register using e-mail address, improve sign-up UX.
### @tomic/react
+- `store.createSubject` allows creating nested paths
+- `store.createSubject` allows creating nested paths
+- Add `useChildren` hook and `Store.getChildren` method
+- Add `Store.postToServer` method, add `endpoints`, `import_json_ad_string`
+- Add `store.preloadClassesAndProperties` and remove `urls.properties.getAll` and `urls.classes.getAll`. This enables using `atomic-data-browser` without relying on `atomicdata.dev` being available.
- Add more options to `useSearch`
diff --git a/browser/data-browser/src/chunks/MarkdownEditor/AsyncMarkdownEditor.tsx b/browser/data-browser/src/chunks/MarkdownEditor/AsyncMarkdownEditor.tsx
index 5ce5d8298..919ecd766 100644
--- a/browser/data-browser/src/chunks/MarkdownEditor/AsyncMarkdownEditor.tsx
+++ b/browser/data-browser/src/chunks/MarkdownEditor/AsyncMarkdownEditor.tsx
@@ -14,6 +14,7 @@ import { ToggleButton } from './ToggleButton';
import { SlashCommands, suggestion } from './SlashMenu/CommandsExtension';
import { ExtendedImage } from './ImagePicker';
import { transition } from '../../helpers/transition';
+import { markdownStyles } from '../../components/datatypes/Markdown';
export type AsyncMarkdownEditorProps = {
placeholder?: string;
@@ -112,7 +113,7 @@ export default function AsyncMarkdownEditor({
/>
)}
-
+
Type '/' for options
@@ -139,6 +140,7 @@ const calcHeight = (value: string) => {
return `calc(${lines * LINE_HEIGHT}em + 5px)`;
};
+// WARNING: Only add styles specific to editing. For other styles, add to `MarkdownWrapper`
const EditorWrapper = styled.div<{ hideEditor: boolean }>`
position: relative;
background-color: ${p => p.theme.colors.bg};
@@ -173,27 +175,7 @@ const EditorWrapper = styled.div<{ hideEditor: boolean }>`
height: auto;
}
- pre {
- padding: 0.75rem 1rem;
- background-color: ${p => p.theme.colors.bg1};
- border-radius: ${p => p.theme.radius};
- font-family: monospace;
-
- code {
- white-space: pre;
- color: inherit;
- padding: 0;
- background: none;
- font-size: 0.8rem;
- }
- }
-
- blockquote {
- margin-inline-start: 0;
- border-inline-start: 3px solid ${p => p.theme.colors.textLight2};
- color: ${p => p.theme.colors.textLight};
- padding-inline-start: 1rem;
- }
+ ${markdownStyles}
}
`;
diff --git a/browser/data-browser/src/components/Card.tsx b/browser/data-browser/src/components/Card.tsx
index 00c678ef3..31a3a48c5 100644
--- a/browser/data-browser/src/components/Card.tsx
+++ b/browser/data-browser/src/components/Card.tsx
@@ -42,6 +42,7 @@ export const CardRow = styled.div`
display: block;
border-top: ${p => (p.noBorder ? 'none' : 'var(--border)')};
padding: ${p => p.theme.size(2)} ${p => p.theme.size()};
+ overflow-wrap: break-word;
`;
/** A block inside a Card which has full width */
diff --git a/browser/data-browser/src/components/CodeBlock.tsx b/browser/data-browser/src/components/CodeBlock.tsx
index aafe0fa56..293014011 100644
--- a/browser/data-browser/src/components/CodeBlock.tsx
+++ b/browser/data-browser/src/components/CodeBlock.tsx
@@ -7,9 +7,11 @@ import { Button } from './Button';
interface CodeBlockProps {
content?: string;
loading?: boolean;
+ wrapContent?: boolean;
}
-export function CodeBlock({ content, loading }: CodeBlockProps) {
+/** Codeblock with copy feature */
+export function CodeBlock({ content, loading, wrapContent }: CodeBlockProps) {
const [isCopied, setIsCopied] = useState(undefined);
function copyToClipboard() {
@@ -19,7 +21,7 @@ export function CodeBlock({ content, loading }: CodeBlockProps) {
}
return (
-
+
{loading ? (
'loading...'
) : (
@@ -46,13 +48,22 @@ export function CodeBlock({ content, loading }: CodeBlockProps) {
);
}
-export const CodeBlockStyled = styled.pre`
+interface Props {
+ /** Renders all in a single line */
+ wrapContent?: boolean;
+}
+
+export const CodeBlockStyled = styled.pre`
position: relative;
background-color: ${p => p.theme.colors.bg1};
border-radius: ${p => p.theme.radius};
border: solid 1px ${p => p.theme.colors.bg2};
padding: 0.3rem;
+ min-height: 2.2rem;
+ font-size: 0.8rem;
font-family: monospace;
width: 100%;
overflow-x: auto;
+ word-wrap: ${p => (p.wrapContent ? 'break-word' : 'initial')};
+ white-space: ${p => (p.wrapContent ? 'pre-wrap' : 'pre')};
`;
diff --git a/browser/data-browser/src/components/ConfirmationDialog.tsx b/browser/data-browser/src/components/ConfirmationDialog.tsx
index b48a7b8f1..4b25bd197 100644
--- a/browser/data-browser/src/components/ConfirmationDialog.tsx
+++ b/browser/data-browser/src/components/ConfirmationDialog.tsx
@@ -33,7 +33,11 @@ export function ConfirmationDialog({
bindShow,
theme = ConfirmationDialogTheme.Default,
}: React.PropsWithChildren): JSX.Element {
- const [dialogProps, showDialog, hideDialog] = useDialog({
+ const {
+ dialogProps,
+ show: showDialog,
+ close: hideDialog,
+ } = useDialog({
bindShow,
onCancel,
onSuccess: onConfirm,
diff --git a/browser/data-browser/src/components/Dialog/index.tsx b/browser/data-browser/src/components/Dialog/index.tsx
index 9e70549b3..350225f56 100644
--- a/browser/data-browser/src/components/Dialog/index.tsx
+++ b/browser/data-browser/src/components/Dialog/index.tsx
@@ -19,7 +19,8 @@ import { useDialogGlobalContext } from './DialogGlobalContextProvider';
import { DIALOG_CONTENT_CONTAINER } from '../../helpers/containers';
export interface InternalDialogProps {
- show: boolean;
+ /** Is the Dialog visible */
+ isVisible: boolean;
onClose: (success: boolean) => void;
onClosed: () => void;
width?: CSS.Property.Width;
@@ -81,7 +82,7 @@ export function Dialog(props: React.PropsWithChildren) {
const InnerDialog: React.FC> = ({
children,
- show,
+ isVisible,
width,
onClose,
onClosed,
@@ -89,9 +90,9 @@ const InnerDialog: React.FC> = ({
const dialogRef = useRef(null);
const innerDialogRef = useRef(null);
const { hasOpenInnerPopup } = useDialogTreeContext();
- const { isTopLevel } = useDialogGlobalContext(show);
+ const { isTopLevel } = useDialogGlobalContext(isVisible);
- useControlLock(show);
+ useControlLock(isVisible);
const cancelDialog = useCallback(() => {
onClose(false);
@@ -123,22 +124,26 @@ const InnerDialog: React.FC> = ({
() => {
cancelDialog();
},
- { enabled: show && !hasOpenInnerPopup && isTopLevel },
+ { enabled: isVisible && !hasOpenInnerPopup && isTopLevel },
);
// When closing the `data-closing` attribute must be set before rendering so the animation has started when the regular useEffect is called.
useLayoutEffect(() => {
- if (!show && dialogRef.current && dialogRef.current.hasAttribute('open')) {
+ if (
+ !isVisible &&
+ dialogRef.current &&
+ dialogRef.current.hasAttribute('open')
+ ) {
dialogRef.current.setAttribute('data-closing', 'true');
}
- }, [show]);
+ }, [isVisible]);
useEffect(() => {
if (!dialogRef.current) {
return;
}
- if (show) {
+ if (isVisible) {
if (!dialogRef.current.hasAttribute('open'))
// @ts-ignore
dialogRef.current.showModal();
@@ -153,7 +158,7 @@ const InnerDialog: React.FC> = ({
onClosed();
}, ANIM_MS);
}
- }, [show, onClosed]);
+ }, [isVisible, onClosed]);
return (
void,
+ show: () => void;
/** Function to close the dialog */
- close: (success?: boolean) => void,
- /** Boolean indicating wether the dialog is currently open */
- isOpen: boolean,
-];
+ close: (success?: boolean) => void;
+ /** Whether the dialog is currently open */
+ isOpen: boolean;
+};
export type UseDialogOptions = {
bindShow?: React.Dispatch;
@@ -25,26 +25,27 @@ export function useDialog(
): UseDialogReturnType {
const { bindShow, onCancel, onSuccess, triggerRef } = options ?? {};
- const [showDialog, setShowDialog] = useState(false);
- const [visible, setVisible] = useState(false);
+ const [isOpen, setIsOpen] = useState(false);
+ const [isVisible, setIsVisible] = useState(false);
const [wasSuccess, setWasSuccess] = useState(false);
const show = useCallback(() => {
document.body.setAttribute('inert', '');
- setShowDialog(true);
- setVisible(true);
+ setIsVisible(true);
+ setIsOpen(true);
bindShow?.(true);
}, []);
const close = useCallback((success = false) => {
setWasSuccess(success);
- setShowDialog(false);
+ setIsVisible(false);
+ // The 'isOpen' will be set to false by handleClosed after the animation
}, []);
const handleClosed = useCallback(() => {
document.body.removeAttribute('inert');
bindShow?.(false);
- setVisible(false);
+ setIsOpen(false);
if (wasSuccess) {
onSuccess?.();
@@ -60,12 +61,12 @@ export function useDialog(
/** Props that should be passed to a {@link Dialog} component. */
const dialogProps = useMemo(
() => ({
- show: showDialog,
+ isVisible,
onClose: close,
onClosed: handleClosed,
}),
- [showDialog, close, handleClosed],
+ [isVisible, close, handleClosed],
);
- return [dialogProps, show, close, visible];
+ return { dialogProps, show, close, isOpen };
}
diff --git a/browser/data-browser/src/components/ErrorLook.tsx b/browser/data-browser/src/components/ErrorLook.tsx
index 71c1469dd..0733fc6c7 100644
--- a/browser/data-browser/src/components/ErrorLook.tsx
+++ b/browser/data-browser/src/components/ErrorLook.tsx
@@ -2,6 +2,9 @@ import { lighten } from 'polished';
import { styled, css } from 'styled-components';
import { FaExclamationTriangle } from 'react-icons/fa';
+import { Column } from './Row';
+import { CodeBlock } from './CodeBlock';
+import { Button } from './Button';
export const errorLookStyle = css`
color: ${props => props.theme.colors.alert};
@@ -18,6 +21,43 @@ export interface ErrorBlockProps {
showTrace?: boolean;
}
+const githubIssueTemplate = (
+ message,
+ stack,
+) => `**Describe what you did to produce the bug**
+
+## Error message
+\`\`\`
+${message}
+\`\`\`
+
+## Stack trace
+\`\`\`
+${stack}
+\`\`\`
+`;
+
+/** Returns github URL for new bugs */
+export function createGithubIssueLink(error: Error): string {
+ const url = new URL(
+ 'https://github.com/atomicdata-dev/atomic-server/issues/new',
+ );
+ url.searchParams.set('body', githubIssueTemplate(error.message, error.stack));
+ url.searchParams.set('labels', 'bug');
+
+ console.log('opening', url);
+
+ return url.href;
+}
+
+export function GitHubIssueButton({ error }) {
+ return (
+ window.open(createGithubIssueLink(error), '_blank')}>
+ Report on Github
+
+ );
+}
+
export function ErrorBlock({ error, showTrace }: ErrorBlockProps): JSX.Element {
return (
@@ -25,40 +65,29 @@ export function ErrorBlock({ error, showTrace }: ErrorBlockProps): JSX.Element {
Something went wrong
-
- {error.message}
+
+
{showTrace && (
<>
-
-
- Stack trace:
-
- {error.stack}
+ Stack trace:
+
>
)}
-
+
);
}
const ErrorLookBig = styled.div`
- color: ${p => p.theme.colors.alert};
font-size: 1rem;
padding: ${p => p.theme.margin}rem;
border-radius: ${p => p.theme.radius};
border: 1px solid ${p => lighten(0.2, p.theme.colors.alert)};
- background-color: ${p => p.theme.colors.bg1};
-`;
-
-const Pre = styled.pre`
- white-space: pre-wrap;
- border-radius: ${p => p.theme.radius};
- padding: ${p => p.theme.margin}rem;
background-color: ${p => p.theme.colors.bg};
- font-size: 0.9rem;
`;
const BiggerText = styled.p`
+ color: ${p => p.theme.colors.alert};
font-size: 1.3rem;
display: flex;
align-items: center;
diff --git a/browser/data-browser/src/components/Guard.tsx b/browser/data-browser/src/components/Guard.tsx
new file mode 100644
index 000000000..60554e845
--- /dev/null
+++ b/browser/data-browser/src/components/Guard.tsx
@@ -0,0 +1,17 @@
+import React from 'react';
+import { useSettings } from '../helpers/AppSettings';
+import { RegisterSignIn } from './RegisterSignIn';
+
+/**
+ * The Guard can be wrapped around a Component that depends on a user being logged in.
+ * If the user is not logged in, it will show a button to sign up / sign in.
+ * Show to users after a new Agent has been created.
+ * Instructs them to save their secret somewhere safe
+ */
+export function Guard({ children }: React.PropsWithChildren): JSX.Element {
+ const { agent } = useSettings();
+
+ if (agent) {
+ return <>{children}>;
+ } else return ;
+}
diff --git a/browser/data-browser/src/components/ParentPicker/ParentPickerDialog.tsx b/browser/data-browser/src/components/ParentPicker/ParentPickerDialog.tsx
index efbc4a85c..7edac75e7 100644
--- a/browser/data-browser/src/components/ParentPicker/ParentPickerDialog.tsx
+++ b/browser/data-browser/src/components/ParentPicker/ParentPickerDialog.tsx
@@ -29,7 +29,7 @@ export function ParentPickerDialog({
}: ParentPickerDialogProps): React.JSX.Element {
const [selected, setSelected] = useState();
- const [dialogProps, show, close, isOpen] = useDialog({
+ const { dialogProps, show, close, isOpen } = useDialog({
onCancel,
bindShow: onOpenChange,
});
diff --git a/browser/data-browser/src/components/RegisterSignIn.tsx b/browser/data-browser/src/components/RegisterSignIn.tsx
new file mode 100644
index 000000000..99533c710
--- /dev/null
+++ b/browser/data-browser/src/components/RegisterSignIn.tsx
@@ -0,0 +1,297 @@
+import {
+ Dialog,
+ DialogActions,
+ DialogContent,
+ DialogTitle,
+ useDialog,
+} from './Dialog';
+import { FormEvent, useCallback, useEffect, useState } from 'react';
+import { Button } from './Button';
+import {
+ addPublicKey,
+ nameRegex,
+ register as createRegistration,
+ useServerSupports,
+ useServerURL,
+ useStore,
+} from '@tomic/react';
+import Field from './forms/Field';
+import { InputWrapper, InputStyled } from './forms/InputStyles';
+import { Row } from './Row';
+import { ErrorLook } from './ErrorLook';
+import { SettingsAgent } from './SettingsAgent';
+
+/** What is currently showing */
+enum PageStateOpts {
+ /** Start state, select register or sign in */
+ none,
+ /** Enter Secret*/
+ signIn,
+ /** Register new Email address */
+ register,
+ /** Reset existing email, send Secret reset email link */
+ reset,
+ mailSentRegistration,
+ mailSentAddPubkey,
+}
+
+/**
+ * Two buttons: Register / Sign in.
+ * Opens a Dialog / Modal with the appropriate form.
+ */
+export function RegisterSignIn(): JSX.Element {
+ const { dialogProps, show, close } = useDialog();
+ const [pageState, setPageState] = useState(PageStateOpts.none);
+ const [email, setEmail] = useState('');
+ const { emailRegister } = useServerSupports();
+
+ if (!emailRegister) {
+ return (
+ <>
+
+ No email support on this server...
+ >
+ );
+ }
+
+ return (
+ <>
+
+ {
+ setPageState(PageStateOpts.register);
+ show();
+ }}
+ >
+ Register
+
+ {
+ setPageState(PageStateOpts.signIn);
+ show();
+ }}
+ >
+ Sign In
+
+
+
+ {pageState === PageStateOpts.register && (
+
+ )}
+ {pageState === PageStateOpts.signIn && (
+
+ )}
+ {pageState === PageStateOpts.reset && (
+
+ )}
+ {pageState === PageStateOpts.mailSentRegistration && (
+
+ )}
+ {pageState === PageStateOpts.mailSentAddPubkey && (
+
+ )}
+
+ >
+ );
+}
+
+function Reset({ email, setEmail, setPageState }) {
+ const store = useStore();
+ const [err, setErr] = useState(undefined);
+
+ const handleRequestReset = useCallback(async () => {
+ try {
+ await addPublicKey(store, email);
+ setPageState(PageStateOpts.mailSentAddPubkey);
+ } catch (e) {
+ setErr(e);
+ }
+ }, [email]);
+
+ return (
+ <>
+
+ Reset your Secret
+
+
+
+ {
+ "Lost it? No worries, we'll send a link that let's you create a new one."
+ }
+
+ {
+ setErr(undefined);
+ setEmail(e);
+ }}
+ />
+ {err && {err.message} }
+
+
+ Send me
+
+ >
+ );
+}
+
+function MailSentConfirm({ email, close, message }) {
+ return (
+ <>
+
+ Go to your email inbox
+
+
+
+ {"We've sent a confirmation link to "}
+ {email}
+ {'.'}
+
+ {message}
+
+
+ {"Ok, I'll open my mailbox!"}
+
+ >
+ );
+}
+
+function Register({ setPageState, email, setEmail }) {
+ const [name, setName] = useState('');
+ const [serverUrlStr] = useServerURL();
+ const [nameErr, setErr] = useState(undefined);
+ const store = useStore();
+
+ const serverUrl = new URL(serverUrlStr);
+ serverUrl.host = `${name}.${serverUrl.host}`;
+
+ useEffect(() => {
+ // check regex of name, set error
+ if (!name.match(nameRegex)) {
+ setErr(new Error('Name must be lowercase and only contain numbers'));
+ } else {
+ setErr(undefined);
+ }
+ }, [name, email]);
+
+ const handleSubmit = useCallback(
+ async (event: FormEvent) => {
+ event.preventDefault();
+
+ if (!name) {
+ setErr(new Error('Name is required'));
+
+ return;
+ }
+
+ try {
+ await createRegistration(store, name, email);
+ setPageState(PageStateOpts.mailSentRegistration);
+ } catch (er) {
+ setErr(er);
+ }
+ },
+ [name, email],
+ );
+
+ return (
+ <>
+
+ Register
+
+
+
+
+
+ setPageState(PageStateOpts.signIn)}>
+ Sign in
+
+
+ Save
+
+
+ >
+ );
+}
+
+function SignIn({ setPageState }) {
+ return (
+ <>
+
+ Sign in
+
+
+
+
+
+ setPageState(PageStateOpts.register)}>
+ Register
+
+ setPageState(PageStateOpts.reset)}>
+ I lost my Secret
+
+
+ >
+ );
+}
+
+function EmailField({ setEmail, email }) {
+ return (
+
+
+ {
+ setEmail(e.target.value);
+ }}
+ />
+
+
+ );
+}
diff --git a/browser/data-browser/src/components/SearchFilter.tsx b/browser/data-browser/src/components/SearchFilter.tsx
index 6cc278ec1..c3c22812e 100644
--- a/browser/data-browser/src/components/SearchFilter.tsx
+++ b/browser/data-browser/src/components/SearchFilter.tsx
@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react';
-import { urls, useArray, useProperty, useResource } from '@tomic/react';
+import { core, urls, useArray, useProperty, useResource } from '@tomic/react';
import { ResourceSelector } from '../components/forms/ResourceSelector';
/**
@@ -18,7 +18,7 @@ export function ClassFilter({ filters, setFilters }): JSX.Element {
// Set the filters to the default values of the properties
setFilters({
...filters,
- [urls.properties.isA]: klass,
+ [core.properties.isA]: klass,
});
}, [klass, JSON.stringify(filters)]);
@@ -27,7 +27,7 @@ export function ClassFilter({ filters, setFilters }): JSX.Element {
{allProps?.map(propertySubject => (
{
+ const { agent, setAgent } = useSettings();
+ const [subject, setSubject] = useState(undefined);
+ const [privateKey, setPrivateKey] = useState(undefined);
+ const [error, setError] = useState(undefined);
+ const [showPrivateKey, setShowPrivateKey] = useState(false);
+ const [advanced, setAdvanced] = useState(false);
+ const [secret, setSecret] = useState(undefined);
+
+ // When there is an agent, set the advanced values
+ // Otherwise, reset the secret value
+ React.useEffect(() => {
+ if (agent !== undefined) {
+ fillAdvanced();
+ } else {
+ setSecret('');
+ }
+ }, [agent]);
+
+ // When the key or subject changes, update the secret
+ React.useEffect(() => {
+ renewSecret();
+ }, [subject, privateKey]);
+
+ function renewSecret() {
+ if (agent) {
+ setSecret(agent.buildSecret());
+ }
+ }
+
+ function fillAdvanced() {
+ try {
+ if (!agent) {
+ throw new Error('No agent set');
+ }
+
+ setSubject(agent.subject);
+ setPrivateKey(agent.privateKey);
+ } catch (e) {
+ const err = new Error('Cannot fill subject and privatekey fields.' + e);
+ setError(err);
+ setSubject('');
+ }
+ }
+
+ function setAgentIfChanged(oldAgent: Agent | undefined, newAgent: Agent) {
+ if (JSON.stringify(oldAgent) !== JSON.stringify(newAgent)) {
+ setAgent(newAgent);
+ }
+ }
+
+ /** Called when the secret or the subject is updated manually */
+ async function handleUpdateSubjectAndKey() {
+ renewSecret();
+ setError(undefined);
+
+ try {
+ const newAgent = new Agent(privateKey!, subject);
+ await newAgent.getPublicKey();
+ await newAgent.verifyPublicKeyWithServer();
+
+ setAgentIfChanged(agent, newAgent);
+ } catch (e) {
+ const err = new Error('Invalid Agent' + e);
+ setError(err);
+ }
+ }
+
+ function handleCopy() {
+ secret && navigator.clipboard.writeText(secret);
+ }
+
+ /** When the Secret updates, parse it and try if the */
+ async function handleUpdateSecret(updateSecret: string) {
+ setSecret(updateSecret);
+
+ if (updateSecret === '') {
+ setSecret('');
+ setError(undefined);
+
+ return;
+ }
+
+ setError(undefined);
+
+ try {
+ const newAgent = Agent.fromSecret(updateSecret);
+ setAgentIfChanged(agent, newAgent);
+ setPrivateKey(newAgent.privateKey);
+ setSubject(newAgent.subject);
+ // This will fail and throw if the agent is not public, which is by default
+ // await newAgent.checkPublicKey();
+ } catch (e) {
+ const err = new Error('Invalid secret. ' + e);
+ setError(err);
+ }
+ }
+
+ return (
+
+ );
+};
diff --git a/browser/data-browser/src/components/SideBar/DriveSwitcher.tsx b/browser/data-browser/src/components/SideBar/DriveSwitcher.tsx
index 6d4ba73c7..27089ecae 100644
--- a/browser/data-browser/src/components/SideBar/DriveSwitcher.tsx
+++ b/browser/data-browser/src/components/SideBar/DriveSwitcher.tsx
@@ -1,4 +1,10 @@
-import { Resource, core, server, useResources } from '@tomic/react';
+import {
+ Resource,
+ server,
+ truncateUrl,
+ urls,
+ useResources,
+} from '@tomic/react';
import { useMemo } from 'react';
import {
FaCog,
@@ -21,7 +27,8 @@ const Trigger = buildDefaultTrigger( , 'Open Drive Settings');
function getTitle(resource: Resource): string {
return (
- (resource.get(core.properties.name) as string) ?? resource.getSubject()
+ (resource.get(urls.properties.name) as string) ??
+ truncateUrl(resource.getSubject(), 20)
);
}
@@ -45,6 +52,7 @@ export function DriveSwitcher() {
};
const createNewResource = useNewResourceUI();
+ // const createNewDrive = useDefaultNewInstanceHandler(classes.drive);
const items = useMemo(
() => [
diff --git a/browser/data-browser/src/components/datatypes/Markdown.tsx b/browser/data-browser/src/components/datatypes/Markdown.tsx
index 3889493f2..8bb4419b8 100644
--- a/browser/data-browser/src/components/datatypes/Markdown.tsx
+++ b/browser/data-browser/src/components/datatypes/Markdown.tsx
@@ -1,5 +1,5 @@
import ReactMarkdown from 'react-markdown';
-import { styled } from 'styled-components';
+import { css, styled } from 'styled-components';
import remarkGFM from 'remark-gfm';
import { Button } from '../Button';
import { truncateMarkdown } from '../../helpers/markdown';
@@ -52,16 +52,15 @@ const Markdown: FC = ({
);
};
-const MarkdownWrapper = styled.div`
- width: 100%;
- overflow-x: hidden;
- img {
- max-width: 100%;
- }
+Markdown.defaultProps = {
+ renderGFM: true,
+};
- * {
+/** Styles shared in Markdown View & Edit renderers */
+export const markdownStyles = css`
+ /* * {
white-space: unset;
- }
+ } */
p,
h1,
@@ -77,6 +76,10 @@ const MarkdownWrapper = styled.div`
margin-bottom: 0;
}
+ ul li {
+ margin-bottom: 1rem;
+ }
+
blockquote {
margin-inline-start: 0rem;
padding-inline-start: 1rem;
@@ -124,4 +127,14 @@ const MarkdownWrapper = styled.div`
}
`;
+const MarkdownWrapper = styled.div`
+ width: 100%;
+ overflow-x: hidden;
+ img {
+ max-width: 100%;
+ }
+
+ ${markdownStyles}
+`;
+
export default Markdown;
diff --git a/browser/data-browser/src/components/forms/FileDropzone/FileDropzoneInput.tsx b/browser/data-browser/src/components/forms/FileDropzone/FileDropzoneInput.tsx
index 25e5a2f48..3d542b842 100644
--- a/browser/data-browser/src/components/forms/FileDropzone/FileDropzoneInput.tsx
+++ b/browser/data-browser/src/components/forms/FileDropzone/FileDropzoneInput.tsx
@@ -8,11 +8,11 @@ import { useUpload } from '../../../hooks/useUpload';
export interface FileDropzoneInputProps {
parentResource: Resource;
- onFilesUploaded?: (files: string[]) => void;
text?: string;
maxFiles?: number;
className?: string;
accept?: string[];
+ onFilesUploaded?: (fileSubjects: string[]) => void;
}
/**
diff --git a/browser/data-browser/src/components/forms/FilePicker/FilePickerDialog.tsx b/browser/data-browser/src/components/forms/FilePicker/FilePickerDialog.tsx
index b73162173..6a1802e30 100644
--- a/browser/data-browser/src/components/forms/FilePicker/FilePickerDialog.tsx
+++ b/browser/data-browser/src/components/forms/FilePicker/FilePickerDialog.tsx
@@ -34,7 +34,11 @@ export function FilePickerDialog({
noUpload = false,
}: FilePickerProps): React.JSX.Element {
const { drive } = useSettings();
- const [dialogProps, showDialog, closeDialog] = useDialog({
+ const {
+ dialogProps,
+ show: showDialog,
+ close: closeDialog,
+ } = useDialog({
bindShow: onShowChange,
});
diff --git a/browser/data-browser/src/components/forms/NewForm/CustomCreateActions/CustomForms/NewArticleDialog.tsx b/browser/data-browser/src/components/forms/NewForm/CustomCreateActions/CustomForms/NewArticleDialog.tsx
index 35d8293b4..9154be958 100644
--- a/browser/data-browser/src/components/forms/NewForm/CustomCreateActions/CustomForms/NewArticleDialog.tsx
+++ b/browser/data-browser/src/components/forms/NewForm/CustomCreateActions/CustomForms/NewArticleDialog.tsx
@@ -44,7 +44,10 @@ export const NewArticleDialog: FC = ({
onClose();
}, [name, createResourceAndNavigate, onClose, parent]);
- const [dialogProps, show, hide] = useDialog({ onSuccess, onCancel: onClose });
+ const { dialogProps, show, close } = useDialog({
+ onSuccess,
+ onCancel: onClose,
+ });
const onNameChange = (e: React.ChangeEvent) => {
setName(e.target.value);
@@ -69,7 +72,7 @@ export const NewArticleDialog: FC = ({
- hide(false)} subtle>
+ close(false)} subtle>
Cancel
- hide(true)} disabled={!valid}>
+ close(true)} disabled={!valid}>
Create
diff --git a/browser/data-browser/src/components/forms/NewForm/CustomCreateActions/CustomForms/NewBookmarkDialog.tsx b/browser/data-browser/src/components/forms/NewForm/CustomCreateActions/CustomForms/NewBookmarkDialog.tsx
index f2b7b4811..482f56bbf 100644
--- a/browser/data-browser/src/components/forms/NewForm/CustomCreateActions/CustomForms/NewBookmarkDialog.tsx
+++ b/browser/data-browser/src/components/forms/NewForm/CustomCreateActions/CustomForms/NewBookmarkDialog.tsx
@@ -27,7 +27,7 @@ export const NewBookmarkDialog: FC = ({
}) => {
const [url, setUrl] = useState('');
- const [dialogProps, show, hide] = useDialog({ onCancel: onClose });
+ const { dialogProps, show, close } = useDialog({ onCancel: onClose });
const createResourceAndNavigate = useCreateAndNavigate();
@@ -58,32 +58,34 @@ export const NewBookmarkDialog: FC = ({
}, []);
return (
-
-
- New Bookmark
-
-
-
-
-
- hide(false)} subtle>
- Cancel
-
-
- Ok
-
-
-
+ <>
+
+
+ New Bookmark
+
+
+
+
+
+
+ Cancel
+
+
+ Ok
+
+
+
+ >
);
};
diff --git a/browser/data-browser/src/components/forms/NewForm/CustomCreateActions/CustomForms/NewCollectionDialog.tsx b/browser/data-browser/src/components/forms/NewForm/CustomCreateActions/CustomForms/NewCollectionDialog.tsx
index 349866c84..09f2ccd54 100644
--- a/browser/data-browser/src/components/forms/NewForm/CustomCreateActions/CustomForms/NewCollectionDialog.tsx
+++ b/browser/data-browser/src/components/forms/NewForm/CustomCreateActions/CustomForms/NewCollectionDialog.tsx
@@ -22,7 +22,7 @@ export const NewCollectionDialog: FC = ({
const [valueFilter, setValue] = useState();
const [propertyFilter, setProperty] = useState();
- const [dialogProps, show, hide] = useDialog({ onCancel: onClose });
+ const { dialogProps, show, close } = useDialog({ onCancel: onClose });
const createResourceAndNavigate = useCreateAndNavigate();
@@ -91,7 +91,7 @@ export const NewCollectionDialog: FC = ({
- hide(false)} subtle>
+ close(false)} subtle>
Cancel
diff --git a/browser/data-browser/src/components/forms/NewForm/CustomCreateActions/CustomForms/NewDriveDialog.tsx b/browser/data-browser/src/components/forms/NewForm/CustomCreateActions/CustomForms/NewDriveDialog.tsx
index 32a83aef9..83e7202e4 100644
--- a/browser/data-browser/src/components/forms/NewForm/CustomCreateActions/CustomForms/NewDriveDialog.tsx
+++ b/browser/data-browser/src/components/forms/NewForm/CustomCreateActions/CustomForms/NewDriveDialog.tsx
@@ -91,7 +91,11 @@ export const NewDriveDialog: FC = ({
onClose();
}, [name, createAndNavigate, onClose, parent, setDrive, store]);
- const [dialogProps, show, hide] = useDialog({ onSuccess, onCancel: onClose });
+ const {
+ dialogProps,
+ show,
+ close: hide,
+ } = useDialog({ onSuccess, onCancel: onClose });
useEffect(() => {
show();
diff --git a/browser/data-browser/src/components/forms/NewForm/CustomCreateActions/CustomForms/NewOntologyDialog.tsx b/browser/data-browser/src/components/forms/NewForm/CustomCreateActions/CustomForms/NewOntologyDialog.tsx
index 8a05ed1c1..f152993be 100644
--- a/browser/data-browser/src/components/forms/NewForm/CustomCreateActions/CustomForms/NewOntologyDialog.tsx
+++ b/browser/data-browser/src/components/forms/NewForm/CustomCreateActions/CustomForms/NewOntologyDialog.tsx
@@ -41,7 +41,11 @@ export const NewOntologyDialog: FC = ({
onClose();
}, [shortname, createResourceAndNavigate, onClose, parent]);
- const [dialogProps, show, hide] = useDialog({ onSuccess, onCancel: onClose });
+ const {
+ dialogProps,
+ show,
+ close: hide,
+ } = useDialog({ onSuccess, onCancel: onClose });
const onShortnameChange = (e: React.ChangeEvent) => {
const value = stringToSlug(e.target.value);
diff --git a/browser/data-browser/src/components/forms/NewForm/CustomCreateActions/CustomForms/NewTableDialog.tsx b/browser/data-browser/src/components/forms/NewForm/CustomCreateActions/CustomForms/NewTableDialog.tsx
index 7503981e7..7ec9ba28d 100644
--- a/browser/data-browser/src/components/forms/NewForm/CustomCreateActions/CustomForms/NewTableDialog.tsx
+++ b/browser/data-browser/src/components/forms/NewForm/CustomCreateActions/CustomForms/NewTableDialog.tsx
@@ -89,7 +89,12 @@ export const NewTableDialog: FC = ({
createResourceAndNavigate,
]);
- const [dialogProps, show, hide, isOpen] = useDialog({ onCancel, onSuccess });
+ const {
+ dialogProps,
+ show,
+ close: hide,
+ isOpen,
+ } = useDialog({ onCancel, onSuccess });
useEffect(() => {
show();
diff --git a/browser/data-browser/src/components/forms/ResourceSelector.tsx b/browser/data-browser/src/components/forms/ResourceSelector.tsx
new file mode 100644
index 000000000..93d7b7a42
--- /dev/null
+++ b/browser/data-browser/src/components/forms/ResourceSelector.tsx
@@ -0,0 +1,206 @@
+import {
+ ArrayError,
+ urls,
+ useArray,
+ useResource,
+ useStore,
+ useTitle,
+} from '@tomic/react';
+import React, {
+ Dispatch,
+ SetStateAction,
+ useContext,
+ useState,
+ useCallback,
+} from 'react';
+import { ErrMessage, InputWrapper } from './InputStyles';
+import { Dialog, useDialog } from '../Dialog';
+import { DialogTreeContext } from '../Dialog/dialogContext';
+import { useSettings } from '../../helpers/AppSettings';
+import styled from 'styled-components';
+import { NewFormDialog } from './NewForm/NewFormDialog';
+import { DropdownInput } from './ResourceSelector/DropdownInput';
+
+export interface ResourceSelectorProps {
+ /**
+ * Whether a certain type of Class is required here. Pass the URL of the
+ * class. Is used for constructing a list of options.
+ */
+ classType?: string;
+ /** If true, the form will show an error if it is left empty. */
+ required?: boolean;
+ /**
+ * This callback is called when the Subject Changes. You can pass an Error
+ * Handler as the second argument to set an error message. Take the second
+ * argument of a `useString` hook and pass the setString part to this property
+ */
+ setSubject: (
+ subject: string | undefined,
+ errHandler?: Dispatch>,
+ ) => void;
+ /** The value (URL of the Resource that is selected) */
+ value?: string;
+ /** A function to remove this item. Only relevant in arrays. */
+ handleRemove?: () => void;
+ /** Only pass an error if it is applicable to this specific field */
+ error?: Error;
+ /**
+ * Set an ArrayError. A special type, because the parent needs to know where
+ * in the Array the error occurred
+ */
+ setError?: Dispatch>;
+ disabled?: boolean;
+ autoFocus?: boolean;
+ /** Is used when a new item is created using the ResourceSelector */
+ parent?: string;
+}
+
+/**
+ * Form field for selecting a single resource. Needs external subject &
+ * setSubject properties
+ */
+export const ResourceSelector = React.memo(function ResourceSelector({
+ required,
+ setSubject,
+ value,
+ handleRemove,
+ error,
+ setError,
+ classType,
+ disabled,
+ parent,
+ ...props
+}: ResourceSelectorProps): JSX.Element {
+ // TODO: This list should use the user's Pod instead of a hardcoded collection;
+ const classesCollection = useResource(getCollectionURL(classType));
+ let [options] = useArray(
+ classesCollection,
+ urls.properties.collection.members,
+ );
+ const requiredClass = useResource(classType);
+ const [classTypeTitle] = useTitle(requiredClass);
+ const store = useStore();
+ const {
+ dialogProps,
+ show: showDialog,
+ close: closeDialog,
+ isOpen: isDialogOpen,
+ } = useDialog();
+ const { drive } = useSettings();
+
+ const [
+ /** The value of the input underneath, updated through a callback */
+ inputValue,
+ setInputValue,
+ ] = useState(value || '');
+
+ const handleUpdate = useCallback(
+ (newval?: string) => {
+ // Pass the error setter for validation purposes
+ // Pass the Error handler to its parent, so validation errors appear locally
+ setSubject(newval, setError);
+ // Reset the error every time anything changes
+ // setError(null);
+ },
+ [setSubject],
+ );
+
+ const handleBlur = useCallback(() => {
+ value === undefined && handleUpdate(inputValue);
+ }, [inputValue, value]);
+
+ const isInDialogTree = useContext(DialogTreeContext);
+
+ if (options.length === 0) {
+ options = store.getAllSubjects();
+ }
+
+ let placeholder = 'Enter an Atomic URL...';
+
+ if (classType && classTypeTitle?.length > 0) {
+ placeholder = `Select a ${classTypeTitle} or enter a ${classTypeTitle} URL...`;
+ }
+
+ if (classType && !requiredClass.isReady()) {
+ placeholder = 'Loading Class...';
+ }
+
+ return (
+
+
+ {value && value !== '' && error && (
+ {error?.message}
+ )}
+ {!isInDialogTree && (
+
+ {isDialogOpen && (
+
+ )}
+
+ )}
+ {required && value === '' && Required }
+
+ );
+});
+
+/** For a given class URL, this tries to return a URL of a Collection containing these. */
+// TODO: Scope to current store / make adjustable https://github.com/atomicdata-dev/atomic-data-browser/issues/295
+export function getCollectionURL(classtypeUrl?: string): string | undefined {
+ switch (classtypeUrl) {
+ case urls.classes.property:
+ return 'https://atomicdata.dev/properties/?page_size=999';
+ case urls.classes.class:
+ return 'https://atomicdata.dev/classes/?page_size=999';
+ case urls.classes.agent:
+ return 'https://atomicdata.dev/agents/';
+ case urls.classes.commit:
+ return 'https://atomicdata.dev/commits';
+ case urls.classes.datatype:
+ return 'https://atomicdata.dev/datatypes';
+ default:
+ return undefined;
+ }
+}
+
+const Wrapper = styled.div`
+ flex: 1;
+ --radius: ${props => props.theme.radius};
+ ${InputWrapper} {
+ border-radius: 0;
+ }
+
+ &:first-of-type ${InputWrapper} {
+ border-top-left-radius: var(--radius);
+ border-top-right-radius: var(--radius);
+ }
+
+ &:last-of-type ${InputWrapper} {
+ border-bottom-left-radius: var(--radius);
+ border-bottom-right-radius: var(--radius);
+ }
+
+ &:not(:last-of-type) ${InputWrapper} {
+ border-bottom: none;
+ }
+`;
diff --git a/browser/data-browser/src/components/forms/ResourceSelector/ResourceSelector.tsx b/browser/data-browser/src/components/forms/ResourceSelector/ResourceSelector.tsx
index b0eb7b0df..d1ca39d2e 100644
--- a/browser/data-browser/src/components/forms/ResourceSelector/ResourceSelector.tsx
+++ b/browser/data-browser/src/components/forms/ResourceSelector/ResourceSelector.tsx
@@ -1,4 +1,4 @@
-import { useState, useMemo, memo, useContext } from 'react';
+import { memo, useContext, useMemo, useState } from 'react';
import { Dialog, useDialog } from '../../Dialog';
import { useSettings } from '../../../helpers/AppSettings';
import { css, styled } from 'styled-components';
@@ -103,7 +103,12 @@ export const ResourceSelector = memo(function ResourceSelector({
const [classSelectorOpen, setClassSelectorOpen] = useState(false);
const [titleProp, setTitleProp] = useState();
- const [dialogProps, showDialog, closeDialog, isDialogOpen] = useDialog({
+ const {
+ dialogProps,
+ show: showDialog,
+ close: closeDialog,
+ isOpen: isDialogOpen,
+ } = useDialog({
onSuccess: () => {
setSubject(pickedSubject);
},
diff --git a/browser/data-browser/src/helpers/AppSettings.tsx b/browser/data-browser/src/helpers/AppSettings.tsx
index fb52ca4eb..9248bba7e 100644
--- a/browser/data-browser/src/helpers/AppSettings.tsx
+++ b/browser/data-browser/src/helpers/AppSettings.tsx
@@ -47,7 +47,8 @@ export const AppSettingsContextProvider = (
const [agent, setAgent] = useCurrentAgent();
const [baseURL, setBaseURL] = useServerURL();
- const [drive, innerSetDrive] = useLocalStorage('drive', baseURL);
+ // By default, we want to use the current URL's origin with a trailing slash.
+ const [drive, innerSetDrive] = useLocalStorage('drive', baseURL + '/');
const setDrive = useCallback(
(newDrive: string) => {
diff --git a/browser/data-browser/src/hooks/useSavedDrives.ts b/browser/data-browser/src/hooks/useSavedDrives.ts
index a58e1b2eb..44c25ab88 100644
--- a/browser/data-browser/src/hooks/useSavedDrives.ts
+++ b/browser/data-browser/src/hooks/useSavedDrives.ts
@@ -4,9 +4,9 @@ import { isDev } from '../config';
import { useSettings } from '../helpers/AppSettings';
const rootDrives = [
- window.location.origin,
- 'https://atomicdata.dev',
- ...(isDev() ? ['http://localhost:9883'] : []),
+ window.location.origin + '/',
+ 'https://atomicdata.dev/',
+ ...(isDev() ? ['http://localhost:9883/'] : []),
];
const arrayOpts = {
diff --git a/browser/data-browser/src/hooks/useUpload.ts b/browser/data-browser/src/hooks/useUpload.ts
index c0b673436..f872d0b7b 100644
--- a/browser/data-browser/src/hooks/useUpload.ts
+++ b/browser/data-browser/src/hooks/useUpload.ts
@@ -1,6 +1,6 @@
import {
AtomicError,
- properties,
+ dataBrowser,
Resource,
useArray,
useStore,
@@ -25,7 +25,7 @@ export function useUpload(parentResource: Resource): UseUploadResult {
const [error, setError] = useState(undefined);
const [subResources, setSubResources] = useArray(
parentResource,
- properties.subResources,
+ dataBrowser.properties.subResources,
opts,
);
@@ -40,7 +40,8 @@ export function useUpload(parentResource: Resource): UseUploadResult {
);
const allUploaded = [...netUploaded];
setIsUploading(false);
- setSubResources([...subResources, ...allUploaded]);
+ await setSubResources([...subResources, ...allUploaded]);
+ await parentResource.save();
return allUploaded;
} catch (e) {
diff --git a/browser/data-browser/src/routes/ConfirmEmail.tsx b/browser/data-browser/src/routes/ConfirmEmail.tsx
new file mode 100644
index 000000000..d329af795
--- /dev/null
+++ b/browser/data-browser/src/routes/ConfirmEmail.tsx
@@ -0,0 +1,108 @@
+import { confirmEmail, useStore } from '@tomic/react';
+import * as React from 'react';
+import { useState } from 'react';
+import toast from 'react-hot-toast';
+import { Button } from '../components/Button';
+import { CodeBlockStyled } from '../components/CodeBlock';
+import { ContainerNarrow } from '../components/Containers';
+import { isDev } from '../config';
+import { useSettings } from '../helpers/AppSettings';
+import {
+ useCurrentSubject,
+ useSubjectParam,
+} from '../helpers/useCurrentSubject';
+import { paths } from './paths';
+
+/** Route that connects to `/confirm-email`, which confirms an email and creates a secret key. */
+const ConfirmEmail: React.FunctionComponent = () => {
+ // Value shown in navbar, after Submitting
+ const [subject] = useCurrentSubject();
+ const [secret, setSecret] = useState('');
+ const store = useStore();
+ const [token] = useSubjectParam('token');
+ const { setAgent } = useSettings();
+ const [destinationToGo, setDestination] = useState();
+ const [err, setErr] = useState(undefined);
+ const [triedConfirm, setTriedConfirm] = useState(false);
+
+ const handleConfirm = React.useCallback(async () => {
+ setTriedConfirm(true);
+ let tokenUrl = subject as string;
+
+ if (isDev()) {
+ const url = new URL(store.getServerUrl());
+ url.pathname = paths.confirmEmail;
+ url.searchParams.set('token', token as string);
+ tokenUrl = url.href;
+ }
+
+ try {
+ const { agent: newAgent, destination } = await confirmEmail(
+ store,
+ tokenUrl,
+ );
+ setSecret(newAgent.buildSecret());
+ setDestination(destination);
+ setAgent(newAgent);
+ toast.success('Email confirmed!');
+ } catch (e) {
+ setErr(e);
+ }
+ }, [subject]);
+
+ if (!triedConfirm && subject) {
+ handleConfirm();
+ }
+
+ if (err) {
+ if (err.message.includes('expired')) {
+ return (
+
+ The link has expired. Request a new one by Registering again.
+
+ );
+ }
+
+ return {err?.message} ;
+ }
+
+ if (secret) {
+ return ;
+ }
+
+ return Verifying token... ;
+};
+
+function SaveSecret({ secret, destination }) {
+ const [copied, setCopied] = useState(false);
+
+ function copyToClipboard() {
+ setCopied(secret);
+ navigator.clipboard.writeText(secret || '');
+ toast.success('Copied to clipboard');
+ }
+
+ return (
+
+ Mail confirmed, please save your Secret
+
+ Your Secret is like your password. Never share it with anyone. Use a
+ password manager like{' '}
+
+ BitWarden
+ {' '}
+ to store it securely.
+
+ {secret}
+ {copied ? (
+
+ {"I've saved my Secret, open my new Drive!"}
+
+ ) : (
+ Copy Secret
+ )}
+
+ );
+}
+
+export default ConfirmEmail;
diff --git a/browser/data-browser/src/routes/DataRoute.tsx b/browser/data-browser/src/routes/DataRoute.tsx
index 1aa41f022..3134e6fba 100644
--- a/browser/data-browser/src/routes/DataRoute.tsx
+++ b/browser/data-browser/src/routes/DataRoute.tsx
@@ -59,7 +59,7 @@ function Data(): JSX.Element {
setTextResponseLoading(true);
try {
- const resp = await window.fetch(subject!, { headers: headers });
+ const resp = await fetch(subject!, { headers: headers });
const body = await resp.text();
setTextResponseLoading(false);
setTextResponse(body);
diff --git a/browser/data-browser/src/routes/History/HistoryMobileView.tsx b/browser/data-browser/src/routes/History/HistoryMobileView.tsx
index b69ac8f6b..df0b57466 100644
--- a/browser/data-browser/src/routes/History/HistoryMobileView.tsx
+++ b/browser/data-browser/src/routes/History/HistoryMobileView.tsx
@@ -23,7 +23,7 @@ export function HistoryMobileView({
onSelectVersion,
onVersionAccept,
}: HistoryViewProps) {
- const [dialogProps, showDialog, closeDialog] = useDialog();
+ const { dialogProps, show: showDialog, close: closeDialog } = useDialog();
const handleVersionSelect = useCallback((version: Version) => {
onSelectVersion(version);
diff --git a/browser/data-browser/src/routes/NewResource/NewRoute.tsx b/browser/data-browser/src/routes/NewResource/NewRoute.tsx
index edb9e307c..b4b27a1fc 100644
--- a/browser/data-browser/src/routes/NewResource/NewRoute.tsx
+++ b/browser/data-browser/src/routes/NewResource/NewRoute.tsx
@@ -62,11 +62,13 @@ function NewResourceSelector() {
}
const onUploadComplete = useCallback(
- (files: string[]) => {
- toast.success(`Uploaded ${files.length} files.`);
+ (fileSubjects: string[]) => {
+ toast.success(`Uploaded ${fileSubjects.length} files.`);
- if (calculatedParent) {
- navigate(constructOpenURL(calculatedParent));
+ if (fileSubjects.length > 1 && parentSubject) {
+ navigate(constructOpenURL(parentSubject));
+ } else {
+ navigate(constructOpenURL(fileSubjects[0]));
}
},
[calculatedParent, navigate],
diff --git a/browser/data-browser/src/routes/Routes.tsx b/browser/data-browser/src/routes/Routes.tsx
index 3c4cc7d40..c1e11f927 100644
--- a/browser/data-browser/src/routes/Routes.tsx
+++ b/browser/data-browser/src/routes/Routes.tsx
@@ -11,7 +11,7 @@ import Data from './DataRoute';
import { Shortcuts } from './ShortcutsRoute';
import { About as About } from './AboutRoute';
import Local from './LocalRoute';
-import SettingsAgent from './SettingsAgent';
+import { SettingsAgentRoute } from './SettingsAgent';
import { SettingsServer } from './SettingsServer';
import { paths } from './paths';
import ResourcePage from '../views/ResourcePage';
@@ -21,8 +21,10 @@ import { TokenRoute } from './TokenRoute';
import { ImporterPage } from '../views/ImporterPage';
import { History } from './History';
import { PruneTestsRoute } from './PruneTestsRoute';
+import ConfirmEmail from './ConfirmEmail';
-const homeURL = window.location.origin;
+/** Server URLs should have a `/` at the end */
+const homeURL = window.location.origin + '/';
const isDev = import.meta.env.MODE === 'development';
@@ -37,7 +39,7 @@ export function AppRoutes(): JSX.Element {
} />
} />
- } />
+ } />
} />
} />
} />
@@ -51,6 +53,7 @@ export function AppRoutes(): JSX.Element {
} />
{isDev && } />}
{isDev && } />}
+ } />
} />
} />
diff --git a/browser/data-browser/src/routes/Sandbox.tsx b/browser/data-browser/src/routes/Sandbox.tsx
index 8685fc42f..f7ffb6e45 100644
--- a/browser/data-browser/src/routes/Sandbox.tsx
+++ b/browser/data-browser/src/routes/Sandbox.tsx
@@ -1,6 +1,15 @@
+import { Button } from '../components/Button';
import { ContainerFull } from '../components/Containers';
+import {
+ Dialog,
+ DialogContent,
+ DialogTitle,
+ useDialog,
+} from '../components/Dialog';
export function Sandbox(): JSX.Element {
+ const { dialogProps, show, isOpen } = useDialog();
+
return (
@@ -9,6 +18,12 @@ export function Sandbox(): JSX.Element {
Welcome to the sandbox. This is a place to test components in
isolation.
+ {isOpen ? 'TRUE' : 'FALSE'}
+ Button
+
+ Title
+ Content
+
);
diff --git a/browser/data-browser/src/routes/SearchRoute.tsx b/browser/data-browser/src/routes/SearchRoute.tsx
index b39175f96..958667526 100644
--- a/browser/data-browser/src/routes/SearchRoute.tsx
+++ b/browser/data-browser/src/routes/SearchRoute.tsx
@@ -95,7 +95,7 @@ export function Search(): JSX.Element {
}
if (loading) {
- message = 'Loading results...';
+ message = 'Loading results for';
}
if (results.length > 0) {
@@ -142,6 +142,7 @@ export function Search(): JSX.Element {
{results.map((subject, index) => (
{
+export function SettingsAgentRoute() {
const { agent, setAgent } = useSettings();
- const [subject, setSubject] = useState(undefined);
- const [privateKey, setPrivateKey] = useState(undefined);
- const [error, setError] = useState(undefined);
- const [showPrivateKey, setShowPrivateKey] = useState(false);
- const [advanced, setAdvanced] = useState(false);
- const [secret, setSecret] = useState(undefined);
const navigate = useNavigate();
- // When there is an agent, set the advanced values
- // Otherwise, reset the secret value
- React.useEffect(() => {
- if (agent !== undefined) {
- fillAdvanced();
- } else {
- setSecret('');
- }
- }, [agent]);
-
- // When the key or subject changes, update the secret
- React.useEffect(() => {
- renewSecret();
- }, [subject, privateKey]);
-
- function renewSecret() {
- if (agent) {
- setSecret(agent.buildSecret());
- }
- }
-
- function fillAdvanced() {
- try {
- if (!agent) {
- throw new Error('No agent set');
- }
-
- setSubject(agent.subject);
- setPrivateKey(agent.privateKey);
- } catch (e) {
- const err = new Error('Cannot fill subject and privatekey fields.' + e);
- setError(err);
- setSubject('');
- }
- }
-
function handleSignOut() {
if (
window.confirm(
@@ -74,196 +19,27 @@ const SettingsAgent: React.FunctionComponent = () => {
)
) {
setAgent(undefined);
- setError(undefined);
- setSubject('');
- setPrivateKey('');
- }
- }
-
- function setAgentIfChanged(oldAgent: Agent | undefined, newAgent: Agent) {
- if (JSON.stringify(oldAgent) !== JSON.stringify(newAgent)) {
- setAgent(newAgent);
- }
- }
-
- /** Called when the secret or the subject is updated manually */
- async function handleUpdateSubjectAndKey() {
- renewSecret();
- setError(undefined);
-
- try {
- const newAgent = new Agent(privateKey!, subject);
- await newAgent.getPublicKey();
- await newAgent.verifyPublicKeyWithServer();
-
- setAgentIfChanged(agent, newAgent);
- } catch (e) {
- const err = new Error('Invalid Agent' + e);
- setError(err);
- }
- }
-
- function handleCopy() {
- secret && navigator.clipboard.writeText(secret);
- }
-
- /** When the Secret updates, parse it and try if the */
- async function handleUpdateSecret(updateSecret: string) {
- setSecret(updateSecret);
-
- if (updateSecret === '') {
- setSecret('');
- setError(undefined);
-
- return;
- }
-
- setError(undefined);
-
- try {
- const newAgent = Agent.fromSecret(updateSecret);
- setAgentIfChanged(agent, newAgent);
- setPrivateKey(newAgent.privateKey);
- setSubject(newAgent.subject);
- // This will fail and throw if the agent is not public, which is by default
- // await newAgent.checkPublicKey();
- } catch (e) {
- const err = new Error('Invalid secret. ' + e);
- setError(err);
}
}
return (
-
-
-
-
-
+ >
+ )}
+
+
);
-};
-
-export default SettingsAgent;
+}
diff --git a/browser/data-browser/src/routes/paths.tsx b/browser/data-browser/src/routes/paths.tsx
index 66531d2a7..bd6b27b3e 100644
--- a/browser/data-browser/src/routes/paths.tsx
+++ b/browser/data-browser/src/routes/paths.tsx
@@ -15,6 +15,7 @@ export const paths = {
history: '/app/history',
allVersions: '/all-versions',
sandbox: '/sandbox',
+ confirmEmail: '/confirm-email',
fetchBookmark: '/fetch-bookmark',
pruneTests: '/prunetests',
};
diff --git a/browser/data-browser/src/views/Card/CollectionCard.tsx b/browser/data-browser/src/views/Card/CollectionCard.tsx
index 754999b5c..353d61cdf 100644
--- a/browser/data-browser/src/views/Card/CollectionCard.tsx
+++ b/browser/data-browser/src/views/Card/CollectionCard.tsx
@@ -16,7 +16,7 @@ const MAX_COUNT = 5;
* Renders a Resource and all its Properties in a random order. Title
* (shortname) is rendered prominently at the top.
*/
-function CollectionCard({ resource, small }: CardViewProps): JSX.Element {
+function CollectionCard({ resource }: CardViewProps): JSX.Element {
const [description] = useString(resource, core.properties.description);
const [members] = useArray(resource, collections.properties.members);
const [showAll, setShowMore] = useState(false);
@@ -32,38 +32,32 @@ function CollectionCard({ resource, small }: CardViewProps): JSX.Element {
{description && }
-
- {subjects.length === 0 ? (
- No resources
- ) : (
-
- {subjects.map(member => {
- return (
-
-
-
- );
- })}
- {tooMany && (
-
- setShowMore(!showAll)}>
- {showAll
- ? 'show less'
- : `show ${members.length - MAX_COUNT} more`}
-
+ {subjects.length === 0 ? (
+ No resources
+ ) : (
+
+ {subjects.map(member => {
+ return (
+
+
- )}
-
- )}
-
+ );
+ })}
+ {tooMany && (
+
+ setShowMore(!showAll)}>
+ {showAll
+ ? 'show less'
+ : `show ${members.length - MAX_COUNT} more`}
+
+
+ )}
+
+ )}
);
}
-const Show: FC> = ({ show, children }) => {
- return show ? children : null;
-};
-
const Empty = styled.span`
color: ${({ theme }) => theme.colors.textLight};
`;
diff --git a/browser/data-browser/src/views/Card/ResourceCard.tsx b/browser/data-browser/src/views/Card/ResourceCard.tsx
index a0a57d188..e5d6ea7e0 100644
--- a/browser/data-browser/src/views/Card/ResourceCard.tsx
+++ b/browser/data-browser/src/views/Card/ResourceCard.tsx
@@ -127,20 +127,22 @@ export function ResourceCardDefault({
{isAResource.title}
-
+
- {!small && (
-
- )}
+
);
}
@@ -148,8 +150,7 @@ export function ResourceCardDefault({
export default ResourceCard;
const DescriptionWrapper = styled.div`
- max-height: 10rem;
- overflow: hidden;
+ overflow: auto;
`;
const ClassName = styled.span`
diff --git a/browser/data-browser/src/views/ChatRoomPage.tsx b/browser/data-browser/src/views/ChatRoomPage.tsx
index ebd8eb281..562e30011 100644
--- a/browser/data-browser/src/views/ChatRoomPage.tsx
+++ b/browser/data-browser/src/views/ChatRoomPage.tsx
@@ -22,6 +22,7 @@ import { CommitDetail } from '../components/CommitDetail';
import Markdown from '../components/datatypes/Markdown';
import { Detail } from '../components/Detail';
import { EditableTitle } from '../components/EditableTitle';
+import { Guard } from '../components/Guard';
import { NavBarSpacer } from '../components/NavBarSpacer';
import { editURL } from '../helpers/navigation';
import { ResourceInline } from './ResourceInline';
@@ -147,25 +148,28 @@ export function ChatRoomPage({ resource }: ResourcePageProps) {
)}
-
-
-
- Send
-
-
+
+
+
+
+ Send
+
+
+
+
);
diff --git a/browser/data-browser/src/views/CodeUsage/ResourceCodeUsageDialog.tsx b/browser/data-browser/src/views/CodeUsage/ResourceCodeUsageDialog.tsx
index a00b091ce..f903aba5c 100644
--- a/browser/data-browser/src/views/CodeUsage/ResourceCodeUsageDialog.tsx
+++ b/browser/data-browser/src/views/CodeUsage/ResourceCodeUsageDialog.tsx
@@ -21,7 +21,7 @@ export function ResourceCodeUsageDialog({
bindShow,
}: ResourceCodeUsageDialogProps): React.JSX.Element {
const resource = useResource(subject);
- const [dialogProps, show, hide, isOpen] = useDialog({ bindShow });
+ const { dialogProps, show, close: hide, isOpen } = useDialog({ bindShow });
useEffect(() => {
if (open) {
diff --git a/browser/data-browser/src/views/CollectionPage.tsx b/browser/data-browser/src/views/CollectionPage.tsx
index e467a39c6..a83d7a85c 100644
--- a/browser/data-browser/src/views/CollectionPage.tsx
+++ b/browser/data-browser/src/views/CollectionPage.tsx
@@ -13,6 +13,7 @@ import {
FaArrowLeft,
FaArrowRight,
FaInfo,
+ FaPlus,
FaTable,
FaThLarge,
} from 'react-icons/fa';
@@ -171,8 +172,9 @@ function Collection({ resource }: ResourcePageProps): JSX.Element {
{isClass && (
diff --git a/browser/data-browser/src/views/CrashPage.tsx b/browser/data-browser/src/views/CrashPage.tsx
index 520f82586..e8e88fa04 100644
--- a/browser/data-browser/src/views/CrashPage.tsx
+++ b/browser/data-browser/src/views/CrashPage.tsx
@@ -2,7 +2,11 @@ import * as React from 'react';
import { Resource } from '@tomic/react';
import { ContainerWide } from '../components/Containers';
-import { ErrorBlock } from '../components/ErrorLook';
+import {
+ createGithubIssueLink,
+ ErrorBlock,
+ GitHubIssueButton,
+} from '../components/ErrorLook';
import { Button } from '../components/Button';
import { Column, Row } from '../components/Row';
@@ -26,6 +30,8 @@ function CrashPage({
{children ? children : }
+ Create Github issue
+
{clearError && Clear error }
@@ -35,7 +41,7 @@ function CrashPage({
)
}
>
- Try Again
+ Refresh page
diff --git a/browser/data-browser/src/views/DrivePage.tsx b/browser/data-browser/src/views/DrivePage.tsx
index 66843c550..b94b64b1e 100644
--- a/browser/data-browser/src/views/DrivePage.tsx
+++ b/browser/data-browser/src/views/DrivePage.tsx
@@ -5,7 +5,7 @@ import { Button } from '../components/Button';
import { useSettings } from '../helpers/AppSettings';
import { ResourcePageProps } from './ResourcePage';
import { EditableTitle } from '../components/EditableTitle';
-import { Column, Row } from '../components/Row';
+import { Row } from '../components/Row';
import { styled } from 'styled-components';
import InputSwitcher from '../components/forms/InputSwitcher';
import { WarningBlock } from '../components/WarningBlock';
@@ -23,38 +23,43 @@ function DrivePage({ resource }: ResourcePageProps): JSX.Element {
return (
-
-
-
- {baseURL !== resource.subject && (
- setBaseURL(resource.subject)}>
- Set as current drive
-
- )}
-
-
+
+ {baseURL !== resource.subject && (
+ setBaseURL(resource.subject)}>
+ Set as current drive
+
+ )}
+
+
+ Default Ontology
+
+
+
Default Ontology
+
-
- Default Ontology
-
-
- {baseURL.startsWith('http://localhost') && (
-
- You are running Atomic-Server on `localhost`, which means that it
- will not be available from any other machine than your current local
- device. If you want your Atomic-Server to be available from the web,
- you should set this up at a Domain on a server.
-
- )}
-
+
+ {baseURL.startsWith('http://localhost') && (
+
+ You are running Atomic-Server on `localhost`, which means that it will
+ not be available from any other machine than your current local
+ device. If you want your Atomic-Server to be available from the web,
+ you should set this up at a Domain on a server.
+
+ )}
);
}
diff --git a/browser/data-browser/src/views/ErrorPage.tsx b/browser/data-browser/src/views/ErrorPage.tsx
index 4be11ec79..b336d1e2a 100644
--- a/browser/data-browser/src/views/ErrorPage.tsx
+++ b/browser/data-browser/src/views/ErrorPage.tsx
@@ -1,14 +1,14 @@
import * as React from 'react';
import { isUnauthorized, useStore } from '@tomic/react';
import { ContainerWide } from '../components/Containers';
-import { ErrorBlock } from '../components/ErrorLook';
+import { ErrorBlock, GitHubIssueButton } from '../components/ErrorLook';
import { Button } from '../components/Button';
-import { SignInButton } from '../components/SignInButton';
import { useSettings } from '../helpers/AppSettings';
import { ResourcePageProps } from './ResourcePage';
import { Column, Row } from '../components/Row';
import CrashPage from './CrashPage';
import { clearAllLocalData } from '../helpers/clearData';
+import { Guard } from '../components/Guard';
/**
* A View for Resource Errors. Not to be confused with the CrashPage, which is
@@ -17,15 +17,28 @@ import { clearAllLocalData } from '../helpers/clearData';
function ErrorPage({ resource }: ResourcePageProps): JSX.Element {
const { agent } = useSettings();
const store = useStore();
- const subject = resource.getSubject();
+ const subject = resource.subject;
+
+ React.useEffect(() => {
+ // Try again when agent changes
+ store.fetchResourceFromServer(subject);
+ }, [agent]);
if (isUnauthorized(resource.error)) {
+ // This might be a bit too aggressive, but it fixes 'Unauthorized' messages after signing in to a new drive.
+ store.fetchResourceFromServer(subject);
+
return (
Unauthorized
{agent ? (
<>
+
+ {
+ "You don't have access to this. Try asking for access, or sign in with a different account."
+ }
+
store.fetchResourceFromServer(subject)}>
@@ -36,7 +49,7 @@ function ErrorPage({ resource }: ResourcePageProps): JSX.Element {
) : (
<>
{"You don't have access to this, try signing in:"}
-
+
>
)}
@@ -47,9 +60,10 @@ function ErrorPage({ resource }: ResourcePageProps): JSX.Element {
return (
- Could not open {resource.getSubject()}
-
+ Could not open {resource.subject}
+
+
store.fetchResourceFromServer(subject, { setLoading: true })
diff --git a/browser/data-browser/src/views/OntologyPage/CreateInstanceButton.tsx b/browser/data-browser/src/views/OntologyPage/CreateInstanceButton.tsx
index d639a2bd0..9744b434f 100644
--- a/browser/data-browser/src/views/OntologyPage/CreateInstanceButton.tsx
+++ b/browser/data-browser/src/views/OntologyPage/CreateInstanceButton.tsx
@@ -13,7 +13,7 @@ interface CreateInstanceButtonProps {
export function CreateInstanceButton({ ontology }: CreateInstanceButtonProps) {
const [classSelectorActive, setClassSelectorActive] = useState(false);
const [classSubject, setClassSubject] = useState();
- const [dialogProps, show, close, isOpen] = useDialog({
+ const { dialogProps, show, close, isOpen } = useDialog({
onSuccess: () => {
setClassSubject(undefined);
ontology.save();
diff --git a/browser/data-browser/src/views/OntologyPage/NewClassButton.tsx b/browser/data-browser/src/views/OntologyPage/NewClassButton.tsx
index 52b16ecc9..64912723b 100644
--- a/browser/data-browser/src/views/OntologyPage/NewClassButton.tsx
+++ b/browser/data-browser/src/views/OntologyPage/NewClassButton.tsx
@@ -29,7 +29,12 @@ export function NewClassButton({ resource }: NewClassButtonProps): JSX.Element {
const subject = subjectForClass(resource, inputValue);
- const [dialogProps, show, hide, isOpen] = useDialog({
+ const {
+ dialogProps,
+ show,
+ close: hide,
+ isOpen,
+ } = useDialog({
onSuccess: async () => {
const createdClass = await newClass(inputValue, resource, store);
const id = toAnchorId(createdClass);
diff --git a/browser/data-browser/src/views/OntologyPage/Property/PropertyLineWrite.tsx b/browser/data-browser/src/views/OntologyPage/Property/PropertyLineWrite.tsx
index 4d07b3fee..67da2d318 100644
--- a/browser/data-browser/src/views/OntologyPage/Property/PropertyLineWrite.tsx
+++ b/browser/data-browser/src/views/OntologyPage/Property/PropertyLineWrite.tsx
@@ -25,7 +25,7 @@ export function PropertyLineWrite({
const resource = useResource(subject);
const shortnameProp = useProperty(core.properties.shortname);
const descriptionProp = useProperty(core.properties.description);
- const [dialogProps, show, hide] = useDialog();
+ const { dialogProps, show, close: hide } = useDialog();
const [canEdit] = useCanWrite(resource);
const { hasProperty } = useOntologyContext();
diff --git a/browser/data-browser/src/views/OntologyPage/Property/PropertyWriteDialog.tsx b/browser/data-browser/src/views/OntologyPage/Property/PropertyWriteDialog.tsx
index 0d143c6cf..396983f45 100644
--- a/browser/data-browser/src/views/OntologyPage/Property/PropertyWriteDialog.tsx
+++ b/browser/data-browser/src/views/OntologyPage/Property/PropertyWriteDialog.tsx
@@ -25,7 +25,7 @@ export function PropertyWriteDialog({
return (
- {dialogProps.show && (
+ {dialogProps.isVisible && (
<>
`${parent.subject}/class/${shortName}`;
diff --git a/browser/data-browser/src/views/TablePage/ExpandedRowDialog.tsx b/browser/data-browser/src/views/TablePage/ExpandedRowDialog.tsx
index fe5076611..a32197400 100644
--- a/browser/data-browser/src/views/TablePage/ExpandedRowDialog.tsx
+++ b/browser/data-browser/src/views/TablePage/ExpandedRowDialog.tsx
@@ -30,11 +30,10 @@ export function ExpandedRowDialog({
}: ExpandedRowDialogProps): JSX.Element {
const { tableRef } = useTableEditorContext();
const resource = useResource(subject);
- const [dialogProps, show] = useDialog({
+ const { dialogProps, show } = useDialog({
bindShow: bindOpen,
triggerRef: tableRef,
});
-
useEffect(() => {
if (open) {
show();
diff --git a/browser/data-browser/src/views/TablePage/PropertyForm/EditPropertyDialog.tsx b/browser/data-browser/src/views/TablePage/PropertyForm/EditPropertyDialog.tsx
index 1b47c3f9d..8baf4db32 100644
--- a/browser/data-browser/src/views/TablePage/PropertyForm/EditPropertyDialog.tsx
+++ b/browser/data-browser/src/views/TablePage/PropertyForm/EditPropertyDialog.tsx
@@ -31,7 +31,7 @@ export function EditPropertyDialog({
resource.save();
}, [resource]);
- const [dialogProps, show, hide] = useDialog({ bindShow, onSuccess });
+ const { dialogProps, show, close: hide } = useDialog({ bindShow, onSuccess });
useEffect(() => {
if (showDialog) {
diff --git a/browser/data-browser/src/views/TablePage/PropertyForm/ExternalPropertyDialog.tsx b/browser/data-browser/src/views/TablePage/PropertyForm/ExternalPropertyDialog.tsx
index ab1e62480..52f8bb4a3 100644
--- a/browser/data-browser/src/views/TablePage/PropertyForm/ExternalPropertyDialog.tsx
+++ b/browser/data-browser/src/views/TablePage/PropertyForm/ExternalPropertyDialog.tsx
@@ -30,7 +30,7 @@ export function ExternalPropertyDialog({
urls.properties.recommends,
{ commit: true },
);
- const [dialogProps, show, hide] = useDialog({ bindShow });
+ const { dialogProps, show, close: hide } = useDialog({ bindShow });
const onAddClick = () => {
if (subject) {
diff --git a/browser/data-browser/src/views/TablePage/PropertyForm/NewPropertyDialog.tsx b/browser/data-browser/src/views/TablePage/PropertyForm/NewPropertyDialog.tsx
index da9dde3a2..d1538cd71 100644
--- a/browser/data-browser/src/views/TablePage/PropertyForm/NewPropertyDialog.tsx
+++ b/browser/data-browser/src/views/TablePage/PropertyForm/NewPropertyDialog.tsx
@@ -136,7 +136,11 @@ export function NewPropertyDialog({
setResource(null);
}, [resource, store, tableClassResource, pushProp]);
- const [dialogProps, show, hide] = useDialog({
+ const {
+ dialogProps,
+ show,
+ close: hide,
+ } = useDialog({
bindShow,
onCancel: handleUserCancelAction,
onSuccess: handleUserSuccessAction,
diff --git a/browser/data-browser/src/views/TablePage/TableExportDialog.tsx b/browser/data-browser/src/views/TablePage/TableExportDialog.tsx
index afc247b75..23498205e 100644
--- a/browser/data-browser/src/views/TablePage/TableExportDialog.tsx
+++ b/browser/data-browser/src/views/TablePage/TableExportDialog.tsx
@@ -33,7 +33,7 @@ export function TableExportDialog({
bindShow,
}: TableExportDialogProps): React.JSX.Element {
const store = useStore();
- const [dialogProps, showDialog] = useDialog({ bindShow });
+ const { dialogProps, show: showDialog } = useDialog({ bindShow });
const [refAsSubject, setRefAsSubject] = useState(false);
const url = buildLink(subject, refAsSubject, store);
diff --git a/browser/data-browser/tests/e2e.spec.ts b/browser/data-browser/tests/e2e.spec.ts
new file mode 100644
index 000000000..789d96198
--- /dev/null
+++ b/browser/data-browser/tests/e2e.spec.ts
@@ -0,0 +1,841 @@
+// This file is copied from `atomic-data-browser` to `atomic-data-server` when `pnpm build-server` is run.
+// This is why the `testConfig` is imported.
+
+import { test, expect } from '@playwright/test';
+import type { Browser, Page } from '@playwright/test';
+
+const demoFileName = 'testimage.svg';
+
+const demoFile = () => {
+ const processPath = process.cwd();
+
+ // In the CI, the tests dir is missing for some reason?
+ if (processPath.endsWith('tests')) {
+ return `${processPath}/${demoFileName}`;
+ } else {
+ return `${processPath}/tests/${demoFileName}`;
+ }
+};
+
+const testConfig: TestConfig = {
+ demoFileName,
+ demoFile: demoFile(),
+ demoInviteName: 'document demo',
+ serverUrl: 'http://localhost:9883',
+ frontEndUrl: process.env.FRONTEND_URL || 'http://localhost:5173',
+ initialTest: true,
+};
+
+export interface TestConfig {
+ demoFileName: string;
+ demoFile: string;
+ demoInviteName: string;
+ serverUrl: string;
+ frontEndUrl: string;
+ /** If /setup is used to register */
+ initialTest: boolean;
+}
+
+const {
+ demoFileName,
+ demoFile,
+ demoInviteName,
+ serverUrl,
+ frontEndUrl,
+ initialTest,
+} = testConfig;
+
+const timestamp = () => new Date().toLocaleTimeString();
+const editableTitle = '[data-test="editable-title"]';
+const sideBarDriveSwitcher = '[title="Open Drive Settings"]';
+const sideBarNewResource = '[data-test="sidebar-new-resource"]';
+const currentDriveTitle = '[data-test=current-drive-title]';
+const publicReadRight =
+ '[data-test="right-public"] input[type="checkbox"] >> nth=0';
+const contextMenu = '[data-test="context-menu"]';
+const addressBar = '[data-test="address-bar"]';
+const defaultDevServer = 'http://localhost:9883';
+const currentDialogOkButton = 'dialog[open] >> footer >> text=Ok';
+// Depends on server index throttle time, `commit_monitor.rs`
+const REBUILD_INDEX_TIME = 6000;
+
+async function setTitle(page, title: string) {
+ await page.locator(editableTitle).click();
+ await page.fill(editableTitle, title);
+ await page.waitForTimeout(300);
+}
+
+test.describe('data-browser', async () => {
+ test.beforeEach(async ({ page }) => {
+ if (!serverUrl) {
+ throw new Error('serverUrl is not set');
+ }
+
+ // Open the server
+ await page.goto(frontEndUrl);
+
+ // Sometimes we run the test server on a different port, but we should
+ // only change the drive if it is non-default.
+ if (serverUrl !== defaultDevServer) {
+ await changeDrive(serverUrl, page);
+ }
+
+ await expect(page.locator(currentDriveTitle)).toBeVisible();
+ });
+
+ test('sidebar mobile', async ({ page }) => {
+ await page.setViewportSize({ width: 500, height: 800 });
+ await page.reload();
+ // TODO: this keeps hanging. How do I make sure something is _not_ visible?
+ // await expect(page.locator('text=new resource')).not.toBeVisible();
+ await page.click('[data-test="sidebar-toggle"]');
+ await expect(await page.locator(currentDriveTitle)).toBeVisible();
+ });
+
+ test('switch Server URL', async ({ page }) => {
+ await expect(page.locator(`text=${demoInviteName}`)).not.toBeVisible();
+ await changeDrive('https://atomicdata.dev', page);
+ await expect(page.locator(`text=${demoInviteName}`).first()).toBeVisible();
+ });
+
+ test('sign in with secret, edit prole, sign out', async ({ page }) => {
+ await signIn(page);
+ await editProfileAndCommit(page);
+
+ page.on('dialog', d => {
+ d.accept();
+ });
+
+ // Sign out
+ await page.click('text=user settings');
+ await page.click('[data-test="sign-out"]');
+ await page.click('text=Sign in');
+ await expect(page.locator('#current-password')).toBeVisible();
+ await page.reload();
+ await page.click('text=Sign in');
+ await expect(page.locator('#current-password')).toBeVisible();
+ });
+
+ test('sign up and edit document atomicdata.dev', async ({ page }) => {
+ await openAtomic(page);
+ // Use invite
+ await page.click(`text=${demoInviteName}`);
+ await page.click('text=Accept as new user');
+ await expect(page.locator(editableTitle)).toBeVisible();
+ // We need the initial enter because removing the top line isn't working ATM
+ await page.keyboard.press('Enter');
+ const teststring = `Testline ${timestamp()}`;
+ await page.fill('[data-test="element-input"]', teststring);
+ // This next line can be flaky, maybe the text disappears because it's overwritten?
+ await expect(page.locator(`text=${teststring}`)).toBeVisible();
+ // Remove the text again for cleanup
+ await page.keyboard.press('Alt+Backspace');
+ await expect(page.locator(`text=${teststring}`)).not.toBeVisible();
+ const docTitle = `Document Title ${timestamp()}`;
+ await page.click(editableTitle, { delay: 200 });
+ await page.fill(editableTitle, docTitle);
+ // Not sure if this test is needed - it fails now.
+ // await expect(page.locator(documentTitle)).toBeFocused();
+ // Check if we can edit our profile
+ await editProfileAndCommit(page);
+ });
+
+ test('text search', async ({ page }) => {
+ await page.fill(addressBar, 'welcome');
+ await expect(page.locator('text=Welcome to your')).toBeVisible();
+ await page.keyboard.press('Enter');
+ await expect(page.locator('text=resources:')).toBeVisible();
+ });
+
+ test('scoped search', async ({ page }) => {
+ await signIn(page);
+ await newDrive(page);
+
+ // Create folder called 1
+ await page.locator('[data-test="sidebar-new-resource"]').click();
+ await page.locator('button:has-text("folder")').click();
+ await setTitle(page, 'Salad folder');
+
+ // Create document called 'Avocado Salad'
+ await page.locator('button:has-text("New Resource")').click();
+ await page.locator('button:has-text("document")').click();
+ await page.waitForResponse(`${serverUrl}/commit`);
+ // commit for initializing the first element (paragraph)
+ await page.waitForResponse(`${serverUrl}/commit`);
+ await editTitle('Avocado Salad', page);
+
+ await page.locator('[data-test="sidebar-new-resource"]').click();
+
+ // Create folder called 'Cake folder'
+ await page.locator('button:has-text("folder")').click();
+ await setTitle(page, 'Cake Folder');
+
+ // Create document called 'Avocado Salad'
+ await page.locator('button:has-text("New Resource")').click();
+ await page.locator('button:has-text("document")').click();
+ await page.waitForResponse(`${serverUrl}/commit`);
+ // commit for initializing the first element (paragraph)
+ await page.waitForResponse(`${serverUrl}/commit`);
+ await editTitle('Avocado Cake', page);
+
+ await clickSidebarItem('Cake Folder', page);
+
+ // Set search scope to 'Cake folder'
+ await page.waitForTimeout(REBUILD_INDEX_TIME);
+ await page.locator('button[title="Search in Cake Folder"]').click();
+ // Search for 'Avocado'
+ await page.locator('[data-test="address-bar"]').type('Avocado');
+ // I don't like the `.first` here, but for some reason there is one frame where
+ // Multiple hits render, which fails the tests.
+ await expect(page.locator('h2:text("Avocado Cake")').first()).toBeVisible();
+ await expect(page.locator('h2:text("Avocado Salad")')).not.toBeVisible();
+
+ // Remove scope
+ await page.locator('button[title="Clear scope"]').click();
+
+ await expect(page.locator('h2:text("Avocado Cake")').first()).toBeVisible();
+ await expect(
+ page.locator('h2:text("Avocado Salad")').first(),
+ ).toBeVisible();
+ });
+
+ test('collections & data view', async ({ page }) => {
+ await openAtomic(page);
+ // collections, pagination, sorting
+ await openSubject(page, 'https://atomicdata.dev/properties');
+ await page.click(
+ '[data-test="sort-https://atomicdata.dev/properties/description"]',
+ );
+ // These values can change as new Properties are added to atomicdata.dev
+ const firstPageText = 'text=A base64 serialized JSON object';
+ const secondPageText = 'text=include-nested';
+ await expect(page.locator(firstPageText)).toBeVisible();
+ await page.click('[data-test="next-page"]');
+ await expect(page.locator(firstPageText)).not.toBeVisible();
+ await expect(page.locator(secondPageText)).toBeVisible();
+
+ // context menu, keyboard & data view
+ await page.click(contextMenu);
+ await page.keyboard.press('ArrowDown');
+ await page.keyboard.press('Enter');
+ await expect(page.locator('text=JSON-AD')).toBeVisible();
+ await page.click('[data-test="fetch-json-ad"]');
+ await expect(
+ page.locator(
+ 'text="https://atomicdata.dev/properties/collection/members": [',
+ ),
+ ).toBeVisible();
+ await page.click('[data-test="fetch-json"]');
+ await expect(page.locator('text= "members": [')).toBeVisible();
+ await page.click('[data-test="fetch-json-ld"]');
+ await expect(page.locator('text="current-page": {')).toBeVisible();
+ await page.click('[data-test="fetch-turtle"]');
+ await expect(page.locator('text= {
+ if (initialTest) {
+ // Setup initial user (this test can only be run once per server)
+ await page.click('[data-test="sidebar-drive-open"]');
+ await expect(page.locator('text=/setup')).toBeVisible();
+ // Don't click on setup - this will take you to a different domain, not to the dev build!
+ // await page.click('text=/setup');
+ await openSubject(page, `${serverUrl}/setup`);
+ await expect(page.locator('text=Accept as')).toBeVisible();
+ // await page.click('[data-test="accept-existing"]');
+ await page.click('text=Accept as');
+ } else {
+ // eslint-disable-next-line no-console
+ console.log('Skipping `/setup` test...');
+ }
+ });
+
+ test('create document, edit, page title, websockets', async ({
+ page,
+ browser,
+ }) => {
+ await signIn(page);
+ await newDrive(page);
+ await makeDrivePublic(page);
+ // Create a document
+ await newResource('document', page);
+ // commit for saving initial document
+ await page.waitForResponse(`${serverUrl}/commit`);
+ // commit for initializing the first element (paragraph)
+ await page.waitForResponse(`${serverUrl}/commit`);
+ await page.locator(editableTitle).click();
+ const title = `Document ${timestamp()}`;
+ // These keys make sure the onChange handler is properly called
+ await page.keyboard.press('Space');
+ await page.keyboard.press('Backspace');
+ // await page.waitForTimeout(100);
+ // await page.fill(documentTitle, title);
+ await page.keyboard.type(title);
+
+ // commit for editing title
+ await page.waitForResponse(`${serverUrl}/commit`);
+ // await page.click('[data-test="document-edit"]');
+ // await expect(await page.title()).toEqual(title);
+ await page.press(editableTitle, 'Enter');
+ // await page.waitForTimeout(500);
+ const teststring = `My test: ${timestamp()}`;
+ await page.fill('textarea', teststring);
+ // commit editing paragraph
+ await expect(await page.locator(`text=${teststring}`)).toBeVisible();
+
+ // multi-user
+ const currentUrl = page.url();
+ const page2 = await openNewSubjectWindow(browser, currentUrl);
+ await expect(await page2.locator(`text=${teststring}`)).toBeVisible();
+ await expect(await page2.title()).toEqual(title);
+
+ // Add a new line on first page, check if it appears on the second
+ await page.keyboard.press('Enter');
+ await page.waitForResponse(`${serverUrl}/commit`);
+ await page.waitForResponse(`${serverUrl}/commit`);
+ const syncText = 'New paragraph';
+ await page.keyboard.type(syncText);
+ // If this fails to show up, websockets aren't working properly
+ await expect(await page2.locator(`text=${syncText}`)).toBeVisible();
+ });
+
+ /**
+ * We remove public read rights from drive, create an invite, open that
+ * invite, and add the public read right again.
+ */
+ test('authorization, invite, share menu', async ({
+ page,
+ browser,
+ context,
+ }) => {
+ // Remove public read rights for Drive
+ await signIn(page);
+ const { driveURL, driveTitle } = await newDrive(page);
+ await page.click(currentDriveTitle);
+ await contextMenuClick('share', page);
+ await expect(await page.isChecked(publicReadRight)).toBe(false);
+
+ // Initialize unauthorized page for reader
+ const context2 = await browser.newContext();
+ const page2 = await context2.newPage();
+ await page2.setViewportSize({ width: 1000, height: 400 });
+ await page2.goto(frontEndUrl);
+ await openSubject(page2, driveURL);
+ // TODO set current drive by opening the URL
+ await expect(await page2.locator('text=Unauthorized')).toBeVisible();
+
+ // Create invite
+ await page.click('button:has-text("Send invite")');
+ context.grantPermissions(['clipboard-read', 'clipboard-write']);
+ await page.click('button:has-text("Create Invite")');
+ await expect(
+ await page.locator('text=Invite created and copied '),
+ ).toBeVisible();
+ const inviteUrl = await page.evaluate(() =>
+ document
+ ?.querySelector('[data-code-content]')
+ ?.getAttribute('data-code-content'),
+ );
+ expect(inviteUrl).not.toBeFalsy();
+
+ // Open invite
+ const page3 = await openNewSubjectWindow(browser, inviteUrl as string);
+ await page3.click('button:has-text("Accept")');
+ await page3.waitForTimeout(200);
+ await page3.reload();
+ await expect(
+ await page3.locator(`text=${driveTitle}`).first(),
+ ).toBeVisible();
+ });
+
+ test('upload, download', async ({ page }) => {
+ await signIn(page);
+ await newDrive(page);
+ // add attachment to drive
+ await page.click(contextMenu);
+ await page.locator('[data-test="menu-item-edit"]').click();
+ const [fileChooser] = await Promise.all([
+ page.waitForEvent('filechooser'),
+ page.click('button:has-text("Upload file")'),
+ ]);
+ await fileChooser.setFiles(demoFile);
+ await page.click(`[data-test="file-pill"]:has-text("${demoFileName}")`);
+ const image = page.locator('[data-test="image-viewer"]');
+ await expect(image).toBeVisible();
+ await expect(image).toHaveScreenshot({ maxDiffPixelRatio: 0.1 });
+ });
+
+ test('chatroom', async ({ page, browser }) => {
+ await signIn(page);
+ await newDrive(page);
+ await newResource('chatroom', page);
+ await page.locator('text=New ChatRoom');
+ const teststring = `My test: ${timestamp()}`;
+ await page.fill('[data-test="message-input"]', teststring);
+ const chatRoomUrl = page.url();
+ await page.keyboard.press('Enter');
+ await expect(await page.locator(`text=${teststring}`)).toBeVisible();
+
+ const dropdownId = await page
+ .locator(contextMenu)
+ .getAttribute('aria-controls');
+
+ await page.click(contextMenu);
+ await page
+ .locator(`[id="${dropdownId}"] >> [data-test="menu-item-share"]`)
+ .click();
+ await page.locator(publicReadRight).click();
+ await page.click('text=save');
+
+ const page2 = await openNewSubjectWindow(browser, chatRoomUrl);
+ // Second user
+ await signIn(page2);
+ await expect(await page2.locator(`text=${teststring}`)).toBeVisible();
+ const teststring2 = `My reply: ${timestamp()}`;
+ await page2.fill('[data-test="message-input"]', teststring2);
+ await page2.keyboard.press('Enter');
+ // Both pages should see then new chat message
+ await expect(await page.locator(`text=${teststring2}`)).toBeVisible();
+ // TODO: get rid of this reload! It should not be necessary
+ // For some reason the page does not see the new message
+ await page2.reload();
+ await expect(await page2.locator(`text=${teststring2}`)).toBeVisible();
+ });
+
+ test('bookmark', async ({ page }) => {
+ await signIn(page);
+ await newDrive(page);
+
+ // Create a new bookmark
+ await newResource('bookmark', page);
+
+ // Fetch `example.com
+ const input = page.locator('[placeholder="https\\:\\/\\/example\\.com"]');
+ await input.click();
+ await input.fill('https://ontola.io');
+ await page.locator(currentDialogOkButton).click();
+
+ await expect(page.locator(':text-is("Full-service")')).toBeVisible();
+ });
+
+ test('folder', async ({ page }) => {
+ await signIn(page);
+ await newDrive(page);
+
+ // Create a new folder
+ await newResource('folder', page);
+ // Createa sub-resource
+ await page.click('[data-test="new-resource-folder"]');
+ await page.click('button:has-text("Document")');
+ await page.locator(editableTitle).click();
+ await page.keyboard.type('RAM Downloading Strategies');
+ await page.keyboard.press('Enter');
+ await page.click('[data-test="sidebar"] >> text=Untitled folder');
+ await expect(
+ page.locator(
+ '[data-test="folder-list"] >> text=RAM Downloading Strategies',
+ ),
+ 'Created document not visible',
+ ).toBeVisible();
+ });
+
+ test('drive switcher', async ({ page }) => {
+ await signIn(page);
+ await page.locator(`${currentDriveTitle} > text=localhost`);
+
+ await page.click(sideBarDriveSwitcher);
+ // temp disable for trailing slash
+ await page.click(`[id="${dropdownId}"] >> text=Atomic Data`);
+ await expect(page.locator(currentDriveTitle)).toHaveText('Atomic Data');
+ // .getAttribute('aria-controls');
+ // await page.click(`[id="${dropdownId}"] >> text=Atomic Data`);
+ // await expect(page.locator(currentDriveTitle)).toHaveText('Atomic Data');
+
+ // Cleanup drives for signed in user
+ await page.click('text=user settings');
+ await page.click('text=Edit profile');
+ await page.click('[data-test="input-drives-clear"]');
+ await page.click('[data-test="save"]');
+ });
+
+ test('configure drive page', async ({ page }) => {
+ await signIn(page);
+ await openDriveMenu(page);
+ await expect(page.locator(currentDriveTitle)).toHaveText('Main drive');
+
+ // temp disable this, because of trailing slash in base URL
+ // await page.click(':text("https://atomicdata.dev") + button:text("Select")');
+ // await expect(page.locator(currentDriveTitle)).toHaveText('Atomic Data');
+
+ await openDriveMenu(page);
+ await page.fill('[data-test="server-url-input"]', 'https://example.com');
+ await page.click('[data-test="server-url-save"]');
+
+ await expect(page.locator(currentDriveTitle)).toHaveText('example.com');
+
+ await openDriveMenu(page);
+ await page.click(':text("https://atomicdata.dev") + button:text("Select")');
+ await openDriveMenu(page);
+ await page.click(
+ ':text("https://example.com") ~ [title="Add to favorites"]',
+ );
+
+ await page.click(
+ ':text("https://example.com") ~ [title="Remove from favorites"]',
+ );
+ });
+
+ test('form validation', async ({ page }) => {
+ await signIn(page);
+ await newDrive(page);
+ await newResource('class', page);
+ const shortnameInput = '[data-test="input-shortname"]';
+ // Try entering a wrong slug
+ await page.click(shortnameInput);
+ await page.keyboard.type('not valid');
+ await expect(page.locator('text=Not a valid slug')).toBeVisible();
+ await page.locator(shortnameInput).fill('');
+ await page.keyboard.type('is-valid');
+ await expect(page.locator('text=Not a valid slug')).not.toBeVisible();
+
+ // Add a new property
+ await page.click(
+ '[placeholder="Select a property or enter a property URL..."]',
+ );
+ await page.keyboard.type(
+ 'https://atomicdata.dev/properties/invite/usagesLeft',
+ );
+ await page.keyboard.press('Enter');
+ await page.click('[title="Add this property"]');
+ await expect(page.locator('text=usages-left')).toBeVisible();
+ // Integer validation
+ await page.click('[data-test="input-usages-left"]');
+ await page.keyboard.type('asdf' + '1');
+ await expect(page.locator('text=asdf')).not.toBeVisible();
+ // Dropdown select
+ await page.click('[data-test="input-recommends-add-resource"]');
+ await page.locator('text=append').click();
+ await expect(
+ page.locator(
+ '[data-test="input-recommends"] >> text=https://atomicdata.dev',
+ ),
+ ).not.toBeVisible();
+
+ // Try to save without a description
+ page.locator('button:has-text("Save")').click();
+ await expect(
+ page.locator(
+ 'text=Property https://atomicdata.dev/properties/description missing',
+ ),
+ ).toBeVisible();
+
+ // Add a description
+ await page.click('textarea[name="yamdeContent"]');
+ await page.keyboard.type('This is a test class');
+ await page.click('button:has-text("Save")');
+
+ await expect(page.locator('text=Resource Saved')).toBeVisible();
+ });
+
+ test('sidebar subresource', async ({ page }) => {
+ await signIn(page);
+ await newDrive(page);
+
+ // create a resource, make sure its visible in the sidebar (and after refresh)
+ const klass = 'folder';
+ await newResource(klass, page);
+ await expect(
+ page.locator(`[data-test="sidebar"] >> text=${klass}`),
+ ).toBeVisible();
+ const d0 = 'depth0';
+ await setTitle(page, d0);
+
+ // Create a subresource, and later check it in the sidebar
+ await page.locator(`[data-test="sidebar"] >> text=${d0}`).hover();
+ await page.locator(`[title="Create new resource under ${d0}"]`).click();
+ await page.click(`button:has-text("${klass}")`);
+ const d1 = 'depth1';
+ await setTitle(page, d1);
+
+ await expect(
+ page.locator(`[data-test="sidebar"] >> text=${d1}`),
+ ).toBeVisible();
+ await expect(
+ page.locator(`[data-test="sidebar"] >> text=${d0}`),
+ ).toBeVisible();
+ await page.reload();
+ await expect(
+ page.locator(`[data-test="sidebar"] >> text=${d1}`),
+ ).toBeVisible();
+ await expect(
+ page.locator(`[data-test="sidebar"] >> text=${d0}`),
+ ).toBeVisible();
+ });
+
+ test('import', async ({ page }) => {
+ await signIn(page);
+ await newDrive(page);
+ await newResource('folder', page);
+ await contextMenuClick('import', page);
+
+ const localID = 'localIDtest';
+ const name = 'blaat';
+ const importStr = {
+ 'https://atomicdata.dev/properties/localId': localID,
+ 'https://atomicdata.dev/properties/name': name,
+ };
+ await page.fill(
+ '[placeholder="Paste your JSON-AD..."]',
+ JSON.stringify(importStr),
+ );
+ await page.click('[data-test="import-post"]');
+ await expect(page.locator('text=Imported!')).toBeVisible();
+
+ // get current url, append the localID
+ const url = await page.url();
+ await page.goto(url + '/' + localID);
+ await expect(page.locator(`text=${name}`)).toBeVisible();
+ });
+
+ test('dialog', async ({ page }) => {
+ await signIn(page);
+ await newDrive(page);
+ // Create new class from new resource menu
+ await newResource('class', page);
+
+ await fillInput('shortname', page);
+ await fillInput('description', page);
+ await page.click('[data-test="save"]');
+ await page.waitForNavigation();
+ await page.locator('text=Resource Saved');
+ await page.goBack();
+
+ await page
+ .locator('[title="Add an item to this list"] >> nth=0')
+ .first()
+ .click();
+ await page.locator('[data-test="input-recommends"]').click();
+ await page.locator('[data-test="input-recommends"]').fill('test-prop');
+
+ // Create new Property using dialog
+ await page.locator('text=Create property: test-prop').click();
+ await expect(page.locator('h1:has-text("new property")')).toBeVisible();
+ await page.locator('[data-test="input-datatype"]').click();
+ // click twice, first click is buggy, it closes the dropdown from earlier
+ await page.locator('[data-test="input-datatype"]').click();
+ await page
+ .locator(
+ 'li:has-text("boolean - Either `true` or `false`. In JSON-AD, th...")',
+ )
+ .click();
+ await page.locator('dialog textarea[name="yamdeContent"]').click();
+ await page
+ .locator('dialog textarea[name="yamdeContent"]')
+ .fill('This is a test prop');
+ await page.locator('dialog footer >> text=Save').click();
+
+ await page.locator('text=Resource Saved');
+ expect(
+ await page.locator(
+ '[data-test="input-recommends"] >> nth=0 >> "test-prop"',
+ ),
+ );
+ });
+
+ test('history page', async ({ page }) => {
+ await signIn(page);
+ await newDrive(page);
+ // Create new class from new resource menu
+ await newResource('document', page);
+
+ // commit for saving initial document
+ await page.waitForResponse(`${serverUrl}/commit`);
+ // commit for initializing the first element (paragraph)
+ await page.waitForResponse(`${serverUrl}/commit`);
+
+ await editTitle('First Title', page);
+ expect(page.locator('text=First Title')).toBeVisible();
+
+ await editTitle('Second Title', page, true);
+ expect(page.locator('text=Second Title')).toBeVisible();
+
+ await contextMenuClick('history', page);
+ expect(page.locator('text=History of Second Title')).toBeVisible();
+
+ await page.getByTestId('version-button').nth(1).click();
+
+ expect(page.locator('text=First Title')).toBeVisible();
+
+ await page.click('text=Make current version');
+
+ expect(page.locator('text=Resource version updated')).toBeVisible();
+ await page.waitForNavigation();
+ expect(page.locator('h1:has-text("First Title")')).toBeVisible();
+ expect(page.locator('text=History of First Title')).not.toBeVisible();
+ });
+});
+
+async function disableViewTransition(page: Page) {
+ await page.click('text=Theme Settings');
+ const checkbox = await page.getByLabel('Enable view transition');
+
+ await expect(checkbox).toBeVisible();
+
+ await checkbox.uncheck();
+ await page.goBack();
+}
+
+/** Signs in using an AtomicData.dev test user */
+async function signIn(page: Page) {
+ await disableViewTransition(page);
+ await page.click('text=user settings');
+ await expect(
+ await page.locator('text=edit data and sign Commits'),
+ ).toBeVisible();
+ // If there are any issues with this agent, try creating a new one https://atomicdata.dev/invites/1
+ const test_agent =
+ 'eyJzdWJqZWN0IjoiaHR0cHM6Ly9hdG9taWNkYXRhLmRldi9hZ2VudHMvaElNWHFoR3VLSDRkM0QrV1BjYzAwUHVFbldFMEtlY21GWStWbWNVR2tEWT0iLCJwcml2YXRlS2V5IjoiZkx0SDAvY29VY1BleFluNC95NGxFemFKbUJmZTYxQ3lEekUwODJyMmdRQT0ifQ==';
+ await page.click('text=Sign in');
+ await page.click('#current-password');
+ await page.fill('#current-password', test_agent);
+ await expect(await page.locator('text=Edit profile')).toBeVisible();
+ await page.goBack();
+}
+
+/**
+ * Create a new drive, go to it, and set it as the current drive. Returns URL of
+ * drive and its name
+ */
+async function newDrive(page: Page) {
+ // Create new drive to prevent polluting the main drive
+ await page.locator(sideBarDriveSwitcher).click();
+ await page.locator('button:has-text("New Drive")').click();
+ await page.waitForNavigation();
+ await expect(await page.locator('text="Create new resource"')).toBeVisible();
+ const driveURL = await getCurrentSubject(page);
+ await expect(driveURL).toContain('localhost');
+ const driveTitle = `testdrive-${timestamp()}`;
+ await page.locator(editableTitle).click();
+ await page.fill(editableTitle, driveTitle);
+ await page.waitForTimeout(200);
+
+ return { driveURL: driveURL as string, driveTitle };
+}
+
+async function makeDrivePublic(page: Page) {
+ await page.click(currentDriveTitle);
+ await page.click(contextMenu);
+ await page.click('button:has-text("share")');
+ await expect(await page.isChecked(publicReadRight)).toBe(false);
+ await page.click(publicReadRight);
+ await page.locator('text=Save').click();
+ await expect(await page.locator('text="Share settings saved"')).toBeVisible();
+}
+
+async function openSubject(page: Page, subject: string) {
+ await page.fill(addressBar, subject);
+}
+
+async function getCurrentSubject(page: Page) {
+ return page.locator(addressBar).getAttribute('value');
+}
+
+/** Set atomicdata.dev as current server */
+async function openAtomic(page: Page) {
+ await changeDrive('https://atomicdata.dev', page);
+ // Accept the invite, create an account if necessary
+ await expect(await page.locator(currentDriveTitle)).toHaveText('Atomic Data');
+}
+
+/** Opens the users' profile, sets a username */
+async function editProfileAndCommit(page: Page) {
+ await page.click('text=user settings');
+ await page.click('text=Edit profile');
+ await expect(page.locator('text=add another property')).toBeVisible();
+ const username = `Test user edited at ${new Date().toLocaleDateString()}`;
+ await page.fill('[data-test="input-name"]', username);
+ await page.click('[data-test="save"]');
+ await expect(page.locator('text=Resource saved')).toBeVisible();
+ await page.waitForURL(/\/app\/show/);
+ await expect(page.locator(`text=${username}`).first()).toBeVisible();
+}
+
+/** Create a new Resource in the current Drive */
+async function newResource(klass: string, page: Page) {
+ await page.locator(sideBarNewResource).click();
+ await expect(page).toHaveURL(`${frontEndUrl}/app/new`);
+ await page.locator(`button:has-text("${klass}")`).click();
+}
+
+/** Opens a new browser page (for) */
+async function openNewSubjectWindow(browser: Browser, url: string) {
+ const context2 = await browser.newContext();
+ const page = await context2.newPage();
+ await page.goto(frontEndUrl);
+
+ // Only when we run on `localhost` we don't need to change drive during tests
+ if (serverUrl !== defaultDevServer) {
+ await changeDrive(serverUrl, page);
+ }
+
+ await openSubject(page, url);
+ await page.setViewportSize({ width: 1000, height: 400 });
+
+ return page;
+}
+
+async function openDriveMenu(page: Page) {
+ await page.click(sideBarDriveSwitcher);
+ await page.click('[data-test="menu-item-configure-drives"]');
+}
+
+async function changeDrive(subject: string, page: Page) {
+ await openDriveMenu(page);
+ await expect(page.locator('text=Drive Configuration')).toBeVisible();
+ await page.fill('[data-test="server-url-input"]', subject);
+ await page.click('[data-test="server-url-save"]');
+ await expect(page.locator('text=Create new resource')).toBeVisible();
+}
+
+async function editTitle(title: string, page: Page, clear = false) {
+ await page.locator(editableTitle).click();
+
+ if (clear) {
+ await page.locator(editableTitle).clear();
+ }
+
+ // These keys make sure the onChange handler is properly called
+ await page.keyboard.press('Space');
+ await page.keyboard.press('Backspace');
+ await page.keyboard.type(title);
+ await page.waitForResponse(`${serverUrl}/commit`);
+}
+
+async function clickSidebarItem(text: string, page: Page) {
+ await page.click(`[data-test="sidebar"] >> text="${text}"`);
+}
+
+async function fillInput(
+ propertyShortname: string,
+ page: Page,
+ value?: string,
+) {
+ let locator = `[data-test="input-${propertyShortname}"]`;
+
+ if (propertyShortname === 'description') {
+ locator = 'textarea[name="yamdeContent"]';
+ }
+
+ await page.click(locator);
+ await page.fill(locator, value || `test-${propertyShortname}`);
+}
+
+/** Click an item from the main, visible context menu */
+async function contextMenuClick(text: string, page: Page) {
+ await page.click(contextMenu);
+ await page
+ .locator(`[data-test="menu-item-${text}"] >> visible = true`)
+ .click();
+}
diff --git a/browser/deno.lock b/browser/deno.lock
new file mode 100644
index 000000000..771ef7e51
--- /dev/null
+++ b/browser/deno.lock
@@ -0,0 +1,8382 @@
+{
+ "version": "4",
+ "specifiers": {
+ "npm:@types/node@^20.11.5": "20.16.10",
+ "npm:@typescript-eslint/eslint-plugin@^7.8.0": "7.18.0_@typescript-eslint+parser@7.18.0__eslint@8.57.1__typescript@5.6.2_eslint@8.57.1_typescript@5.6.2",
+ "npm:@typescript-eslint/parser@^7.8.0": "7.18.0_eslint@8.57.1_typescript@5.6.2",
+ "npm:eslint-config-prettier@^9.1.0": "9.1.0_eslint@8.57.1",
+ "npm:eslint-plugin-import@^2.29.1": "2.31.0_eslint@8.57.1",
+ "npm:eslint-plugin-jsx-a11y@^6.6.1": "6.10.0_eslint@8.57.1",
+ "npm:eslint-plugin-prettier@^5.1.3": "5.2.1_eslint@8.57.1_eslint-config-prettier@9.1.0__eslint@8.57.1_prettier@3.2.5",
+ "npm:eslint-plugin-react-hooks@^4.3.0": "4.6.2_eslint@8.57.1",
+ "npm:eslint-plugin-react@^7.28.0": "7.37.1_eslint@8.57.1",
+ "npm:eslint@^8.57.0": "8.57.1",
+ "npm:husky@^8.0.3": "8.0.3",
+ "npm:netlify-cli@16.2.0": "16.2.0_inquirer@6.5.2",
+ "npm:prettier-plugin-jsdoc@^1.3.0": "1.3.0_prettier@3.2.5",
+ "npm:prettier@3.2.5": "3.2.5",
+ "npm:typedoc-plugin-missing-exports@^2.1.0": "2.3.0_typedoc@0.25.13__typescript@5.4.5",
+ "npm:typedoc@~0.25.3": "0.25.13_typescript@5.4.5",
+ "npm:typescript@^5.4.5": "5.6.2",
+ "npm:vite@^5.0.12": "5.4.8_@types+node@20.16.10",
+ "npm:vitest@^2.0.5": "2.1.1_@types+node@20.16.10_@vitest+spy@2.1.1_vite@5.4.8__@types+node@20.16.10"
+ },
+ "npm": {
+ "@babel/code-frame@7.25.7": {
+ "integrity": "sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==",
+ "dependencies": [
+ "@babel/highlight",
+ "picocolors"
+ ]
+ },
+ "@babel/helper-string-parser@7.25.7": {
+ "integrity": "sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g=="
+ },
+ "@babel/helper-validator-identifier@7.25.7": {
+ "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg=="
+ },
+ "@babel/highlight@7.25.7": {
+ "integrity": "sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==",
+ "dependencies": [
+ "@babel/helper-validator-identifier",
+ "chalk@2.4.2",
+ "js-tokens",
+ "picocolors"
+ ]
+ },
+ "@babel/parser@7.25.7": {
+ "integrity": "sha512-aZn7ETtQsjjGG5HruveUK06cU3Hljuhd9Iojm4M8WWv3wLE6OkE5PWbDUkItmMgegmccaITudyuW5RPYrYlgWw==",
+ "dependencies": [
+ "@babel/types@7.25.7"
+ ]
+ },
+ "@babel/types@7.25.6": {
+ "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==",
+ "dependencies": [
+ "@babel/helper-string-parser",
+ "@babel/helper-validator-identifier",
+ "to-fast-properties"
+ ]
+ },
+ "@babel/types@7.25.7": {
+ "integrity": "sha512-vwIVdXG+j+FOpkwqHRcBgHLYNL7XMkufrlaFvL9o6Ai9sJn9+PdyIL5qa0XzTZw084c+u9LOls53eoZWP/W5WQ==",
+ "dependencies": [
+ "@babel/helper-string-parser",
+ "@babel/helper-validator-identifier",
+ "to-fast-properties"
+ ]
+ },
+ "@bugsnag/browser@7.25.0": {
+ "integrity": "sha512-PzzWy5d9Ly1CU1KkxTB6ZaOw/dO+CYSfVtqxVJccy832e6+7rW/dvSw5Jy7rsNhgcKSKjZq86LtNkPSvritOLA==",
+ "dependencies": [
+ "@bugsnag/core"
+ ]
+ },
+ "@bugsnag/core@7.25.0": {
+ "integrity": "sha512-JZLak1b5BVzy77CPcklViZrppac/pE07L3uSDmfSvFYSCGReXkik2txOgV05VlF9EDe36dtUAIIV7iAPDfFpQQ==",
+ "dependencies": [
+ "@bugsnag/cuid",
+ "@bugsnag/safe-json-stringify",
+ "error-stack-parser",
+ "iserror",
+ "stack-generator"
+ ]
+ },
+ "@bugsnag/cuid@3.1.1": {
+ "integrity": "sha512-d2z4b0rEo3chI07FNN1Xds8v25CNeekecU6FC/2Fs9MxY2EipkZTThVcV2YinMn8dvRUlViKOyC50evoUxg8tw=="
+ },
+ "@bugsnag/js@7.20.2": {
+ "integrity": "sha512-Q08k0h0h6NFwFGkFmib39Uln2WpvJdqT1EGF1JlyYiGW03Y+VopVb9r37pZrRrN9IY08mxaIEO8la5xeaWAs6A==",
+ "dependencies": [
+ "@bugsnag/browser",
+ "@bugsnag/node"
+ ]
+ },
+ "@bugsnag/node@7.25.0": {
+ "integrity": "sha512-KlxBaJ8EREEsfKInybAjTO9LmdDXV3cUH5+XNXyqUZrcRVuPOu4j4xvljh+n24ifok/wbFZTKVXUzrN4iKIeIA==",
+ "dependencies": [
+ "@bugsnag/core",
+ "byline",
+ "error-stack-parser",
+ "iserror",
+ "pump@3.0.0",
+ "stack-generator"
+ ]
+ },
+ "@bugsnag/safe-json-stringify@6.0.0": {
+ "integrity": "sha512-htzFO1Zc57S8kgdRK9mLcPVTW1BY2ijfH7Dk2CeZmspTWKdKqSo1iwmqrq2WtRjFlo8aRZYgLX0wFrDXF/9DLA=="
+ },
+ "@colors/colors@1.5.0": {
+ "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ=="
+ },
+ "@colors/colors@1.6.0": {
+ "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA=="
+ },
+ "@cspotcode/source-map-support@0.8.1": {
+ "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
+ "dependencies": [
+ "@jridgewell/trace-mapping"
+ ]
+ },
+ "@dabh/diagnostics@2.0.3": {
+ "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==",
+ "dependencies": [
+ "colorspace",
+ "enabled",
+ "kuler"
+ ]
+ },
+ "@dependents/detective-less@4.1.0": {
+ "integrity": "sha512-KrkT6qO5NxqNfy68sBl6CTSoJ4SNDIS5iQArkibhlbGU4LaDukZ3q2HIkh8aUKDio6o4itU4xDR7t82Y2eP1Bg==",
+ "dependencies": [
+ "gonzales-pe",
+ "node-source-walk"
+ ]
+ },
+ "@esbuild/aix-ppc64@0.19.11": {
+ "integrity": "sha512-FnzU0LyE3ySQk7UntJO4+qIiQgI7KoODnZg5xzXIrFJlKd2P2gwHsHY4927xj9y5PJmJSzULiUCWmv7iWnNa7g=="
+ },
+ "@esbuild/aix-ppc64@0.21.5": {
+ "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="
+ },
+ "@esbuild/android-arm64@0.19.11": {
+ "integrity": "sha512-aiu7K/5JnLj//KOnOfEZ0D90obUkRzDMyqd/wNAUQ34m4YUPVhRZpnqKV9uqDGxT7cToSDnIHsGooyIczu9T+Q=="
+ },
+ "@esbuild/android-arm64@0.19.2": {
+ "integrity": "sha512-lsB65vAbe90I/Qe10OjkmrdxSX4UJDjosDgb8sZUKcg3oefEuW2OT2Vozz8ef7wrJbMcmhvCC+hciF8jY/uAkw=="
+ },
+ "@esbuild/android-arm64@0.21.5": {
+ "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="
+ },
+ "@esbuild/android-arm@0.19.11": {
+ "integrity": "sha512-5OVapq0ClabvKvQ58Bws8+wkLCV+Rxg7tUVbo9xu034Nm536QTII4YzhaFriQ7rMrorfnFKUsArD2lqKbFY4vw=="
+ },
+ "@esbuild/android-arm@0.19.2": {
+ "integrity": "sha512-tM8yLeYVe7pRyAu9VMi/Q7aunpLwD139EY1S99xbQkT4/q2qa6eA4ige/WJQYdJ8GBL1K33pPFhPfPdJ/WzT8Q=="
+ },
+ "@esbuild/android-arm@0.21.5": {
+ "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="
+ },
+ "@esbuild/android-x64@0.19.11": {
+ "integrity": "sha512-eccxjlfGw43WYoY9QgB82SgGgDbibcqyDTlk3l3C0jOVHKxrjdc9CTwDUQd0vkvYg5um0OH+GpxYvp39r+IPOg=="
+ },
+ "@esbuild/android-x64@0.19.2": {
+ "integrity": "sha512-qK/TpmHt2M/Hg82WXHRc/W/2SGo/l1thtDHZWqFq7oi24AjZ4O/CpPSu6ZuYKFkEgmZlFoa7CooAyYmuvnaG8w=="
+ },
+ "@esbuild/android-x64@0.21.5": {
+ "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="
+ },
+ "@esbuild/darwin-arm64@0.19.11": {
+ "integrity": "sha512-ETp87DRWuSt9KdDVkqSoKoLFHYTrkyz2+65fj9nfXsaV3bMhTCjtQfw3y+um88vGRKRiF7erPrh/ZuIdLUIVxQ=="
+ },
+ "@esbuild/darwin-arm64@0.19.2": {
+ "integrity": "sha512-Ora8JokrvrzEPEpZO18ZYXkH4asCdc1DLdcVy8TGf5eWtPO1Ie4WroEJzwI52ZGtpODy3+m0a2yEX9l+KUn0tA=="
+ },
+ "@esbuild/darwin-arm64@0.21.5": {
+ "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="
+ },
+ "@esbuild/darwin-x64@0.19.11": {
+ "integrity": "sha512-fkFUiS6IUK9WYUO/+22omwetaSNl5/A8giXvQlcinLIjVkxwTLSktbF5f/kJMftM2MJp9+fXqZ5ezS7+SALp4g=="
+ },
+ "@esbuild/darwin-x64@0.19.2": {
+ "integrity": "sha512-tP+B5UuIbbFMj2hQaUr6EALlHOIOmlLM2FK7jeFBobPy2ERdohI4Ka6ZFjZ1ZYsrHE/hZimGuU90jusRE0pwDw=="
+ },
+ "@esbuild/darwin-x64@0.21.5": {
+ "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="
+ },
+ "@esbuild/freebsd-arm64@0.19.11": {
+ "integrity": "sha512-lhoSp5K6bxKRNdXUtHoNc5HhbXVCS8V0iZmDvyWvYq9S5WSfTIHU2UGjcGt7UeS6iEYp9eeymIl5mJBn0yiuxA=="
+ },
+ "@esbuild/freebsd-arm64@0.19.2": {
+ "integrity": "sha512-YbPY2kc0acfzL1VPVK6EnAlig4f+l8xmq36OZkU0jzBVHcOTyQDhnKQaLzZudNJQyymd9OqQezeaBgkTGdTGeQ=="
+ },
+ "@esbuild/freebsd-arm64@0.21.5": {
+ "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="
+ },
+ "@esbuild/freebsd-x64@0.19.11": {
+ "integrity": "sha512-JkUqn44AffGXitVI6/AbQdoYAq0TEullFdqcMY/PCUZ36xJ9ZJRtQabzMA+Vi7r78+25ZIBosLTOKnUXBSi1Kw=="
+ },
+ "@esbuild/freebsd-x64@0.19.2": {
+ "integrity": "sha512-nSO5uZT2clM6hosjWHAsS15hLrwCvIWx+b2e3lZ3MwbYSaXwvfO528OF+dLjas1g3bZonciivI8qKR/Hm7IWGw=="
+ },
+ "@esbuild/freebsd-x64@0.21.5": {
+ "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="
+ },
+ "@esbuild/linux-arm64@0.19.11": {
+ "integrity": "sha512-LneLg3ypEeveBSMuoa0kwMpCGmpu8XQUh+mL8XXwoYZ6Be2qBnVtcDI5azSvh7vioMDhoJFZzp9GWp9IWpYoUg=="
+ },
+ "@esbuild/linux-arm64@0.19.2": {
+ "integrity": "sha512-ig2P7GeG//zWlU0AggA3pV1h5gdix0MA3wgB+NsnBXViwiGgY77fuN9Wr5uoCrs2YzaYfogXgsWZbm+HGr09xg=="
+ },
+ "@esbuild/linux-arm64@0.21.5": {
+ "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="
+ },
+ "@esbuild/linux-arm@0.19.11": {
+ "integrity": "sha512-3CRkr9+vCV2XJbjwgzjPtO8T0SZUmRZla+UL1jw+XqHZPkPgZiyWvbDvl9rqAN8Zl7qJF0O/9ycMtjU67HN9/Q=="
+ },
+ "@esbuild/linux-arm@0.19.2": {
+ "integrity": "sha512-Odalh8hICg7SOD7XCj0YLpYCEc+6mkoq63UnExDCiRA2wXEmGlK5JVrW50vZR9Qz4qkvqnHcpH+OFEggO3PgTg=="
+ },
+ "@esbuild/linux-arm@0.21.5": {
+ "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="
+ },
+ "@esbuild/linux-ia32@0.19.11": {
+ "integrity": "sha512-caHy++CsD8Bgq2V5CodbJjFPEiDPq8JJmBdeyZ8GWVQMjRD0sU548nNdwPNvKjVpamYYVL40AORekgfIubwHoA=="
+ },
+ "@esbuild/linux-ia32@0.19.2": {
+ "integrity": "sha512-mLfp0ziRPOLSTek0Gd9T5B8AtzKAkoZE70fneiiyPlSnUKKI4lp+mGEnQXcQEHLJAcIYDPSyBvsUbKUG2ri/XQ=="
+ },
+ "@esbuild/linux-ia32@0.21.5": {
+ "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="
+ },
+ "@esbuild/linux-loong64@0.19.11": {
+ "integrity": "sha512-ppZSSLVpPrwHccvC6nQVZaSHlFsvCQyjnvirnVjbKSHuE5N24Yl8F3UwYUUR1UEPaFObGD2tSvVKbvR+uT1Nrg=="
+ },
+ "@esbuild/linux-loong64@0.19.2": {
+ "integrity": "sha512-hn28+JNDTxxCpnYjdDYVMNTR3SKavyLlCHHkufHV91fkewpIyQchS1d8wSbmXhs1fiYDpNww8KTFlJ1dHsxeSw=="
+ },
+ "@esbuild/linux-loong64@0.21.5": {
+ "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="
+ },
+ "@esbuild/linux-mips64el@0.19.11": {
+ "integrity": "sha512-B5x9j0OgjG+v1dF2DkH34lr+7Gmv0kzX6/V0afF41FkPMMqaQ77pH7CrhWeR22aEeHKaeZVtZ6yFwlxOKPVFyg=="
+ },
+ "@esbuild/linux-mips64el@0.19.2": {
+ "integrity": "sha512-KbXaC0Sejt7vD2fEgPoIKb6nxkfYW9OmFUK9XQE4//PvGIxNIfPk1NmlHmMg6f25x57rpmEFrn1OotASYIAaTg=="
+ },
+ "@esbuild/linux-mips64el@0.21.5": {
+ "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="
+ },
+ "@esbuild/linux-ppc64@0.19.11": {
+ "integrity": "sha512-MHrZYLeCG8vXblMetWyttkdVRjQlQUb/oMgBNurVEnhj4YWOr4G5lmBfZjHYQHHN0g6yDmCAQRR8MUHldvvRDA=="
+ },
+ "@esbuild/linux-ppc64@0.19.2": {
+ "integrity": "sha512-dJ0kE8KTqbiHtA3Fc/zn7lCd7pqVr4JcT0JqOnbj4LLzYnp+7h8Qi4yjfq42ZlHfhOCM42rBh0EwHYLL6LEzcw=="
+ },
+ "@esbuild/linux-ppc64@0.21.5": {
+ "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="
+ },
+ "@esbuild/linux-riscv64@0.19.11": {
+ "integrity": "sha512-f3DY++t94uVg141dozDu4CCUkYW+09rWtaWfnb3bqe4w5NqmZd6nPVBm+qbz7WaHZCoqXqHz5p6CM6qv3qnSSQ=="
+ },
+ "@esbuild/linux-riscv64@0.19.2": {
+ "integrity": "sha512-7Z/jKNFufZ/bbu4INqqCN6DDlrmOTmdw6D0gH+6Y7auok2r02Ur661qPuXidPOJ+FSgbEeQnnAGgsVynfLuOEw=="
+ },
+ "@esbuild/linux-riscv64@0.21.5": {
+ "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="
+ },
+ "@esbuild/linux-s390x@0.19.11": {
+ "integrity": "sha512-A5xdUoyWJHMMlcSMcPGVLzYzpcY8QP1RtYzX5/bS4dvjBGVxdhuiYyFwp7z74ocV7WDc0n1harxmpq2ePOjI0Q=="
+ },
+ "@esbuild/linux-s390x@0.19.2": {
+ "integrity": "sha512-U+RinR6aXXABFCcAY4gSlv4CL1oOVvSSCdseQmGO66H+XyuQGZIUdhG56SZaDJQcLmrSfRmx5XZOWyCJPRqS7g=="
+ },
+ "@esbuild/linux-s390x@0.21.5": {
+ "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="
+ },
+ "@esbuild/linux-x64@0.19.11": {
+ "integrity": "sha512-grbyMlVCvJSfxFQUndw5mCtWs5LO1gUlwP4CDi4iJBbVpZcqLVT29FxgGuBJGSzyOxotFG4LoO5X+M1350zmPA=="
+ },
+ "@esbuild/linux-x64@0.19.2": {
+ "integrity": "sha512-oxzHTEv6VPm3XXNaHPyUTTte+3wGv7qVQtqaZCrgstI16gCuhNOtBXLEBkBREP57YTd68P0VgDgG73jSD8bwXQ=="
+ },
+ "@esbuild/linux-x64@0.21.5": {
+ "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="
+ },
+ "@esbuild/netbsd-x64@0.19.11": {
+ "integrity": "sha512-13jvrQZJc3P230OhU8xgwUnDeuC/9egsjTkXN49b3GcS5BKvJqZn86aGM8W9pd14Kd+u7HuFBMVtrNGhh6fHEQ=="
+ },
+ "@esbuild/netbsd-x64@0.19.2": {
+ "integrity": "sha512-WNa5zZk1XpTTwMDompZmvQLHszDDDN7lYjEHCUmAGB83Bgs20EMs7ICD+oKeT6xt4phV4NDdSi/8OfjPbSbZfQ=="
+ },
+ "@esbuild/netbsd-x64@0.21.5": {
+ "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="
+ },
+ "@esbuild/openbsd-x64@0.19.11": {
+ "integrity": "sha512-ysyOGZuTp6SNKPE11INDUeFVVQFrhcNDVUgSQVDzqsqX38DjhPEPATpid04LCoUr2WXhQTEZ8ct/EgJCUDpyNw=="
+ },
+ "@esbuild/openbsd-x64@0.19.2": {
+ "integrity": "sha512-S6kI1aT3S++Dedb7vxIuUOb3oAxqxk2Rh5rOXOTYnzN8JzW1VzBd+IqPiSpgitu45042SYD3HCoEyhLKQcDFDw=="
+ },
+ "@esbuild/openbsd-x64@0.21.5": {
+ "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="
+ },
+ "@esbuild/sunos-x64@0.19.11": {
+ "integrity": "sha512-Hf+Sad9nVwvtxy4DXCZQqLpgmRTQqyFyhT3bZ4F2XlJCjxGmRFF0Shwn9rzhOYRB61w9VMXUkxlBy56dk9JJiQ=="
+ },
+ "@esbuild/sunos-x64@0.19.2": {
+ "integrity": "sha512-VXSSMsmb+Z8LbsQGcBMiM+fYObDNRm8p7tkUDMPG/g4fhFX5DEFmjxIEa3N8Zr96SjsJ1woAhF0DUnS3MF3ARw=="
+ },
+ "@esbuild/sunos-x64@0.21.5": {
+ "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="
+ },
+ "@esbuild/win32-arm64@0.19.11": {
+ "integrity": "sha512-0P58Sbi0LctOMOQbpEOvOL44Ne0sqbS0XWHMvvrg6NE5jQ1xguCSSw9jQeUk2lfrXYsKDdOe6K+oZiwKPilYPQ=="
+ },
+ "@esbuild/win32-arm64@0.19.2": {
+ "integrity": "sha512-5NayUlSAyb5PQYFAU9x3bHdsqB88RC3aM9lKDAz4X1mo/EchMIT1Q+pSeBXNgkfNmRecLXA0O8xP+x8V+g/LKg=="
+ },
+ "@esbuild/win32-arm64@0.21.5": {
+ "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="
+ },
+ "@esbuild/win32-ia32@0.19.11": {
+ "integrity": "sha512-6YOrWS+sDJDmshdBIQU+Uoyh7pQKrdykdefC1avn76ss5c+RN6gut3LZA4E2cH5xUEp5/cA0+YxRaVtRAb0xBg=="
+ },
+ "@esbuild/win32-ia32@0.19.2": {
+ "integrity": "sha512-47gL/ek1v36iN0wL9L4Q2MFdujR0poLZMJwhO2/N3gA89jgHp4MR8DKCmwYtGNksbfJb9JoTtbkoe6sDhg2QTA=="
+ },
+ "@esbuild/win32-ia32@0.21.5": {
+ "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="
+ },
+ "@esbuild/win32-x64@0.19.11": {
+ "integrity": "sha512-vfkhltrjCAb603XaFhqhAF4LGDi2M4OrCRrFusyQ+iTLQ/o60QQXxc9cZC/FFpihBI9N1Grn6SMKVJ4KP7Fuiw=="
+ },
+ "@esbuild/win32-x64@0.19.2": {
+ "integrity": "sha512-tcuhV7ncXBqbt/Ybf0IyrMcwVOAPDckMK9rXNHtF17UTK18OKLpg08glminN06pt2WCoALhXdLfSPbVvK/6fxw=="
+ },
+ "@esbuild/win32-x64@0.21.5": {
+ "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="
+ },
+ "@eslint-community/eslint-utils@4.4.0_eslint@8.57.1": {
+ "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+ "dependencies": [
+ "eslint",
+ "eslint-visitor-keys"
+ ]
+ },
+ "@eslint-community/regexpp@4.11.1": {
+ "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q=="
+ },
+ "@eslint/eslintrc@2.1.4": {
+ "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
+ "dependencies": [
+ "ajv@6.12.6",
+ "debug@4.3.7",
+ "espree",
+ "globals",
+ "ignore",
+ "import-fresh",
+ "js-yaml",
+ "minimatch@3.1.2",
+ "strip-json-comments@3.1.1"
+ ]
+ },
+ "@eslint/js@8.57.1": {
+ "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q=="
+ },
+ "@fastify/accept-negotiator@1.1.0": {
+ "integrity": "sha512-OIHZrb2ImZ7XG85HXOONLcJWGosv7sIvM2ifAPQVhg9Lv7qdmMBNVaai4QTdyuaqbKM5eO6sLSQOYI7wEQeCJQ=="
+ },
+ "@fastify/ajv-compiler@3.6.0_ajv@8.17.1": {
+ "integrity": "sha512-LwdXQJjmMD+GwLOkP7TVC68qa+pSSogeWWmznRJ/coyTcfe9qA05AHFSe1eZFwK6q+xVRpChnvFUkf1iYaSZsQ==",
+ "dependencies": [
+ "ajv@8.17.1",
+ "ajv-formats@2.1.1_ajv@8.17.1",
+ "fast-uri@2.4.0"
+ ]
+ },
+ "@fastify/error@3.4.1": {
+ "integrity": "sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ=="
+ },
+ "@fastify/fast-json-stringify-compiler@4.3.0": {
+ "integrity": "sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==",
+ "dependencies": [
+ "fast-json-stringify"
+ ]
+ },
+ "@fastify/merge-json-schemas@0.1.1": {
+ "integrity": "sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==",
+ "dependencies": [
+ "fast-deep-equal"
+ ]
+ },
+ "@fastify/send@2.1.0": {
+ "integrity": "sha512-yNYiY6sDkexoJR0D8IDy3aRP3+L4wdqCpvx5WP+VtEU58sn7USmKynBzDQex5X42Zzvw2gNzzYgP90UfWShLFA==",
+ "dependencies": [
+ "@lukeed/ms",
+ "escape-html",
+ "fast-decode-uri-component",
+ "http-errors@2.0.0",
+ "mime@3.0.0"
+ ]
+ },
+ "@fastify/static@6.10.2": {
+ "integrity": "sha512-UoaMvIHSBLCZBYOVZwFRYqX2ufUhd7FFMYGDeSf0Z+D8jhYtwljjmuQGuanUP8kS4y/ZEV1a8mfLha3zNwsnnQ==",
+ "dependencies": [
+ "@fastify/accept-negotiator",
+ "@fastify/send",
+ "content-disposition",
+ "fastify-plugin",
+ "glob@8.1.0",
+ "p-limit@3.1.0",
+ "readable-stream@4.5.2"
+ ]
+ },
+ "@grpc/grpc-js@1.12.0": {
+ "integrity": "sha512-eWdP97A6xKtZXVP/ze9y8zYRB2t6ugQAuLXFuZXAsyqmyltaAjl4yPkmIfc0wuTFJMOUF1AdvIFQCL7fMtaX6g==",
+ "dependencies": [
+ "@grpc/proto-loader",
+ "@js-sdsl/ordered-map"
+ ]
+ },
+ "@grpc/proto-loader@0.7.13": {
+ "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==",
+ "dependencies": [
+ "lodash.camelcase",
+ "long@5.2.3",
+ "protobufjs",
+ "yargs"
+ ]
+ },
+ "@honeycombio/opentelemetry-node@0.4.0_@opentelemetry+api@1.9.0": {
+ "integrity": "sha512-6PFX8FGW7uA7vQ3mxNIoN36rH9Zx5kXh4kKP9zu28nynyWyy9JE3l8PNJYd9FS2L/d88ZUpQAiQ1pROaANd5MA==",
+ "dependencies": [
+ "@grpc/grpc-js",
+ "@opentelemetry/api@1.9.0",
+ "@opentelemetry/exporter-metrics-otlp-grpc",
+ "@opentelemetry/exporter-metrics-otlp-proto",
+ "@opentelemetry/exporter-trace-otlp-grpc@0.36.1_@opentelemetry+api@1.9.0",
+ "@opentelemetry/exporter-trace-otlp-proto@0.36.1_@opentelemetry+api@1.9.0",
+ "@opentelemetry/resources@1.26.0_@opentelemetry+api@1.9.0",
+ "@opentelemetry/sdk-metrics@1.26.0_@opentelemetry+api@1.9.0",
+ "@opentelemetry/sdk-node",
+ "@opentelemetry/sdk-trace-base@1.26.0_@opentelemetry+api@1.9.0",
+ "axios"
+ ]
+ },
+ "@humanwhocodes/config-array@0.13.0": {
+ "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==",
+ "dependencies": [
+ "@humanwhocodes/object-schema",
+ "debug@4.3.7",
+ "minimatch@3.1.2"
+ ]
+ },
+ "@humanwhocodes/module-importer@1.0.1": {
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="
+ },
+ "@humanwhocodes/momoa@2.0.4": {
+ "integrity": "sha512-RE815I4arJFtt+FVeU1Tgp9/Xvecacji8w/V6XtXsWWH/wz/eNkNbhb+ny/+PlVZjV0rxQpRSQKNKE3lcktHEA=="
+ },
+ "@humanwhocodes/object-schema@2.0.3": {
+ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA=="
+ },
+ "@import-maps/resolve@1.0.1": {
+ "integrity": "sha512-tWZNBIS1CoekcwlMuyG2mr0a1Wo5lb5lEHwwWvZo+5GLgr3e9LLDTtmgtCWEwBpXMkxn9D+2W9j2FY6eZQq0tA=="
+ },
+ "@isaacs/cliui@8.0.2": {
+ "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+ "dependencies": [
+ "string-width@5.1.2",
+ "string-width-cjs@npm:string-width@4.2.3",
+ "strip-ansi@7.1.0",
+ "strip-ansi-cjs@npm:strip-ansi@6.0.1",
+ "wrap-ansi@8.1.0",
+ "wrap-ansi-cjs@npm:wrap-ansi@7.0.0"
+ ]
+ },
+ "@jest/types@27.5.1": {
+ "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==",
+ "dependencies": [
+ "@types/istanbul-lib-coverage",
+ "@types/istanbul-reports",
+ "@types/node@22.5.4",
+ "@types/yargs",
+ "chalk@4.1.2"
+ ]
+ },
+ "@jridgewell/resolve-uri@3.1.2": {
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="
+ },
+ "@jridgewell/sourcemap-codec@1.5.0": {
+ "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="
+ },
+ "@jridgewell/trace-mapping@0.3.9": {
+ "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
+ "dependencies": [
+ "@jridgewell/resolve-uri",
+ "@jridgewell/sourcemap-codec"
+ ]
+ },
+ "@js-sdsl/ordered-map@4.4.2": {
+ "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw=="
+ },
+ "@lukeed/ms@2.0.2": {
+ "integrity": "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA=="
+ },
+ "@mapbox/node-pre-gyp@1.0.11": {
+ "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==",
+ "dependencies": [
+ "detect-libc",
+ "https-proxy-agent",
+ "make-dir@3.1.0",
+ "node-fetch@2.6.12",
+ "nopt",
+ "npmlog",
+ "rimraf",
+ "semver@7.6.3",
+ "tar"
+ ]
+ },
+ "@netlify/binary-info@1.0.0": {
+ "integrity": "sha512-4wMPu9iN3/HL97QblBsBay3E1etIciR84izI3U+4iALY+JHCrI+a2jO0qbAZ/nxKoegypYEaiiqWXylm+/zfrw=="
+ },
+ "@netlify/build-info@7.7.4": {
+ "integrity": "sha512-dJt4looPD2UuGprGB29Y/tXAHKKV05gWOIsY71gByv7fs2DQ3LqXYXy4qdGcrGAAeEKRHBwXOMfAn1TAq4hgMQ==",
+ "dependencies": [
+ "@bugsnag/js",
+ "dot-prop@7.2.0",
+ "find-up@6.3.0",
+ "minimatch@9.0.5",
+ "read-pkg",
+ "semver@7.6.3",
+ "toml",
+ "yaml",
+ "yargs"
+ ]
+ },
+ "@netlify/build@29.20.8_typescript@5.6.2": {
+ "integrity": "sha512-dPO62SIzn4KkaVDLC9UPzOs8vyBD36wz4DcjSNFhxJ0ASp51HneJaWFQi0vZon4nizCob7iBKVm4QpgqAkeTjA==",
+ "dependencies": [
+ "@bugsnag/js",
+ "@honeycombio/opentelemetry-node",
+ "@netlify/cache-utils",
+ "@netlify/config",
+ "@netlify/edge-bundler",
+ "@netlify/framework-info",
+ "@netlify/functions-utils",
+ "@netlify/git-utils",
+ "@netlify/plugins-list",
+ "@netlify/run-utils",
+ "@netlify/zip-it-and-ship-it@9.17.0",
+ "@opentelemetry/api@1.9.0",
+ "@sindresorhus/slugify",
+ "ansi-escapes@6.2.0",
+ "chalk@5.2.0",
+ "clean-stack",
+ "execa@6.1.0",
+ "fdir",
+ "figures@5.0.0",
+ "filter-obj@5.1.0",
+ "got",
+ "hot-shots",
+ "indent-string@5.0.0",
+ "is-plain-obj@4.1.0",
+ "js-yaml",
+ "keep-func-props",
+ "locate-path@7.2.0",
+ "log-process-errors",
+ "map-obj",
+ "memoize-one",
+ "os-name",
+ "p-event@5.0.1",
+ "p-every",
+ "p-filter",
+ "p-locate@6.0.0",
+ "p-reduce",
+ "path-exists@5.0.0",
+ "path-type@5.0.0",
+ "pkg-dir",
+ "pretty-ms",
+ "ps-list",
+ "read-pkg-up",
+ "readdirp@3.6.0",
+ "resolve@2.0.0-next.5",
+ "rfdc",
+ "safe-json-stringify",
+ "semver@7.6.3",
+ "string-width@5.1.2",
+ "strip-ansi@7.1.0",
+ "supports-color@9.4.0",
+ "terminal-link",
+ "ts-node",
+ "typescript@5.6.2",
+ "uuid@9.0.0",
+ "yargs"
+ ]
+ },
+ "@netlify/cache-utils@5.1.6": {
+ "integrity": "sha512-0K1+5umxENy9H3CC+v5qGQbeTmKv/PBAhOxPKK6GPykOVa7OxT26KGMU7Jozo6pVNeLPJUvCCMw48ycwtQ1fvw==",
+ "dependencies": [
+ "cpy",
+ "get-stream@6.0.1",
+ "globby@13.2.2",
+ "junk",
+ "locate-path@7.2.0",
+ "move-file",
+ "path-exists@5.0.0",
+ "readdirp@3.6.0"
+ ]
+ },
+ "@netlify/config@20.8.0": {
+ "integrity": "sha512-jzklg2Kj9D/2h+QO2MNbbc7oz9Wo56Zp1ob/kaG9P7DJLZSgc0h6G2GQSybqKqvApLju+8iqPB2rMAp02QSjpA==",
+ "dependencies": [
+ "chalk@5.2.0",
+ "cron-parser",
+ "deepmerge",
+ "dot-prop@7.2.0",
+ "execa@6.1.0",
+ "fast-safe-stringify",
+ "figures@5.0.0",
+ "filter-obj@5.1.0",
+ "find-up@6.3.0",
+ "indent-string@5.0.0",
+ "is-plain-obj@4.1.0",
+ "js-yaml",
+ "map-obj",
+ "netlify",
+ "netlify-headers-parser",
+ "netlify-redirect-parser",
+ "node-fetch@3.3.2",
+ "omit.js",
+ "p-locate@6.0.0",
+ "path-type@5.0.0",
+ "toml",
+ "tomlify-j0.4",
+ "validate-npm-package-name",
+ "yargs"
+ ]
+ },
+ "@netlify/edge-bundler@8.19.0_ajv@8.17.1": {
+ "integrity": "sha512-blIZHLXlEXcjpAhd2TJ+Rw7H+WhNXSBfmFfRHn2pyzNiAbQa71eU0eNUE2+Nw58DGfvUC0unerbTlN2gd3iVdA==",
+ "dependencies": [
+ "@import-maps/resolve",
+ "ajv@8.17.1",
+ "ajv-errors",
+ "better-ajv-errors",
+ "common-path-prefix",
+ "env-paths",
+ "execa@6.1.0",
+ "find-up@6.3.0",
+ "get-port@6.1.2",
+ "is-path-inside@4.0.0",
+ "jsonc-parser",
+ "node-fetch@3.3.2",
+ "node-stream-zip",
+ "p-retry",
+ "p-wait-for@4.1.0",
+ "path-key@4.0.0",
+ "regexp-tree",
+ "semver@7.6.3",
+ "tmp-promise",
+ "urlpattern-polyfill",
+ "uuid@9.0.0"
+ ]
+ },
+ "@netlify/framework-info@9.8.13": {
+ "integrity": "sha512-ZZXCggokY/y5Sz93XYbl/Lig1UAUSWPMBiQRpkVfbrrkjmW2ZPkYS/BgrM2/MxwXRvYhc/TQpZX6y5JPe3quQg==",
+ "dependencies": [
+ "ajv@8.17.1",
+ "filter-obj@5.1.0",
+ "find-up@6.3.0",
+ "is-plain-obj@4.1.0",
+ "locate-path@7.2.0",
+ "p-filter",
+ "p-locate@6.0.0",
+ "process@0.11.10",
+ "read-pkg-up",
+ "semver@7.6.3"
+ ]
+ },
+ "@netlify/functions-utils@5.2.86": {
+ "integrity": "sha512-aG4hAeRJ3NVUCBImxIaI4H1/OR9vQ4iaLhRd/dPCEgKccQyw2/EOjuGNg28tkhqwTNnCLpN3uDM2KK5n02IXUg==",
+ "dependencies": [
+ "@netlify/zip-it-and-ship-it@9.39.5",
+ "cpy",
+ "path-exists@5.0.0"
+ ]
+ },
+ "@netlify/git-utils@5.1.1": {
+ "integrity": "sha512-oyHieuTZH3rKTmg7EKpGEGa28IFxta2oXuVwpPJI/FJAtBje3UE+yko0eDjNufgm3AyGa8G77trUxgBhInAYuw==",
+ "dependencies": [
+ "execa@6.1.0",
+ "map-obj",
+ "micromatch@4.0.8",
+ "moize",
+ "path-exists@5.0.0"
+ ]
+ },
+ "@netlify/local-functions-proxy-darwin-arm64@1.1.1": {
+ "integrity": "sha512-lphJ9qqZ3glnKWEqlemU1LMqXxtJ/tKf7VzakqqyjigwLscXSZSb6fupSjQfd4tR1xqxA76ylws/2HDhc/gs+Q=="
+ },
+ "@netlify/local-functions-proxy-darwin-x64@1.1.1": {
+ "integrity": "sha512-4CRB0H+dXZzoEklq5Jpmg+chizXlVwCko94d8+UHWCgy/bA3M/rU/BJ8OLZisnJaAktHoeLABKtcLOhtRHpxZQ=="
+ },
+ "@netlify/local-functions-proxy-freebsd-arm64@1.1.1": {
+ "integrity": "sha512-u13lWTVMJDF0A6jX7V4N3HYGTIHLe5d1Z2wT43fSIHwXkTs6UXi72cGSraisajG+5JFIwHfPr7asw5vxFC0P9w=="
+ },
+ "@netlify/local-functions-proxy-freebsd-x64@1.1.1": {
+ "integrity": "sha512-g5xw4xATK5YDzvXtzJ8S1qSkWBiyF8VVRehXPMOAMzpGjCX86twYhWp8rbAk7yA1zBWmmWrWNA2Odq/MgpKJJg=="
+ },
+ "@netlify/local-functions-proxy-linux-arm64@1.1.1": {
+ "integrity": "sha512-dPGu1H5n8na7mBKxiXQ+FNmthDAiA57wqgpm5JMAHtcdcmRvcXwJkwWVGvwfj8ShhYJHQaSaS9oPgO+mpKkgmA=="
+ },
+ "@netlify/local-functions-proxy-linux-arm@1.1.1": {
+ "integrity": "sha512-YsTpL+AbHwQrfHWXmKnwUrJBjoUON363nr6jUG1ueYnpbbv6wTUA7gI5snMi/gkGpqFusBthAA7C30e6bixfiA=="
+ },
+ "@netlify/local-functions-proxy-linux-ia32@1.1.1": {
+ "integrity": "sha512-Ra0FlXDrmPRaq+rYH3/ttkXSrwk1D5Zx/Na7UPfJZxMY7Qo5iY4bgi/FuzjzWzlp0uuKZOhYOYzYzsIIyrSvmw=="
+ },
+ "@netlify/local-functions-proxy-linux-ppc64@1.1.1": {
+ "integrity": "sha512-oXf1satwqwUUxz7LHS1BxbRqc4FFEKIDFTls04eXiLReFR3sqv9H/QuYNTCCDMuRcCOd92qKyDfATdnxT4HR8w=="
+ },
+ "@netlify/local-functions-proxy-linux-x64@1.1.1": {
+ "integrity": "sha512-bS3u4JuDg/eC0y4Na3i/29JBOxrdUvsK5JSjHfzUeZEbOcuXYf4KavTpHS5uikdvTgyczoSrvbmQJ5m0FLXfLA=="
+ },
+ "@netlify/local-functions-proxy-openbsd-x64@1.1.1": {
+ "integrity": "sha512-1xLef/kLRNkBTXJ+ZGoRFcwsFxd/B2H3oeJZyXaZ3CN5umd9Mv9wZuAD74NuMt/535yRva8jtAJqvEgl9xMSdA=="
+ },
+ "@netlify/local-functions-proxy-win32-ia32@1.1.1": {
+ "integrity": "sha512-4IOMDBxp2f8VbIkhZ85zGNDrZR4ey8d68fCMSOIwitjsnKav35YrCf8UmAh3UR6CNIRJdJL4MW1GYePJ7iJ8uA=="
+ },
+ "@netlify/local-functions-proxy-win32-x64@1.1.1": {
+ "integrity": "sha512-VCBXBJWBujVxyo5f+3r8ovLc9I7wJqpmgDn3ixs1fvdrER5Ac+SzYwYH4mUug9HI08mzTSAKZErzKeuadSez3w=="
+ },
+ "@netlify/local-functions-proxy@1.1.1": {
+ "integrity": "sha512-eXSsayLT6PMvjzFQpjC9nkg2Otc3lZ5GoYele9M6f8PmsvWpaXRhwjNQ0NYhQQ2UZbLMIiO2dH8dbRsT3bMkFw==",
+ "dependencies": [
+ "@netlify/local-functions-proxy-darwin-arm64",
+ "@netlify/local-functions-proxy-darwin-x64",
+ "@netlify/local-functions-proxy-freebsd-arm64",
+ "@netlify/local-functions-proxy-freebsd-x64",
+ "@netlify/local-functions-proxy-linux-arm",
+ "@netlify/local-functions-proxy-linux-arm64",
+ "@netlify/local-functions-proxy-linux-ia32",
+ "@netlify/local-functions-proxy-linux-ppc64",
+ "@netlify/local-functions-proxy-linux-x64",
+ "@netlify/local-functions-proxy-openbsd-x64",
+ "@netlify/local-functions-proxy-win32-ia32",
+ "@netlify/local-functions-proxy-win32-x64"
+ ]
+ },
+ "@netlify/node-cookies@0.1.0": {
+ "integrity": "sha512-OAs1xG+FfLX0LoRASpqzVntVV/RpYkgpI0VrUnw2u0Q1qiZUzcPffxRK8HF3gc4GjuhG5ahOEMJ9bswBiZPq0g=="
+ },
+ "@netlify/open-api@2.34.0": {
+ "integrity": "sha512-C4v7Od/vnGgZ1P4JK3Fn9uUi9HkTxeUqUtj4OLnGD+rGyaVrl4JY89xMCoVksijDtO8XylYFU59CSTnQNeNw7g=="
+ },
+ "@netlify/plugins-list@6.80.0": {
+ "integrity": "sha512-bCKLI51UZ70ziIWsf2nvgPd4XuG6m8AMCoHiYtl/BSsiaSBfmryZnTTqdRXerH09tBRpbPPwzaEgUJwyU9o8Qw=="
+ },
+ "@netlify/run-utils@5.1.1": {
+ "integrity": "sha512-V2B8ZB19heVKa715uOeDkztxLH7uaqZ+9U5fV7BRzbQ2514DO5Vxj9hG0irzuRLfZXZZjp/chPUesv4VVsce/A==",
+ "dependencies": [
+ "execa@6.1.0"
+ ]
+ },
+ "@netlify/serverless-functions-api@1.26.1": {
+ "integrity": "sha512-q3L9i3HoNfz0SGpTIS4zTcKBbRkxzCRpd169eyiTuk3IwcPC3/85mzLHranlKo2b+HYT0gu37YxGB45aD8A3Tw==",
+ "dependencies": [
+ "@netlify/node-cookies",
+ "urlpattern-polyfill"
+ ]
+ },
+ "@netlify/serverless-functions-api@1.7.3": {
+ "integrity": "sha512-n6/7cJlSWvvbBlUOEAbkGyEld80S6KbG/ldQI9OhLfe1lTatgKmrTNIgqVNpaWpUdTgP2OHWFjmFBzkxxBWs5w==",
+ "dependencies": [
+ "@netlify/node-cookies",
+ "urlpattern-polyfill"
+ ]
+ },
+ "@netlify/zip-it-and-ship-it@9.17.0": {
+ "integrity": "sha512-7wnrWxtczXzBMYh9QXmvG9WkCJSyK+abQGdhwSoZcFPQ0u0HZzY/9rU8jLcRnTodEK20lZPil60FRU/Nta5spg==",
+ "dependencies": [
+ "@babel/parser",
+ "@netlify/binary-info",
+ "@netlify/serverless-functions-api@1.7.3",
+ "@vercel/nft@0.23.1",
+ "archiver@5.3.2",
+ "common-path-prefix",
+ "cp-file@10.0.0",
+ "es-module-lexer",
+ "esbuild@0.19.2",
+ "execa@6.1.0",
+ "filter-obj@5.1.0",
+ "find-up@6.3.0",
+ "get-tsconfig",
+ "glob@8.1.0",
+ "is-builtin-module",
+ "is-path-inside@4.0.0",
+ "junk",
+ "locate-path@7.2.0",
+ "merge-options",
+ "minimatch@9.0.5",
+ "normalize-path@3.0.0",
+ "p-map@5.5.0",
+ "path-exists@5.0.0",
+ "precinct",
+ "require-package-name",
+ "resolve@2.0.0-next.5",
+ "semver@7.6.3",
+ "tmp-promise",
+ "toml",
+ "unixify",
+ "urlpattern-polyfill",
+ "yargs"
+ ]
+ },
+ "@netlify/zip-it-and-ship-it@9.39.5": {
+ "integrity": "sha512-Kj9rTshNo20YCuW/5IUZfmo/oCfzKUSk6nqBw7Lh0FLoKNVXe+6+YPRY2U9/VGuNqG/pTWPKnNYOzFyAI9xPIQ==",
+ "dependencies": [
+ "@babel/parser",
+ "@babel/types@7.25.6",
+ "@netlify/binary-info",
+ "@netlify/serverless-functions-api@1.26.1",
+ "@vercel/nft@0.27.4_acorn@8.12.1",
+ "archiver@7.0.1",
+ "common-path-prefix",
+ "cp-file@10.0.0",
+ "es-module-lexer",
+ "esbuild@0.19.11",
+ "execa@6.1.0",
+ "fast-glob",
+ "filter-obj@5.1.0",
+ "find-up@6.3.0",
+ "glob@8.1.0",
+ "is-builtin-module",
+ "is-path-inside@4.0.0",
+ "junk",
+ "locate-path@7.2.0",
+ "merge-options",
+ "minimatch@9.0.5",
+ "normalize-path@3.0.0",
+ "p-map@5.5.0",
+ "path-exists@5.0.0",
+ "precinct",
+ "require-package-name",
+ "resolve@2.0.0-next.5",
+ "semver@7.6.3",
+ "tmp-promise",
+ "toml",
+ "unixify",
+ "urlpattern-polyfill",
+ "yargs",
+ "zod"
+ ]
+ },
+ "@nodelib/fs.scandir@2.1.5": {
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dependencies": [
+ "@nodelib/fs.stat",
+ "run-parallel"
+ ]
+ },
+ "@nodelib/fs.stat@2.0.5": {
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="
+ },
+ "@nodelib/fs.walk@1.2.8": {
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dependencies": [
+ "@nodelib/fs.scandir",
+ "fastq"
+ ]
+ },
+ "@octokit/auth-token@3.0.4": {
+ "integrity": "sha512-TWFX7cZF2LXoCvdmJWY7XVPi74aSY0+FfBZNSXEXFkMpjcqsQwDSYVv5FhRFaI0V1ECnwbz4j59T/G+rXNWaIQ=="
+ },
+ "@octokit/core@4.2.4": {
+ "integrity": "sha512-rYKilwgzQ7/imScn3M9/pFfUf4I1AZEH3KhyJmtPdE2zfaXAn2mFfUy4FbKewzc2We5y/LlKLj36fWJLKC2SIQ==",
+ "dependencies": [
+ "@octokit/auth-token",
+ "@octokit/graphql",
+ "@octokit/request",
+ "@octokit/request-error",
+ "@octokit/types@9.3.2",
+ "before-after-hook",
+ "universal-user-agent"
+ ]
+ },
+ "@octokit/endpoint@7.0.6": {
+ "integrity": "sha512-5L4fseVRUsDFGR00tMWD/Trdeeihn999rTMGRMC1G/Ldi1uWlWJzI98H4Iak5DB/RVvQuyMYKqSK/R6mbSOQyg==",
+ "dependencies": [
+ "@octokit/types@9.3.2",
+ "is-plain-object@5.0.0",
+ "universal-user-agent"
+ ]
+ },
+ "@octokit/graphql@5.0.6": {
+ "integrity": "sha512-Fxyxdy/JH0MnIB5h+UQ3yCoh1FG4kWXfFKkpWqjZHw/p+Kc8Y44Hu/kCgNBT6nU1shNumEchmW/sUO1JuQnPcw==",
+ "dependencies": [
+ "@octokit/request",
+ "@octokit/types@9.3.2",
+ "universal-user-agent"
+ ]
+ },
+ "@octokit/openapi-types@18.1.1": {
+ "integrity": "sha512-VRaeH8nCDtF5aXWnjPuEMIYf1itK/s3JYyJcWFJT8X9pSNnBtriDf7wlEWsGuhPLl4QIH4xM8fqTXDwJ3Mu6sw=="
+ },
+ "@octokit/plugin-paginate-rest@6.1.2_@octokit+core@4.2.4": {
+ "integrity": "sha512-qhrmtQeHU/IivxucOV1bbI/xZyC/iOBhclokv7Sut5vnejAIAEXVcGQeRpQlU39E0WwK9lNvJHphHri/DB6lbQ==",
+ "dependencies": [
+ "@octokit/core",
+ "@octokit/tsconfig",
+ "@octokit/types@9.3.2"
+ ]
+ },
+ "@octokit/plugin-request-log@1.0.4_@octokit+core@4.2.4": {
+ "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==",
+ "dependencies": [
+ "@octokit/core"
+ ]
+ },
+ "@octokit/plugin-rest-endpoint-methods@7.2.3_@octokit+core@4.2.4": {
+ "integrity": "sha512-I5Gml6kTAkzVlN7KCtjOM+Ruwe/rQppp0QU372K1GP7kNOYEKe8Xn5BW4sE62JAHdwpq95OQK/qGNyKQMUzVgA==",
+ "dependencies": [
+ "@octokit/core",
+ "@octokit/types@10.0.0"
+ ]
+ },
+ "@octokit/request-error@3.0.3": {
+ "integrity": "sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==",
+ "dependencies": [
+ "@octokit/types@9.3.2",
+ "deprecation",
+ "once"
+ ]
+ },
+ "@octokit/request@6.2.8": {
+ "integrity": "sha512-ow4+pkVQ+6XVVsekSYBzJC0VTVvh/FCTUUgTsboGq+DTeWdyIFV8WSCdo0RIxk6wSkBTHqIK1mYuY7nOBXOchw==",
+ "dependencies": [
+ "@octokit/endpoint",
+ "@octokit/request-error",
+ "@octokit/types@9.3.2",
+ "is-plain-object@5.0.0",
+ "node-fetch@2.6.12",
+ "universal-user-agent"
+ ]
+ },
+ "@octokit/rest@19.0.13_@octokit+core@4.2.4": {
+ "integrity": "sha512-/EzVox5V9gYGdbAI+ovYj3nXQT1TtTHRT+0eZPcuC05UFSWO3mdO9UY1C0i2eLF9Un1ONJkAk+IEtYGAC+TahA==",
+ "dependencies": [
+ "@octokit/core",
+ "@octokit/plugin-paginate-rest",
+ "@octokit/plugin-request-log",
+ "@octokit/plugin-rest-endpoint-methods"
+ ]
+ },
+ "@octokit/tsconfig@1.0.2": {
+ "integrity": "sha512-I0vDR0rdtP8p2lGMzvsJzbhdOWy405HcGovrspJ8RRibHnyRgggUSNO5AIox5LmqiwmatHKYsvj6VGFHkqS7lA=="
+ },
+ "@octokit/types@10.0.0": {
+ "integrity": "sha512-Vm8IddVmhCgU1fxC1eyinpwqzXPEYu0NrYzD3YZjlGjyftdLBTeqNblRC0jmJmgxbJIsQlyogVeGnrNaaMVzIg==",
+ "dependencies": [
+ "@octokit/openapi-types"
+ ]
+ },
+ "@octokit/types@9.3.2": {
+ "integrity": "sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA==",
+ "dependencies": [
+ "@octokit/openapi-types"
+ ]
+ },
+ "@opentelemetry/api@1.4.1": {
+ "integrity": "sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA=="
+ },
+ "@opentelemetry/api@1.9.0": {
+ "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="
+ },
+ "@opentelemetry/context-async-hooks@1.10.1_@opentelemetry+api@1.4.1": {
+ "integrity": "sha512-6CC9sWOZDkUkKrAR957fmxXXlaK3uiBu5xVnuNEQ7hI7VqkUC/r0mNYIql0ouRInLz5o0HwmDuga1eXgQU7KNQ==",
+ "dependencies": [
+ "@opentelemetry/api@1.4.1"
+ ]
+ },
+ "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1": {
+ "integrity": "sha512-uBZs9poKMWX7WWHsRfaGHqvrn77D9EU5LwU8Ge3YKD/Su5Gy+T1v476l49nl1UOzEMNo4cISao3nIqQVsABB8g==",
+ "dependencies": [
+ "@opentelemetry/api@1.4.1",
+ "@opentelemetry/semantic-conventions@1.10.1"
+ ]
+ },
+ "@opentelemetry/core@1.26.0_@opentelemetry+api@1.9.0": {
+ "integrity": "sha512-1iKxXXE8415Cdv0yjG3G6hQnB5eVEsJce3QaawX8SjDn0mAS0ZM8fAbZZJD4ajvhC15cePvosSCut404KrIIvQ==",
+ "dependencies": [
+ "@opentelemetry/api@1.9.0",
+ "@opentelemetry/semantic-conventions@1.27.0"
+ ]
+ },
+ "@opentelemetry/exporter-jaeger@1.10.1_@opentelemetry+api@1.4.1": {
+ "integrity": "sha512-bZIoSD6M7uxO19HtRJCAceAahX56LUmj5N/XQFHmoi3iFqA2JfR7bqsyHQCYbgINdiee155UejaqkNpgvjV7fw==",
+ "dependencies": [
+ "@opentelemetry/api@1.4.1",
+ "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/sdk-trace-base@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/semantic-conventions@1.10.1",
+ "jaeger-client"
+ ]
+ },
+ "@opentelemetry/exporter-metrics-otlp-grpc@0.36.1_@opentelemetry+api@1.9.0": {
+ "integrity": "sha512-yQPHny0Y3HIE1BSqbN82MoqqbbJeLINjL7Qf3kJwv1zt5YLUhYbn3FkqHQWS0YWpAvdjK0/OcN40SjEbVz2HRA==",
+ "dependencies": [
+ "@grpc/grpc-js",
+ "@opentelemetry/api@1.9.0",
+ "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/exporter-metrics-otlp-http",
+ "@opentelemetry/otlp-grpc-exporter-base@0.36.1_@opentelemetry+api@1.9.0",
+ "@opentelemetry/otlp-transformer",
+ "@opentelemetry/resources@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/sdk-metrics@1.10.1_@opentelemetry+api@1.4.1"
+ ]
+ },
+ "@opentelemetry/exporter-metrics-otlp-http@0.36.1_@opentelemetry+api@1.9.0": {
+ "integrity": "sha512-JcpEBwtBpNhVvmCLH3zjTPDcOld2AeI5rNglv2JrB16QCxQ5pwsOgzw7mPe/UR4u/53Ij7LIjFTOCeyVto/6aA==",
+ "dependencies": [
+ "@opentelemetry/api@1.9.0",
+ "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/otlp-exporter-base@0.36.1_@opentelemetry+api@1.9.0",
+ "@opentelemetry/otlp-transformer",
+ "@opentelemetry/resources@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/sdk-metrics@1.10.1_@opentelemetry+api@1.4.1"
+ ]
+ },
+ "@opentelemetry/exporter-metrics-otlp-proto@0.36.1_@opentelemetry+api@1.9.0": {
+ "integrity": "sha512-dKJRKvIiyupuZJOVCzW9wNfsK6RxkELnzCSJHzFoIwhGRXSYpbWyYrfHj4ZJZWYZiQSJ7+I8BFUa4aSkBgnO0w==",
+ "dependencies": [
+ "@opentelemetry/api@1.9.0",
+ "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/exporter-metrics-otlp-http",
+ "@opentelemetry/otlp-exporter-base@0.36.1_@opentelemetry+api@1.9.0",
+ "@opentelemetry/otlp-proto-exporter-base@0.36.1_@opentelemetry+api@1.9.0",
+ "@opentelemetry/otlp-transformer",
+ "@opentelemetry/resources@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/sdk-metrics@1.10.1_@opentelemetry+api@1.4.1"
+ ]
+ },
+ "@opentelemetry/exporter-trace-otlp-grpc@0.36.1_@opentelemetry+api@1.4.1": {
+ "integrity": "sha512-U2HdWvQho2VkeSAcAhkZ2wjfUb/1SKQixo5x6LNBF17ES4QYuh5+BagYxfN5FP4dbLnjZpTtFk5lj+97lfNLEw==",
+ "dependencies": [
+ "@grpc/grpc-js",
+ "@opentelemetry/api@1.4.1",
+ "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/otlp-grpc-exporter-base@0.36.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/otlp-transformer",
+ "@opentelemetry/resources@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/sdk-trace-base@1.10.1_@opentelemetry+api@1.4.1"
+ ]
+ },
+ "@opentelemetry/exporter-trace-otlp-grpc@0.36.1_@opentelemetry+api@1.9.0": {
+ "integrity": "sha512-U2HdWvQho2VkeSAcAhkZ2wjfUb/1SKQixo5x6LNBF17ES4QYuh5+BagYxfN5FP4dbLnjZpTtFk5lj+97lfNLEw==",
+ "dependencies": [
+ "@grpc/grpc-js",
+ "@opentelemetry/api@1.9.0",
+ "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/otlp-grpc-exporter-base@0.36.1_@opentelemetry+api@1.9.0",
+ "@opentelemetry/otlp-transformer",
+ "@opentelemetry/resources@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/sdk-trace-base@1.10.1_@opentelemetry+api@1.4.1"
+ ]
+ },
+ "@opentelemetry/exporter-trace-otlp-http@0.36.1_@opentelemetry+api@1.4.1": {
+ "integrity": "sha512-q/jKlfuKiHqltDzgzgEvXkoEJ/EyVSIAZhfiaoyBeQ49UhHCPvNTH36/hSwbGSEhKeX98WxXZK4NB/S3sUs8ig==",
+ "dependencies": [
+ "@opentelemetry/api@1.4.1",
+ "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/otlp-exporter-base@0.36.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/otlp-transformer",
+ "@opentelemetry/resources@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/sdk-trace-base@1.10.1_@opentelemetry+api@1.4.1"
+ ]
+ },
+ "@opentelemetry/exporter-trace-otlp-proto@0.36.1_@opentelemetry+api@1.4.1": {
+ "integrity": "sha512-pNfrto7amygyyhmL4Kf96wuepROEecBYXSrtoXIVb1aUhUqjWLsA3/6DR3unB5EfSRA1Oq1Z9bqHfNuKqGfPNw==",
+ "dependencies": [
+ "@opentelemetry/api@1.4.1",
+ "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/otlp-exporter-base@0.36.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/otlp-proto-exporter-base@0.36.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/otlp-transformer",
+ "@opentelemetry/resources@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/sdk-trace-base@1.10.1_@opentelemetry+api@1.4.1"
+ ]
+ },
+ "@opentelemetry/exporter-trace-otlp-proto@0.36.1_@opentelemetry+api@1.9.0": {
+ "integrity": "sha512-pNfrto7amygyyhmL4Kf96wuepROEecBYXSrtoXIVb1aUhUqjWLsA3/6DR3unB5EfSRA1Oq1Z9bqHfNuKqGfPNw==",
+ "dependencies": [
+ "@opentelemetry/api@1.9.0",
+ "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/otlp-exporter-base@0.36.1_@opentelemetry+api@1.9.0",
+ "@opentelemetry/otlp-proto-exporter-base@0.36.1_@opentelemetry+api@1.9.0",
+ "@opentelemetry/otlp-transformer",
+ "@opentelemetry/resources@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/sdk-trace-base@1.10.1_@opentelemetry+api@1.4.1"
+ ]
+ },
+ "@opentelemetry/exporter-zipkin@1.10.1_@opentelemetry+api@1.4.1": {
+ "integrity": "sha512-8gF8MjcFf6IhQ7vm6W4tPYtu/vQswYVzpYvk3pUSaX9BMGrwgjeXg+LpuRtaxGoiGd08/g7JjZ4sWLUaELnzWw==",
+ "dependencies": [
+ "@opentelemetry/api@1.4.1",
+ "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/resources@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/sdk-trace-base@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/semantic-conventions@1.10.1"
+ ]
+ },
+ "@opentelemetry/instrumentation@0.36.1_@opentelemetry+api@1.4.1": {
+ "integrity": "sha512-gtYErugMEF5NXVacmuE+tHFBiyB82YIiO5l8iZX9/4R4TDV8uCWdrLW5QZMqgTzPhiyOG9AITFdqhwIZMw/5lA==",
+ "dependencies": [
+ "@opentelemetry/api@1.4.1",
+ "require-in-the-middle",
+ "semver@7.6.3",
+ "shimmer"
+ ]
+ },
+ "@opentelemetry/otlp-exporter-base@0.36.1_@opentelemetry+api@1.4.1": {
+ "integrity": "sha512-fpjPwLafJIjgxY5qx7Ly74AYmRCd9spC6/jCxvEgGheg1YT4+NkfVnrfllxLRgc9wQNhDj7Y0Knp8RcmXLLVfA==",
+ "dependencies": [
+ "@opentelemetry/api@1.4.1",
+ "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1"
+ ]
+ },
+ "@opentelemetry/otlp-exporter-base@0.36.1_@opentelemetry+api@1.9.0": {
+ "integrity": "sha512-fpjPwLafJIjgxY5qx7Ly74AYmRCd9spC6/jCxvEgGheg1YT4+NkfVnrfllxLRgc9wQNhDj7Y0Knp8RcmXLLVfA==",
+ "dependencies": [
+ "@opentelemetry/api@1.9.0",
+ "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1"
+ ]
+ },
+ "@opentelemetry/otlp-grpc-exporter-base@0.36.1_@opentelemetry+api@1.4.1": {
+ "integrity": "sha512-71TdQ3Z0D2Trq8rc2UMvky7tmIpg8kVPUhdYH3p0tNsTmbx6GDpEBOpjp2/zCFvQ0SZFVfHH2Oj2OZxZiz+FNQ==",
+ "dependencies": [
+ "@grpc/grpc-js",
+ "@grpc/proto-loader",
+ "@opentelemetry/api@1.4.1",
+ "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/otlp-exporter-base@0.36.1_@opentelemetry+api@1.4.1"
+ ]
+ },
+ "@opentelemetry/otlp-grpc-exporter-base@0.36.1_@opentelemetry+api@1.9.0": {
+ "integrity": "sha512-71TdQ3Z0D2Trq8rc2UMvky7tmIpg8kVPUhdYH3p0tNsTmbx6GDpEBOpjp2/zCFvQ0SZFVfHH2Oj2OZxZiz+FNQ==",
+ "dependencies": [
+ "@grpc/grpc-js",
+ "@grpc/proto-loader",
+ "@opentelemetry/api@1.9.0",
+ "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/otlp-exporter-base@0.36.1_@opentelemetry+api@1.9.0"
+ ]
+ },
+ "@opentelemetry/otlp-proto-exporter-base@0.36.1_@opentelemetry+api@1.4.1": {
+ "integrity": "sha512-9ErknJ5fS7r2NxEFeca93H+pGWnCjZCUWsz6Stcj5/z2rgsiZGHXLz3fQoUGQz+iXjiXKkks9wxTCRgWOW+Yiw==",
+ "dependencies": [
+ "@opentelemetry/api@1.4.1",
+ "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/otlp-exporter-base@0.36.1_@opentelemetry+api@1.4.1",
+ "protobufjs"
+ ]
+ },
+ "@opentelemetry/otlp-proto-exporter-base@0.36.1_@opentelemetry+api@1.9.0": {
+ "integrity": "sha512-9ErknJ5fS7r2NxEFeca93H+pGWnCjZCUWsz6Stcj5/z2rgsiZGHXLz3fQoUGQz+iXjiXKkks9wxTCRgWOW+Yiw==",
+ "dependencies": [
+ "@opentelemetry/api@1.9.0",
+ "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/otlp-exporter-base@0.36.1_@opentelemetry+api@1.9.0",
+ "protobufjs"
+ ]
+ },
+ "@opentelemetry/otlp-transformer@0.36.1_@opentelemetry+api@1.4.1": {
+ "integrity": "sha512-d2MomkVHBHwfsmNz6E60s/sm7gtpSjFwDzkFLm9brVq//VXzEhaEyfYSeTabdUs4BmrzhqTIogHWlcd6cOiL+w==",
+ "dependencies": [
+ "@opentelemetry/api@1.4.1",
+ "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/resources@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/sdk-metrics@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/sdk-trace-base@1.10.1_@opentelemetry+api@1.4.1"
+ ]
+ },
+ "@opentelemetry/propagator-b3@1.10.1_@opentelemetry+api@1.4.1": {
+ "integrity": "sha512-YrWqU93PH8RyCmqGhtDZgyk64D+cp8XIjQsLhEgOPcOsxvxSSGXnGt46rx9Z8+WdIbJgj13Q4nV/xuh36k+O+A==",
+ "dependencies": [
+ "@opentelemetry/api@1.4.1",
+ "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1"
+ ]
+ },
+ "@opentelemetry/propagator-jaeger@1.10.1_@opentelemetry+api@1.4.1": {
+ "integrity": "sha512-qvwFfDPoBw2YQW/OsGHdLdD/rqNRGBRLz5UZR/akO21C4qwIK+lQcXbSi5ve0p2eLHnFshhNFqDmgQclOYBcmg==",
+ "dependencies": [
+ "@opentelemetry/api@1.4.1",
+ "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1"
+ ]
+ },
+ "@opentelemetry/resources@1.10.1_@opentelemetry+api@1.4.1": {
+ "integrity": "sha512-e+wwdyO44jZtsT1aqGiWMFOfN1XuP9Tv4+H0OYP3yQajBtGdsZjdSUn9UNjw46JsW0Mb+RaTxJwsb2uvfHar0g==",
+ "dependencies": [
+ "@opentelemetry/api@1.4.1",
+ "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/semantic-conventions@1.10.1"
+ ]
+ },
+ "@opentelemetry/resources@1.26.0_@opentelemetry+api@1.9.0": {
+ "integrity": "sha512-CPNYchBE7MBecCSVy0HKpUISEeJOniWqcHaAHpmasZ3j9o6V3AyBzhRc90jdmemq0HOxDr6ylhUbDhBqqPpeNw==",
+ "dependencies": [
+ "@opentelemetry/api@1.9.0",
+ "@opentelemetry/core@1.26.0_@opentelemetry+api@1.9.0",
+ "@opentelemetry/semantic-conventions@1.27.0"
+ ]
+ },
+ "@opentelemetry/sdk-metrics@1.10.1_@opentelemetry+api@1.4.1": {
+ "integrity": "sha512-ARAD4e6lZhLwstwW+1HG2Q3XuYFA/t8vn10KK/mA4em1pZYKFn64c45RJZJcntxWp4wOZRbp9iL1RXsg7zIjow==",
+ "dependencies": [
+ "@opentelemetry/api@1.4.1",
+ "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/resources@1.10.1_@opentelemetry+api@1.4.1",
+ "lodash.merge"
+ ]
+ },
+ "@opentelemetry/sdk-metrics@1.26.0_@opentelemetry+api@1.9.0": {
+ "integrity": "sha512-0SvDXmou/JjzSDOjUmetAAvcKQW6ZrvosU0rkbDGpXvvZN+pQF6JbK/Kd4hNdK4q/22yeruqvukXEJyySTzyTQ==",
+ "dependencies": [
+ "@opentelemetry/api@1.9.0",
+ "@opentelemetry/core@1.26.0_@opentelemetry+api@1.9.0",
+ "@opentelemetry/resources@1.26.0_@opentelemetry+api@1.9.0"
+ ]
+ },
+ "@opentelemetry/sdk-node@0.36.1_@opentelemetry+api@1.4.1": {
+ "integrity": "sha512-7cRIxls3Ccg6HmzSu30R5upi0yHEizab2rm2rATrAyFV3JJ/ISA7cojmwKwYG8p4rkPNNPLOwCxI3vlLJrBnKA==",
+ "dependencies": [
+ "@opentelemetry/api@1.4.1",
+ "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/exporter-jaeger",
+ "@opentelemetry/exporter-trace-otlp-grpc@0.36.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/exporter-trace-otlp-http",
+ "@opentelemetry/exporter-trace-otlp-proto@0.36.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/exporter-zipkin",
+ "@opentelemetry/instrumentation",
+ "@opentelemetry/resources@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/sdk-metrics@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/sdk-trace-base@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/sdk-trace-node",
+ "@opentelemetry/semantic-conventions@1.10.1"
+ ]
+ },
+ "@opentelemetry/sdk-trace-base@1.10.1_@opentelemetry+api@1.4.1": {
+ "integrity": "sha512-jutSP5t22wrPKReJKzI5uKht4mJ4cQdF/mGFJkN+emFFsDXru9CuFv/NfUrD0jEqoaaiqjcZtPSyTzMgu9LXvw==",
+ "dependencies": [
+ "@opentelemetry/api@1.4.1",
+ "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/resources@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/semantic-conventions@1.10.1"
+ ]
+ },
+ "@opentelemetry/sdk-trace-base@1.26.0_@opentelemetry+api@1.9.0": {
+ "integrity": "sha512-olWQldtvbK4v22ymrKLbIcBi9L2SpMO84sCPY54IVsJhP9fRsxJT194C/AVaAuJzLE30EdhhM1VmvVYR7az+cw==",
+ "dependencies": [
+ "@opentelemetry/api@1.9.0",
+ "@opentelemetry/core@1.26.0_@opentelemetry+api@1.9.0",
+ "@opentelemetry/resources@1.26.0_@opentelemetry+api@1.9.0",
+ "@opentelemetry/semantic-conventions@1.27.0"
+ ]
+ },
+ "@opentelemetry/sdk-trace-node@1.10.1_@opentelemetry+api@1.4.1": {
+ "integrity": "sha512-/y+s1j8rPTaKnPnbrsbYv3ygTb4hjx/1H32zqobFr85cvWX+Tt1RWmcZ51TaPAfq5uJobGFhhLh6ADI2RDvk5Q==",
+ "dependencies": [
+ "@opentelemetry/api@1.4.1",
+ "@opentelemetry/context-async-hooks",
+ "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1",
+ "@opentelemetry/propagator-b3",
+ "@opentelemetry/propagator-jaeger",
+ "@opentelemetry/sdk-trace-base@1.10.1_@opentelemetry+api@1.4.1",
+ "semver@7.6.3"
+ ]
+ },
+ "@opentelemetry/semantic-conventions@1.10.1": {
+ "integrity": "sha512-qiAueuCoN+1YEuHNXnsct9bkbroZBPd7QwQgd56YURG0LBRVHwE/lF6FOprfUvp1n1tu0O6+E3s6x+dmUndXFQ=="
+ },
+ "@opentelemetry/semantic-conventions@1.27.0": {
+ "integrity": "sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg=="
+ },
+ "@pkgjs/parseargs@0.11.0": {
+ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="
+ },
+ "@pkgr/core@0.1.1": {
+ "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA=="
+ },
+ "@pnpm/config.env-replace@1.1.0": {
+ "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w=="
+ },
+ "@pnpm/network.ca-file@1.0.2": {
+ "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==",
+ "dependencies": [
+ "graceful-fs@4.2.10"
+ ]
+ },
+ "@pnpm/npm-conf@2.3.1": {
+ "integrity": "sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==",
+ "dependencies": [
+ "@pnpm/config.env-replace",
+ "@pnpm/network.ca-file",
+ "config-chain"
+ ]
+ },
+ "@protobufjs/aspromise@1.1.2": {
+ "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="
+ },
+ "@protobufjs/base64@1.1.2": {
+ "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="
+ },
+ "@protobufjs/codegen@2.0.4": {
+ "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="
+ },
+ "@protobufjs/eventemitter@1.1.0": {
+ "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="
+ },
+ "@protobufjs/fetch@1.1.0": {
+ "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
+ "dependencies": [
+ "@protobufjs/aspromise",
+ "@protobufjs/inquire"
+ ]
+ },
+ "@protobufjs/float@1.0.2": {
+ "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="
+ },
+ "@protobufjs/inquire@1.1.0": {
+ "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="
+ },
+ "@protobufjs/path@1.1.2": {
+ "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="
+ },
+ "@protobufjs/pool@1.1.0": {
+ "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="
+ },
+ "@protobufjs/utf8@1.1.0": {
+ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
+ },
+ "@rollup/pluginutils@4.2.1": {
+ "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==",
+ "dependencies": [
+ "estree-walker@2.0.2",
+ "picomatch"
+ ]
+ },
+ "@rollup/rollup-android-arm-eabi@4.24.0": {
+ "integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA=="
+ },
+ "@rollup/rollup-android-arm64@4.24.0": {
+ "integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA=="
+ },
+ "@rollup/rollup-darwin-arm64@4.24.0": {
+ "integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA=="
+ },
+ "@rollup/rollup-darwin-x64@4.24.0": {
+ "integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ=="
+ },
+ "@rollup/rollup-linux-arm-gnueabihf@4.24.0": {
+ "integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA=="
+ },
+ "@rollup/rollup-linux-arm-musleabihf@4.24.0": {
+ "integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw=="
+ },
+ "@rollup/rollup-linux-arm64-gnu@4.24.0": {
+ "integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA=="
+ },
+ "@rollup/rollup-linux-arm64-musl@4.24.0": {
+ "integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw=="
+ },
+ "@rollup/rollup-linux-powerpc64le-gnu@4.24.0": {
+ "integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw=="
+ },
+ "@rollup/rollup-linux-riscv64-gnu@4.24.0": {
+ "integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg=="
+ },
+ "@rollup/rollup-linux-s390x-gnu@4.24.0": {
+ "integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g=="
+ },
+ "@rollup/rollup-linux-x64-gnu@4.24.0": {
+ "integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A=="
+ },
+ "@rollup/rollup-linux-x64-musl@4.24.0": {
+ "integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ=="
+ },
+ "@rollup/rollup-win32-arm64-msvc@4.24.0": {
+ "integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ=="
+ },
+ "@rollup/rollup-win32-ia32-msvc@4.24.0": {
+ "integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ=="
+ },
+ "@rollup/rollup-win32-x64-msvc@4.24.0": {
+ "integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw=="
+ },
+ "@rtsao/scc@1.1.0": {
+ "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="
+ },
+ "@samverschueren/stream-to-observable@0.3.1": {
+ "integrity": "sha512-c/qwwcHyafOQuVQJj0IlBjf5yYgBI7YPJ77k4fOJYesb41jio65eaJODRUmfYKhTOFBrIZ66kgvGPlNbjuoRdQ==",
+ "dependencies": [
+ "any-observable"
+ ]
+ },
+ "@sindresorhus/is@5.6.0": {
+ "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g=="
+ },
+ "@sindresorhus/slugify@2.2.1": {
+ "integrity": "sha512-MkngSCRZ8JdSOCHRaYd+D01XhvU3Hjy6MGl06zhOk614hp9EOAp5gIkBeQg7wtmxpitU6eAL4kdiRMcJa2dlrw==",
+ "dependencies": [
+ "@sindresorhus/transliterate",
+ "escape-string-regexp@5.0.0"
+ ]
+ },
+ "@sindresorhus/transliterate@1.6.0": {
+ "integrity": "sha512-doH1gimEu3A46VX6aVxpHTeHrytJAG6HgdxntYnCFiIFHEM/ZGpG8KiZGBChchjQmG0XFIBL552kBTjVcMZXwQ==",
+ "dependencies": [
+ "escape-string-regexp@5.0.0"
+ ]
+ },
+ "@szmarczak/http-timer@5.0.1": {
+ "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==",
+ "dependencies": [
+ "defer-to-connect"
+ ]
+ },
+ "@tokenizer/token@0.3.0": {
+ "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="
+ },
+ "@tsconfig/node10@1.0.11": {
+ "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw=="
+ },
+ "@tsconfig/node12@1.0.11": {
+ "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag=="
+ },
+ "@tsconfig/node14@1.0.3": {
+ "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow=="
+ },
+ "@tsconfig/node16@1.0.4": {
+ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA=="
+ },
+ "@types/debug@4.1.12": {
+ "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
+ "dependencies": [
+ "@types/ms"
+ ]
+ },
+ "@types/estree@1.0.6": {
+ "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="
+ },
+ "@types/http-cache-semantics@4.0.4": {
+ "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA=="
+ },
+ "@types/http-proxy@1.17.15": {
+ "integrity": "sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ==",
+ "dependencies": [
+ "@types/node@22.5.4"
+ ]
+ },
+ "@types/istanbul-lib-coverage@2.0.6": {
+ "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w=="
+ },
+ "@types/istanbul-lib-report@3.0.3": {
+ "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==",
+ "dependencies": [
+ "@types/istanbul-lib-coverage"
+ ]
+ },
+ "@types/istanbul-reports@3.0.4": {
+ "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==",
+ "dependencies": [
+ "@types/istanbul-lib-report"
+ ]
+ },
+ "@types/json5@0.0.29": {
+ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="
+ },
+ "@types/mdast@4.0.4": {
+ "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==",
+ "dependencies": [
+ "@types/unist"
+ ]
+ },
+ "@types/ms@0.7.34": {
+ "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g=="
+ },
+ "@types/node@20.16.10": {
+ "integrity": "sha512-vQUKgWTjEIRFCvK6CyriPH3MZYiYlNy0fKiEYHWbcoWLEgs4opurGGKlebrTLqdSMIbXImH6XExNiIyNUv3WpA==",
+ "dependencies": [
+ "undici-types"
+ ]
+ },
+ "@types/node@22.5.4": {
+ "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==",
+ "dependencies": [
+ "undici-types"
+ ]
+ },
+ "@types/normalize-package-data@2.4.4": {
+ "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA=="
+ },
+ "@types/retry@0.12.1": {
+ "integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g=="
+ },
+ "@types/triple-beam@1.3.5": {
+ "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw=="
+ },
+ "@types/unist@3.0.3": {
+ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="
+ },
+ "@types/yargs-parser@21.0.3": {
+ "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ=="
+ },
+ "@types/yargs@16.0.9": {
+ "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==",
+ "dependencies": [
+ "@types/yargs-parser"
+ ]
+ },
+ "@types/yauzl@2.10.3": {
+ "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==",
+ "dependencies": [
+ "@types/node@22.5.4"
+ ]
+ },
+ "@typescript-eslint/eslint-plugin@7.18.0_@typescript-eslint+parser@7.18.0__eslint@8.57.1__typescript@5.6.2_eslint@8.57.1_typescript@5.6.2": {
+ "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==",
+ "dependencies": [
+ "@eslint-community/regexpp",
+ "@typescript-eslint/parser",
+ "@typescript-eslint/scope-manager",
+ "@typescript-eslint/type-utils",
+ "@typescript-eslint/utils",
+ "@typescript-eslint/visitor-keys@7.18.0",
+ "eslint",
+ "graphemer",
+ "ignore",
+ "natural-compare",
+ "ts-api-utils"
+ ]
+ },
+ "@typescript-eslint/parser@7.18.0_eslint@8.57.1_typescript@5.6.2": {
+ "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==",
+ "dependencies": [
+ "@typescript-eslint/scope-manager",
+ "@typescript-eslint/types@7.18.0",
+ "@typescript-eslint/typescript-estree@7.18.0_typescript@5.6.2",
+ "@typescript-eslint/visitor-keys@7.18.0",
+ "debug@4.3.7",
+ "eslint"
+ ]
+ },
+ "@typescript-eslint/scope-manager@7.18.0": {
+ "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==",
+ "dependencies": [
+ "@typescript-eslint/types@7.18.0",
+ "@typescript-eslint/visitor-keys@7.18.0"
+ ]
+ },
+ "@typescript-eslint/type-utils@7.18.0_eslint@8.57.1_typescript@5.6.2": {
+ "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==",
+ "dependencies": [
+ "@typescript-eslint/typescript-estree@7.18.0_typescript@5.6.2",
+ "@typescript-eslint/utils",
+ "debug@4.3.7",
+ "eslint",
+ "ts-api-utils"
+ ]
+ },
+ "@typescript-eslint/types@5.62.0": {
+ "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ=="
+ },
+ "@typescript-eslint/types@7.18.0": {
+ "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ=="
+ },
+ "@typescript-eslint/typescript-estree@5.62.0_typescript@5.6.2": {
+ "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==",
+ "dependencies": [
+ "@typescript-eslint/types@5.62.0",
+ "@typescript-eslint/visitor-keys@5.62.0",
+ "debug@4.3.7",
+ "globby@11.1.0",
+ "is-glob",
+ "semver@7.6.3",
+ "tsutils"
+ ]
+ },
+ "@typescript-eslint/typescript-estree@7.18.0_typescript@5.6.2": {
+ "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==",
+ "dependencies": [
+ "@typescript-eslint/types@7.18.0",
+ "@typescript-eslint/visitor-keys@7.18.0",
+ "debug@4.3.7",
+ "globby@11.1.0",
+ "is-glob",
+ "minimatch@9.0.5",
+ "semver@7.6.3",
+ "ts-api-utils"
+ ]
+ },
+ "@typescript-eslint/utils@7.18.0_eslint@8.57.1_typescript@5.6.2": {
+ "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==",
+ "dependencies": [
+ "@eslint-community/eslint-utils",
+ "@typescript-eslint/scope-manager",
+ "@typescript-eslint/types@7.18.0",
+ "@typescript-eslint/typescript-estree@7.18.0_typescript@5.6.2",
+ "eslint"
+ ]
+ },
+ "@typescript-eslint/visitor-keys@5.62.0": {
+ "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==",
+ "dependencies": [
+ "@typescript-eslint/types@5.62.0",
+ "eslint-visitor-keys"
+ ]
+ },
+ "@typescript-eslint/visitor-keys@7.18.0": {
+ "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==",
+ "dependencies": [
+ "@typescript-eslint/types@7.18.0",
+ "eslint-visitor-keys"
+ ]
+ },
+ "@ungap/structured-clone@1.2.0": {
+ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ=="
+ },
+ "@vercel/nft@0.23.1": {
+ "integrity": "sha512-NE0xSmGWVhgHF1OIoir71XAd0W0C1UE3nzFyhpFiMr3rVhetww7NvM1kc41trBsPG37Bh+dE5FYCTMzM/gBu0w==",
+ "dependencies": [
+ "@mapbox/node-pre-gyp",
+ "@rollup/pluginutils",
+ "acorn",
+ "async-sema",
+ "bindings",
+ "estree-walker@2.0.2",
+ "glob@7.2.3",
+ "graceful-fs@4.2.11",
+ "micromatch@4.0.8",
+ "node-gyp-build",
+ "resolve-from@5.0.0"
+ ]
+ },
+ "@vercel/nft@0.27.4_acorn@8.12.1": {
+ "integrity": "sha512-Rioz3LJkEKicKCi9BSyc1RXZ5R6GmXosFMeBSThh6msWSOiArKhb7c75MiWwZEgPL7x0/l3TAfH/l0cxKNuUFA==",
+ "dependencies": [
+ "@mapbox/node-pre-gyp",
+ "@rollup/pluginutils",
+ "acorn",
+ "acorn-import-attributes",
+ "async-sema",
+ "bindings",
+ "estree-walker@2.0.2",
+ "glob@7.2.3",
+ "graceful-fs@4.2.11",
+ "micromatch@4.0.8",
+ "node-gyp-build",
+ "resolve-from@5.0.0"
+ ]
+ },
+ "@vitest/expect@2.1.1": {
+ "integrity": "sha512-YeueunS0HiHiQxk+KEOnq/QMzlUuOzbU1Go+PgAsHvvv3tUkJPm9xWt+6ITNTlzsMXUjmgm5T+U7KBPK2qQV6w==",
+ "dependencies": [
+ "@vitest/spy",
+ "@vitest/utils",
+ "chai",
+ "tinyrainbow"
+ ]
+ },
+ "@vitest/mocker@2.1.1_@vitest+spy@2.1.1_vite@5.4.8__@types+node@20.16.10_@types+node@20.16.10": {
+ "integrity": "sha512-LNN5VwOEdJqCmJ/2XJBywB11DLlkbY0ooDJW3uRX5cZyYCrc4PI/ePX0iQhE3BiEGiQmK4GE7Q/PqCkkaiPnrA==",
+ "dependencies": [
+ "@vitest/spy",
+ "estree-walker@3.0.3",
+ "magic-string",
+ "vite"
+ ]
+ },
+ "@vitest/pretty-format@2.1.1": {
+ "integrity": "sha512-SjxPFOtuINDUW8/UkElJYQSFtnWX7tMksSGW0vfjxMneFqxVr8YJ979QpMbDW7g+BIiq88RAGDjf7en6rvLPPQ==",
+ "dependencies": [
+ "tinyrainbow"
+ ]
+ },
+ "@vitest/pretty-format@2.1.2": {
+ "integrity": "sha512-FIoglbHrSUlOJPDGIrh2bjX1sNars5HbxlcsFKCtKzu4+5lpsRhOCVcuzp0fEhAGHkPZRIXVNzPcpSlkoZ3LuA==",
+ "dependencies": [
+ "tinyrainbow"
+ ]
+ },
+ "@vitest/runner@2.1.1": {
+ "integrity": "sha512-uTPuY6PWOYitIkLPidaY5L3t0JJITdGTSwBtwMjKzo5O6RCOEncz9PUN+0pDidX8kTHYjO0EwUIvhlGpnGpxmA==",
+ "dependencies": [
+ "@vitest/utils",
+ "pathe"
+ ]
+ },
+ "@vitest/snapshot@2.1.1": {
+ "integrity": "sha512-BnSku1WFy7r4mm96ha2FzN99AZJgpZOWrAhtQfoxjUU5YMRpq1zmHRq7a5K9/NjqonebO7iVDla+VvZS8BOWMw==",
+ "dependencies": [
+ "@vitest/pretty-format@2.1.1",
+ "magic-string",
+ "pathe"
+ ]
+ },
+ "@vitest/spy@2.1.1": {
+ "integrity": "sha512-ZM39BnZ9t/xZ/nF4UwRH5il0Sw93QnZXd9NAZGRpIgj0yvVwPpLd702s/Cx955rGaMlyBQkZJ2Ir7qyY48VZ+g==",
+ "dependencies": [
+ "tinyspy"
+ ]
+ },
+ "@vitest/utils@2.1.1": {
+ "integrity": "sha512-Y6Q9TsI+qJ2CC0ZKj6VBb+T8UPz593N113nnUykqwANqhgf3QkZeHFlusgKLTqrnVHbj/XDKZcDHol+dxVT+rQ==",
+ "dependencies": [
+ "@vitest/pretty-format@2.1.1",
+ "loupe",
+ "tinyrainbow"
+ ]
+ },
+ "@xhmikosr/archive-type@6.0.1": {
+ "integrity": "sha512-PB3NeJL8xARZt52yDBupK0dNPn8uIVQDe15qNehUpoeeLWCZyAOam4vGXnoZGz2N9D1VXtjievJuCsXam2TmbQ==",
+ "dependencies": [
+ "file-type"
+ ]
+ },
+ "@xhmikosr/decompress-tar@7.0.0": {
+ "integrity": "sha512-kyWf2hybtQVbWtB+FdRyOT+jyR5jxCNZPLqvQGB7djZj75lrpLUPEmRbyo86AtJ5OEtivpYaNWjCkqSJ8xtRWw==",
+ "dependencies": [
+ "file-type",
+ "is-stream@3.0.0",
+ "tar-stream@3.1.7"
+ ]
+ },
+ "@xhmikosr/decompress-tarbz2@7.0.0": {
+ "integrity": "sha512-3QnjipYkRgh3Dee1MWDgKmANWxOQBVN4e1IwiGNe2fHYfMYTeSkVvWREt87UIoSucKUh3E95v8uGFttgTknZcA==",
+ "dependencies": [
+ "@xhmikosr/decompress-tar",
+ "file-type",
+ "is-stream@3.0.0",
+ "seek-bzip",
+ "unbzip2-stream"
+ ]
+ },
+ "@xhmikosr/decompress-targz@7.0.0": {
+ "integrity": "sha512-7BNHJl92g9OLhw89zqcFS67V1LAtm4Ex02j6OiQzuE8P7Yy9lQcyBuEL3x6v436grLdL+BcFjgbmhWxnem4GHw==",
+ "dependencies": [
+ "@xhmikosr/decompress-tar",
+ "file-type",
+ "is-stream@3.0.0"
+ ]
+ },
+ "@xhmikosr/decompress-unzip@6.0.0": {
+ "integrity": "sha512-R1HAkjXLS7RAL74YFLxYY9zYflCcYGssld9KKFDu87PnJ4h4btdhzXfSC8J5i5A2njH3oYIoCzx03RIGTH07Sg==",
+ "dependencies": [
+ "file-type",
+ "get-stream@6.0.1",
+ "yauzl"
+ ]
+ },
+ "@xhmikosr/decompress@9.0.1": {
+ "integrity": "sha512-9Lvlt6Qdpo9SaRQyRIXCo3lgU++eMZ68lzgjcTwtuKDrlwT635+5zsHZ1yrSx/Blc5IDuVLlPkBPj5CZkx+2+Q==",
+ "dependencies": [
+ "@xhmikosr/decompress-tar",
+ "@xhmikosr/decompress-tarbz2",
+ "@xhmikosr/decompress-targz",
+ "@xhmikosr/decompress-unzip",
+ "graceful-fs@4.2.11",
+ "make-dir@4.0.0",
+ "strip-dirs"
+ ]
+ },
+ "@xhmikosr/downloader@13.0.1": {
+ "integrity": "sha512-mBvWew1kZJHfNQVVfVllMjUDwCGN9apPa0t4/z1zaUJ9MzpXjRL3w8fsfJKB8gHN/h4rik9HneKfDbh2fErN+w==",
+ "dependencies": [
+ "@xhmikosr/archive-type",
+ "@xhmikosr/decompress",
+ "content-disposition",
+ "ext-name",
+ "file-type",
+ "filenamify",
+ "get-stream@6.0.1",
+ "got",
+ "merge-options",
+ "p-event@5.0.1"
+ ]
+ },
+ "abbrev@1.1.1": {
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
+ },
+ "abort-controller@3.0.0": {
+ "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+ "dependencies": [
+ "event-target-shim"
+ ]
+ },
+ "abstract-logging@2.0.1": {
+ "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA=="
+ },
+ "accepts@1.3.8": {
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "dependencies": [
+ "mime-types",
+ "negotiator"
+ ]
+ },
+ "acorn-import-attributes@1.9.5_acorn@8.12.1": {
+ "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==",
+ "dependencies": [
+ "acorn"
+ ]
+ },
+ "acorn-jsx@5.3.2_acorn@8.12.1": {
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dependencies": [
+ "acorn"
+ ]
+ },
+ "acorn-walk@8.3.4": {
+ "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
+ "dependencies": [
+ "acorn"
+ ]
+ },
+ "acorn@8.12.1": {
+ "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg=="
+ },
+ "agent-base@6.0.2": {
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "dependencies": [
+ "debug@4.3.7"
+ ]
+ },
+ "aggregate-error@4.0.1": {
+ "integrity": "sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==",
+ "dependencies": [
+ "clean-stack",
+ "indent-string@5.0.0"
+ ]
+ },
+ "ajv-errors@3.0.0_ajv@8.17.1": {
+ "integrity": "sha512-V3wD15YHfHz6y0KdhYFjyy9vWtEVALT9UrxfN3zqlI6dMioHnJrqOYfyPKol3oqrnCM9uwkcdCwkJ0WUcbLMTQ==",
+ "dependencies": [
+ "ajv@8.17.1"
+ ]
+ },
+ "ajv-formats@2.1.1_ajv@8.17.1": {
+ "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
+ "dependencies": [
+ "ajv@8.17.1"
+ ]
+ },
+ "ajv-formats@3.0.1_ajv@8.17.1": {
+ "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==",
+ "dependencies": [
+ "ajv@8.17.1"
+ ]
+ },
+ "ajv@6.12.6": {
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dependencies": [
+ "fast-deep-equal",
+ "fast-json-stable-stringify",
+ "json-schema-traverse@0.4.1",
+ "uri-js"
+ ]
+ },
+ "ajv@8.17.1": {
+ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
+ "dependencies": [
+ "fast-deep-equal",
+ "fast-uri@3.0.2",
+ "json-schema-traverse@1.0.0",
+ "require-from-string"
+ ]
+ },
+ "all-node-versions@11.3.0": {
+ "integrity": "sha512-psMkc5s3qpr+QMfires9bC4azRYciPWql1wqZKMsYRh1731qefQDH2X4+O19xSBX6u0Ra/8Y5diG6y/fEmqKsw==",
+ "dependencies": [
+ "fetch-node-website",
+ "filter-obj@5.1.0",
+ "get-stream@6.0.1",
+ "global-cache-dir",
+ "is-plain-obj@4.1.0",
+ "path-exists@5.0.0",
+ "semver@7.6.3",
+ "write-file-atomic@4.0.2"
+ ]
+ },
+ "ansi-align@3.0.1": {
+ "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==",
+ "dependencies": [
+ "string-width@4.2.3"
+ ]
+ },
+ "ansi-color@0.2.1": {
+ "integrity": "sha512-bF6xLaZBLpOQzgYUtYEhJx090nPSZk1BQ/q2oyBK9aMMcJHzx9uXGCjI2Y+LebsN4Jwoykr0V9whbPiogdyHoQ=="
+ },
+ "ansi-escapes@3.2.0": {
+ "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ=="
+ },
+ "ansi-escapes@4.3.2": {
+ "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
+ "dependencies": [
+ "type-fest@0.21.3"
+ ]
+ },
+ "ansi-escapes@5.0.0": {
+ "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==",
+ "dependencies": [
+ "type-fest@1.4.0"
+ ]
+ },
+ "ansi-escapes@6.2.0": {
+ "integrity": "sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==",
+ "dependencies": [
+ "type-fest@3.13.1"
+ ]
+ },
+ "ansi-regex@2.1.1": {
+ "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA=="
+ },
+ "ansi-regex@3.0.1": {
+ "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw=="
+ },
+ "ansi-regex@4.1.1": {
+ "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="
+ },
+ "ansi-regex@5.0.1": {
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+ },
+ "ansi-regex@6.1.0": {
+ "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="
+ },
+ "ansi-sequence-parser@1.1.1": {
+ "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg=="
+ },
+ "ansi-styles@2.2.1": {
+ "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA=="
+ },
+ "ansi-styles@3.2.1": {
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dependencies": [
+ "color-convert@1.9.3"
+ ]
+ },
+ "ansi-styles@4.3.0": {
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dependencies": [
+ "color-convert@2.0.1"
+ ]
+ },
+ "ansi-styles@5.2.0": {
+ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="
+ },
+ "ansi-styles@6.2.1": {
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="
+ },
+ "ansi-to-html@0.7.2": {
+ "integrity": "sha512-v6MqmEpNlxF+POuyhKkidusCHWWkaLcGRURzivcU3I9tv7k4JVhFcnukrM5Rlk2rUywdZuzYAZ+kbZqWCnfN3g==",
+ "dependencies": [
+ "entities"
+ ]
+ },
+ "any-observable@0.3.0": {
+ "integrity": "sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog=="
+ },
+ "anymatch@3.1.3": {
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dependencies": [
+ "normalize-path@3.0.0",
+ "picomatch"
+ ]
+ },
+ "aproba@2.0.0": {
+ "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ=="
+ },
+ "archiver-utils@2.1.0": {
+ "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==",
+ "dependencies": [
+ "glob@7.2.3",
+ "graceful-fs@4.2.11",
+ "lazystream",
+ "lodash.defaults",
+ "lodash.difference",
+ "lodash.flatten",
+ "lodash.isplainobject",
+ "lodash.union",
+ "normalize-path@3.0.0",
+ "readable-stream@2.3.8"
+ ]
+ },
+ "archiver-utils@3.0.4": {
+ "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==",
+ "dependencies": [
+ "glob@7.2.3",
+ "graceful-fs@4.2.11",
+ "lazystream",
+ "lodash.defaults",
+ "lodash.difference",
+ "lodash.flatten",
+ "lodash.isplainobject",
+ "lodash.union",
+ "normalize-path@3.0.0",
+ "readable-stream@3.6.2"
+ ]
+ },
+ "archiver-utils@5.0.2": {
+ "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==",
+ "dependencies": [
+ "glob@10.4.5",
+ "graceful-fs@4.2.11",
+ "is-stream@2.0.1",
+ "lazystream",
+ "lodash",
+ "normalize-path@3.0.0",
+ "readable-stream@4.5.2"
+ ]
+ },
+ "archiver@5.3.2": {
+ "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==",
+ "dependencies": [
+ "archiver-utils@2.1.0",
+ "async@3.2.6",
+ "buffer-crc32@0.2.13",
+ "readable-stream@3.6.2",
+ "readdir-glob",
+ "tar-stream@2.2.0",
+ "zip-stream@4.1.1"
+ ]
+ },
+ "archiver@7.0.1": {
+ "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==",
+ "dependencies": [
+ "archiver-utils@5.0.2",
+ "async@3.2.6",
+ "buffer-crc32@1.0.0",
+ "readable-stream@4.5.2",
+ "readdir-glob",
+ "tar-stream@3.1.7",
+ "zip-stream@6.0.1"
+ ]
+ },
+ "are-we-there-yet@2.0.0": {
+ "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
+ "dependencies": [
+ "delegates",
+ "readable-stream@3.6.2"
+ ]
+ },
+ "arg@4.1.3": {
+ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA=="
+ },
+ "argparse@2.0.1": {
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
+ },
+ "aria-query@5.1.3": {
+ "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==",
+ "dependencies": [
+ "deep-equal"
+ ]
+ },
+ "arr-diff@4.0.0": {
+ "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA=="
+ },
+ "arr-flatten@1.1.0": {
+ "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg=="
+ },
+ "arr-union@3.1.0": {
+ "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q=="
+ },
+ "array-buffer-byte-length@1.0.1": {
+ "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==",
+ "dependencies": [
+ "call-bind",
+ "is-array-buffer"
+ ]
+ },
+ "array-flatten@1.1.1": {
+ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
+ },
+ "array-includes@3.1.8": {
+ "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==",
+ "dependencies": [
+ "call-bind",
+ "define-properties",
+ "es-abstract",
+ "es-object-atoms",
+ "get-intrinsic",
+ "is-string"
+ ]
+ },
+ "array-timsort@1.0.3": {
+ "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ=="
+ },
+ "array-union@2.1.0": {
+ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw=="
+ },
+ "array-unique@0.3.2": {
+ "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ=="
+ },
+ "array.prototype.findlast@1.2.5": {
+ "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==",
+ "dependencies": [
+ "call-bind",
+ "define-properties",
+ "es-abstract",
+ "es-errors",
+ "es-object-atoms",
+ "es-shim-unscopables"
+ ]
+ },
+ "array.prototype.findlastindex@1.2.5": {
+ "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==",
+ "dependencies": [
+ "call-bind",
+ "define-properties",
+ "es-abstract",
+ "es-errors",
+ "es-object-atoms",
+ "es-shim-unscopables"
+ ]
+ },
+ "array.prototype.flat@1.3.2": {
+ "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==",
+ "dependencies": [
+ "call-bind",
+ "define-properties",
+ "es-abstract",
+ "es-shim-unscopables"
+ ]
+ },
+ "array.prototype.flatmap@1.3.2": {
+ "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==",
+ "dependencies": [
+ "call-bind",
+ "define-properties",
+ "es-abstract",
+ "es-shim-unscopables"
+ ]
+ },
+ "array.prototype.tosorted@1.1.4": {
+ "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==",
+ "dependencies": [
+ "call-bind",
+ "define-properties",
+ "es-abstract",
+ "es-errors",
+ "es-shim-unscopables"
+ ]
+ },
+ "arraybuffer.prototype.slice@1.0.3": {
+ "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==",
+ "dependencies": [
+ "array-buffer-byte-length",
+ "call-bind",
+ "define-properties",
+ "es-abstract",
+ "es-errors",
+ "get-intrinsic",
+ "is-array-buffer",
+ "is-shared-array-buffer"
+ ]
+ },
+ "arrify@3.0.0": {
+ "integrity": "sha512-tLkvA81vQG/XqE2mjDkGQHoOINtMHtysSnemrmoGe6PydDPMRbVugqyk4A6V/WDWEfm3l+0d8anA9r8cv/5Jaw=="
+ },
+ "ascii-table@0.0.9": {
+ "integrity": "sha512-xpkr6sCDIYTPqzvjG8M3ncw1YOTaloWZOyrUmicoEifBEKzQzt+ooUpRpQ/AbOoJfO/p2ZKiyp79qHThzJDulQ=="
+ },
+ "assertion-error@2.0.1": {
+ "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="
+ },
+ "assign-symbols@1.0.0": {
+ "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw=="
+ },
+ "ast-module-types@5.0.0": {
+ "integrity": "sha512-JvqziE0Wc0rXQfma0HZC/aY7URXHFuZV84fJRtP8u+lhp0JYCNd5wJzVXP45t0PH0Mej3ynlzvdyITYIu0G4LQ=="
+ },
+ "ast-types-flow@0.0.8": {
+ "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ=="
+ },
+ "async-sema@3.1.1": {
+ "integrity": "sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg=="
+ },
+ "async@1.5.2": {
+ "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w=="
+ },
+ "async@3.2.6": {
+ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="
+ },
+ "asynckit@0.4.0": {
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+ },
+ "atob@2.1.2": {
+ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="
+ },
+ "atomic-sleep@1.0.0": {
+ "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ=="
+ },
+ "available-typed-arrays@1.0.7": {
+ "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+ "dependencies": [
+ "possible-typed-array-names"
+ ]
+ },
+ "avvio@8.4.0": {
+ "integrity": "sha512-CDSwaxINFy59iNwhYnkvALBwZiTydGkOecZyPkqBpABYR1KqGEsET0VOOYDwtleZSUIdeY36DC2bSZ24CO1igA==",
+ "dependencies": [
+ "@fastify/error",
+ "fastq"
+ ]
+ },
+ "axe-core@4.10.0": {
+ "integrity": "sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g=="
+ },
+ "axios@1.7.7": {
+ "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
+ "dependencies": [
+ "follow-redirects",
+ "form-data",
+ "proxy-from-env"
+ ]
+ },
+ "axobject-query@4.1.0": {
+ "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="
+ },
+ "b4a@1.6.7": {
+ "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg=="
+ },
+ "backoff@2.5.0": {
+ "integrity": "sha512-wC5ihrnUXmR2douXmXLCe5O3zg3GKIyvRi/hi58a/XyRxVI+3/yM0PYueQOZXPXQ9pxBislYkw+sF9b7C/RuMA==",
+ "dependencies": [
+ "precond"
+ ]
+ },
+ "balanced-match@1.0.2": {
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+ },
+ "bare-events@2.5.0": {
+ "integrity": "sha512-/E8dDe9dsbLyh2qrZ64PEPadOQ0F4gbl1sUJOrmph7xOiIxfY8vwab/4bFLh4Y88/Hk/ujKcrQKc+ps0mv873A=="
+ },
+ "base64-js@1.5.1": {
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
+ },
+ "base@0.11.2": {
+ "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
+ "dependencies": [
+ "cache-base",
+ "class-utils",
+ "component-emitter",
+ "define-property@1.0.0",
+ "isobject@3.0.1",
+ "mixin-deep",
+ "pascalcase"
+ ]
+ },
+ "before-after-hook@2.2.3": {
+ "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="
+ },
+ "better-ajv-errors@1.2.0_ajv@8.17.1": {
+ "integrity": "sha512-UW+IsFycygIo7bclP9h5ugkNH8EjCSgqyFB/yQ4Hqqa1OEYDtb0uFIkYE0b6+CjkgJYVM5UKI/pJPxjYe9EZlA==",
+ "dependencies": [
+ "@babel/code-frame",
+ "@humanwhocodes/momoa",
+ "ajv@8.17.1",
+ "chalk@4.1.2",
+ "jsonpointer",
+ "leven"
+ ]
+ },
+ "better-opn@3.0.2": {
+ "integrity": "sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==",
+ "dependencies": [
+ "open"
+ ]
+ },
+ "binary-extensions@2.3.0": {
+ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="
+ },
+ "binary-searching@2.0.5": {
+ "integrity": "sha512-v4N2l3RxL+m4zDxyxz3Ne2aTmiPn8ZUpKFpdPtO+ItW1NcTCXA7JeHG5GMBSvoKSkQZ9ycS+EouDVxYB9ufKWA=="
+ },
+ "bindings@1.5.0": {
+ "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+ "dependencies": [
+ "file-uri-to-path"
+ ]
+ },
+ "bl@4.1.0": {
+ "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+ "dependencies": [
+ "buffer@5.7.1",
+ "inherits",
+ "readable-stream@3.6.2"
+ ]
+ },
+ "bl@5.1.0": {
+ "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==",
+ "dependencies": [
+ "buffer@6.0.3",
+ "inherits",
+ "readable-stream@3.6.2"
+ ]
+ },
+ "blueimp-md5@2.19.0": {
+ "integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w=="
+ },
+ "body-parser@1.20.1": {
+ "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
+ "dependencies": [
+ "bytes",
+ "content-type",
+ "debug@2.6.9",
+ "depd@2.0.0",
+ "destroy",
+ "http-errors@2.0.0",
+ "iconv-lite",
+ "on-finished",
+ "qs",
+ "raw-body@2.5.1",
+ "type-is",
+ "unpipe"
+ ]
+ },
+ "boxen@7.1.1": {
+ "integrity": "sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==",
+ "dependencies": [
+ "ansi-align",
+ "camelcase@7.0.1",
+ "chalk@5.2.0",
+ "cli-boxes",
+ "string-width@5.1.2",
+ "type-fest@2.19.0",
+ "widest-line",
+ "wrap-ansi@8.1.0"
+ ]
+ },
+ "brace-expansion@1.1.11": {
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dependencies": [
+ "balanced-match",
+ "concat-map"
+ ]
+ },
+ "brace-expansion@2.0.1": {
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dependencies": [
+ "balanced-match"
+ ]
+ },
+ "braces@2.3.2": {
+ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+ "dependencies": [
+ "arr-flatten",
+ "array-unique",
+ "extend-shallow@2.0.1",
+ "fill-range@4.0.0",
+ "isobject@3.0.1",
+ "repeat-element",
+ "snapdragon",
+ "snapdragon-node",
+ "split-string",
+ "to-regex"
+ ]
+ },
+ "braces@3.0.3": {
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dependencies": [
+ "fill-range@7.1.1"
+ ]
+ },
+ "buffer-crc32@0.2.13": {
+ "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ=="
+ },
+ "buffer-crc32@1.0.0": {
+ "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w=="
+ },
+ "buffer-equal-constant-time@1.0.1": {
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
+ },
+ "buffer-from@1.1.2": {
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
+ },
+ "buffer@5.7.1": {
+ "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+ "dependencies": [
+ "base64-js",
+ "ieee754"
+ ]
+ },
+ "buffer@6.0.3": {
+ "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+ "dependencies": [
+ "base64-js",
+ "ieee754"
+ ]
+ },
+ "bufrw@1.4.0": {
+ "integrity": "sha512-sWm8iPbqvL9+5SiYxXH73UOkyEbGQg7kyHQmReF89WJHQJw2eV4P/yZ0E+b71cczJ4pPobVhXxgQcmfSTgGHxQ==",
+ "dependencies": [
+ "ansi-color",
+ "error",
+ "hexer",
+ "xtend"
+ ]
+ },
+ "builtin-modules@3.3.0": {
+ "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw=="
+ },
+ "builtins@5.1.0": {
+ "integrity": "sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==",
+ "dependencies": [
+ "semver@7.6.3"
+ ]
+ },
+ "byline@5.0.0": {
+ "integrity": "sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q=="
+ },
+ "bytes@3.1.2": {
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="
+ },
+ "cac@6.7.14": {
+ "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="
+ },
+ "cache-base@1.0.1": {
+ "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
+ "dependencies": [
+ "collection-visit",
+ "component-emitter",
+ "get-value",
+ "has-value@1.0.0",
+ "isobject@3.0.1",
+ "set-value",
+ "to-object-path",
+ "union-value",
+ "unset-value"
+ ]
+ },
+ "cacheable-lookup@7.0.0": {
+ "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w=="
+ },
+ "cacheable-request@10.2.14": {
+ "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==",
+ "dependencies": [
+ "@types/http-cache-semantics",
+ "get-stream@6.0.1",
+ "http-cache-semantics",
+ "keyv",
+ "mimic-response@4.0.0",
+ "normalize-url",
+ "responselike"
+ ]
+ },
+ "cachedir@2.4.0": {
+ "integrity": "sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ=="
+ },
+ "call-bind@1.0.7": {
+ "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
+ "dependencies": [
+ "es-define-property",
+ "es-errors",
+ "function-bind",
+ "get-intrinsic",
+ "set-function-length"
+ ]
+ },
+ "callsite@1.0.0": {
+ "integrity": "sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ=="
+ },
+ "callsites@3.1.0": {
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
+ },
+ "camelcase@6.3.0": {
+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA=="
+ },
+ "camelcase@7.0.1": {
+ "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw=="
+ },
+ "chai@5.1.1": {
+ "integrity": "sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==",
+ "dependencies": [
+ "assertion-error",
+ "check-error",
+ "deep-eql",
+ "loupe",
+ "pathval"
+ ]
+ },
+ "chalk@1.1.3": {
+ "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
+ "dependencies": [
+ "ansi-styles@2.2.1",
+ "escape-string-regexp@1.0.5",
+ "has-ansi",
+ "strip-ansi@3.0.1",
+ "supports-color@2.0.0"
+ ]
+ },
+ "chalk@2.4.2": {
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dependencies": [
+ "ansi-styles@3.2.1",
+ "escape-string-regexp@1.0.5",
+ "supports-color@5.5.0"
+ ]
+ },
+ "chalk@4.1.2": {
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dependencies": [
+ "ansi-styles@4.3.0",
+ "supports-color@7.2.0"
+ ]
+ },
+ "chalk@5.2.0": {
+ "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA=="
+ },
+ "character-entities@2.0.2": {
+ "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="
+ },
+ "chardet@0.7.0": {
+ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="
+ },
+ "check-error@2.1.1": {
+ "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="
+ },
+ "chokidar@3.5.3": {
+ "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+ "dependencies": [
+ "anymatch",
+ "braces@3.0.3",
+ "fsevents",
+ "glob-parent@5.1.2",
+ "is-binary-path",
+ "is-glob",
+ "normalize-path@3.0.0",
+ "readdirp@3.6.0"
+ ]
+ },
+ "chownr@2.0.0": {
+ "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="
+ },
+ "ci-info@3.8.0": {
+ "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw=="
+ },
+ "class-utils@0.3.6": {
+ "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
+ "dependencies": [
+ "arr-union",
+ "define-property@0.2.5",
+ "isobject@3.0.1",
+ "static-extend"
+ ]
+ },
+ "clean-deep@3.4.0": {
+ "integrity": "sha512-Lo78NV5ItJL/jl+B5w0BycAisaieJGXK1qYi/9m4SjR8zbqmrUtO7Yhro40wEShGmmxs/aJLI/A+jNhdkXK8mw==",
+ "dependencies": [
+ "lodash.isempty",
+ "lodash.isplainobject",
+ "lodash.transform"
+ ]
+ },
+ "clean-stack@4.2.0": {
+ "integrity": "sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==",
+ "dependencies": [
+ "escape-string-regexp@5.0.0"
+ ]
+ },
+ "cli-boxes@3.0.0": {
+ "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g=="
+ },
+ "cli-cursor@2.1.0": {
+ "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==",
+ "dependencies": [
+ "restore-cursor@2.0.0"
+ ]
+ },
+ "cli-cursor@4.0.0": {
+ "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==",
+ "dependencies": [
+ "restore-cursor@4.0.0"
+ ]
+ },
+ "cli-progress@3.12.0": {
+ "integrity": "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==",
+ "dependencies": [
+ "string-width@4.2.3"
+ ]
+ },
+ "cli-spinners@2.9.2": {
+ "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="
+ },
+ "cli-truncate@0.2.1": {
+ "integrity": "sha512-f4r4yJnbT++qUPI9NR4XLDLq41gQ+uqnPItWG0F5ZkehuNiTTa3EY0S4AqTSUOeJ7/zU41oWPQSNkW5BqPL9bg==",
+ "dependencies": [
+ "slice-ansi@0.0.4",
+ "string-width@1.0.2"
+ ]
+ },
+ "cli-width@2.2.1": {
+ "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw=="
+ },
+ "cliui@8.0.1": {
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dependencies": [
+ "string-width@4.2.3",
+ "strip-ansi@6.0.1",
+ "wrap-ansi@7.0.0"
+ ]
+ },
+ "clone@1.0.4": {
+ "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg=="
+ },
+ "code-point-at@1.1.0": {
+ "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA=="
+ },
+ "collection-visit@1.0.0": {
+ "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==",
+ "dependencies": [
+ "map-visit",
+ "object-visit"
+ ]
+ },
+ "color-convert@1.9.3": {
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dependencies": [
+ "color-name@1.1.3"
+ ]
+ },
+ "color-convert@2.0.1": {
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dependencies": [
+ "color-name@1.1.4"
+ ]
+ },
+ "color-name@1.1.3": {
+ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
+ },
+ "color-name@1.1.4": {
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ },
+ "color-string@1.9.1": {
+ "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
+ "dependencies": [
+ "color-name@1.1.4",
+ "simple-swizzle"
+ ]
+ },
+ "color-support@1.1.3": {
+ "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg=="
+ },
+ "color@3.2.1": {
+ "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==",
+ "dependencies": [
+ "color-convert@1.9.3",
+ "color-string"
+ ]
+ },
+ "colors-option@3.0.0": {
+ "integrity": "sha512-DP3FpjsiDDvnQC1OJBsdOJZPuy7r0o6sepY2T5M3L/d2nrE23O/ErFkEqyY3ngVL1ZhTj/H0pCMNObZGkEOaaQ==",
+ "dependencies": [
+ "chalk@5.2.0",
+ "filter-obj@3.0.0",
+ "is-plain-obj@4.1.0",
+ "jest-validate"
+ ]
+ },
+ "colors-option@4.5.0": {
+ "integrity": "sha512-Soe5lerRg3erMRgYC0EC696/8dMCGpBzcQchFfi55Yrkja8F+P7cUt0LVTIg7u5ob5BexLZ/F1kO+ejmv+nq8w==",
+ "dependencies": [
+ "chalk@5.2.0",
+ "is-plain-obj@4.1.0"
+ ]
+ },
+ "colors@1.4.0": {
+ "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA=="
+ },
+ "colorspace@1.1.4": {
+ "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==",
+ "dependencies": [
+ "color",
+ "text-hex"
+ ]
+ },
+ "combined-stream@1.0.8": {
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dependencies": [
+ "delayed-stream"
+ ]
+ },
+ "commander@10.0.1": {
+ "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug=="
+ },
+ "commander@2.20.3": {
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+ },
+ "commander@9.5.0": {
+ "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ=="
+ },
+ "comment-json@4.2.3": {
+ "integrity": "sha512-SsxdiOf064DWoZLH799Ata6u7iV658A11PlWtZATDlXPpKGJnbJZ5Z24ybixAi+LUUqJ/GKowAejtC5GFUG7Tw==",
+ "dependencies": [
+ "array-timsort",
+ "core-util-is",
+ "esprima",
+ "has-own-prop",
+ "repeat-string"
+ ]
+ },
+ "comment-parser@1.4.1": {
+ "integrity": "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg=="
+ },
+ "common-path-prefix@3.0.0": {
+ "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w=="
+ },
+ "component-emitter@1.3.1": {
+ "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ=="
+ },
+ "compress-commons@4.1.2": {
+ "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==",
+ "dependencies": [
+ "buffer-crc32@0.2.13",
+ "crc32-stream@4.0.3",
+ "normalize-path@3.0.0",
+ "readable-stream@3.6.2"
+ ]
+ },
+ "compress-commons@6.0.2": {
+ "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==",
+ "dependencies": [
+ "crc-32",
+ "crc32-stream@6.0.0",
+ "is-stream@2.0.1",
+ "normalize-path@3.0.0",
+ "readable-stream@4.5.2"
+ ]
+ },
+ "concat-map@0.0.1": {
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
+ },
+ "concordance@5.0.4": {
+ "integrity": "sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw==",
+ "dependencies": [
+ "date-time",
+ "esutils",
+ "fast-diff",
+ "js-string-escape",
+ "lodash",
+ "md5-hex",
+ "semver@7.6.3",
+ "well-known-symbols"
+ ]
+ },
+ "config-chain@1.1.13": {
+ "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==",
+ "dependencies": [
+ "ini@1.3.8",
+ "proto-list"
+ ]
+ },
+ "configstore@6.0.0": {
+ "integrity": "sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==",
+ "dependencies": [
+ "dot-prop@6.0.1",
+ "graceful-fs@4.2.11",
+ "unique-string",
+ "write-file-atomic@3.0.3",
+ "xdg-basedir"
+ ]
+ },
+ "console-control-strings@1.1.0": {
+ "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ=="
+ },
+ "content-disposition@0.5.4": {
+ "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+ "dependencies": [
+ "safe-buffer@5.2.1"
+ ]
+ },
+ "content-type@1.0.5": {
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="
+ },
+ "cookie-signature@1.0.6": {
+ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
+ },
+ "cookie@0.5.0": {
+ "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw=="
+ },
+ "cookie@0.6.0": {
+ "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="
+ },
+ "copy-descriptor@0.1.1": {
+ "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw=="
+ },
+ "copy-template-dir@1.4.0": {
+ "integrity": "sha512-xkXSJhvKz4MfLbVkZ7GyCaFo4ciB3uKI/HHzkGwj1eyTH5+7RTFxW5CE0irWAZgV5oFcO9hd6+NVXAtY9hlo7Q==",
+ "dependencies": [
+ "end-of-stream",
+ "graceful-fs@4.2.11",
+ "maxstache",
+ "maxstache-stream",
+ "mkdirp@0.5.6",
+ "noop2",
+ "pump@1.0.3",
+ "readdirp@2.2.1",
+ "run-parallel"
+ ]
+ },
+ "core-util-is@1.0.3": {
+ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
+ },
+ "cp-file@10.0.0": {
+ "integrity": "sha512-vy2Vi1r2epK5WqxOLnskeKeZkdZvTKfFZQCplE3XWsP+SUJyd5XAUFC9lFgTjjXJF2GMne/UML14iEmkAaDfFg==",
+ "dependencies": [
+ "graceful-fs@4.2.11",
+ "nested-error-stacks",
+ "p-event@5.0.1"
+ ]
+ },
+ "cp-file@9.1.0": {
+ "integrity": "sha512-3scnzFj/94eb7y4wyXRWwvzLFaQp87yyfTnChIjlfYrVqp5lVO3E2hIJMeQIltUT0K2ZAB3An1qXcBmwGyvuwA==",
+ "dependencies": [
+ "graceful-fs@4.2.11",
+ "make-dir@3.1.0",
+ "nested-error-stacks",
+ "p-event@4.2.0"
+ ]
+ },
+ "cpy@9.0.1": {
+ "integrity": "sha512-D9U0DR5FjTCN3oMTcFGktanHnAG5l020yvOCR1zKILmAyPP7I/9pl6NFgRbDcmSENtbK1sQLBz1p9HIOlroiNg==",
+ "dependencies": [
+ "arrify",
+ "cp-file@9.1.0",
+ "globby@13.2.2",
+ "junk",
+ "micromatch@4.0.8",
+ "nested-error-stacks",
+ "p-filter",
+ "p-map@5.5.0"
+ ]
+ },
+ "crc-32@1.2.2": {
+ "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ=="
+ },
+ "crc32-stream@4.0.3": {
+ "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==",
+ "dependencies": [
+ "crc-32",
+ "readable-stream@3.6.2"
+ ]
+ },
+ "crc32-stream@6.0.0": {
+ "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==",
+ "dependencies": [
+ "crc-32",
+ "readable-stream@4.5.2"
+ ]
+ },
+ "create-require@1.1.1": {
+ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ=="
+ },
+ "cron-parser@4.8.1": {
+ "integrity": "sha512-jbokKWGcyU4gl6jAfX97E1gDpY12DJ1cLJZmoDzaAln/shZ+S3KBFBuA2Q6WeUN4gJf/8klnV1EfvhA2lK5IRQ==",
+ "dependencies": [
+ "luxon"
+ ]
+ },
+ "cross-spawn@7.0.3": {
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dependencies": [
+ "path-key@3.1.1",
+ "shebang-command",
+ "which"
+ ]
+ },
+ "crypto-random-string@4.0.0": {
+ "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==",
+ "dependencies": [
+ "type-fest@1.4.0"
+ ]
+ },
+ "cyclist@1.0.2": {
+ "integrity": "sha512-0sVXIohTfLqVIW3kb/0n6IiWF3Ifj5nm2XaSrLq2DI6fKIGa2fYAZdk917rUneaeLVpYfFcyXE2ft0fe3remsA=="
+ },
+ "damerau-levenshtein@1.0.8": {
+ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA=="
+ },
+ "data-uri-to-buffer@4.0.1": {
+ "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="
+ },
+ "data-view-buffer@1.0.1": {
+ "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==",
+ "dependencies": [
+ "call-bind",
+ "es-errors",
+ "is-data-view"
+ ]
+ },
+ "data-view-byte-length@1.0.1": {
+ "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==",
+ "dependencies": [
+ "call-bind",
+ "es-errors",
+ "is-data-view"
+ ]
+ },
+ "data-view-byte-offset@1.0.0": {
+ "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==",
+ "dependencies": [
+ "call-bind",
+ "es-errors",
+ "is-data-view"
+ ]
+ },
+ "date-fns@1.30.1": {
+ "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw=="
+ },
+ "date-time@3.1.0": {
+ "integrity": "sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==",
+ "dependencies": [
+ "time-zone"
+ ]
+ },
+ "debug@2.6.9": {
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dependencies": [
+ "ms@2.0.0"
+ ]
+ },
+ "debug@3.2.7": {
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dependencies": [
+ "ms@2.1.3"
+ ]
+ },
+ "debug@4.3.4": {
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dependencies": [
+ "ms@2.1.2"
+ ]
+ },
+ "debug@4.3.7": {
+ "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
+ "dependencies": [
+ "ms@2.1.3"
+ ]
+ },
+ "decache@4.6.2": {
+ "integrity": "sha512-2LPqkLeu8XWHU8qNCS3kcF6sCcb5zIzvWaAHYSvPfwhdd7mHuah29NssMzrTYyHN4F5oFy2ko9OBYxegtU0FEw==",
+ "dependencies": [
+ "callsite"
+ ]
+ },
+ "decode-named-character-reference@1.0.2": {
+ "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==",
+ "dependencies": [
+ "character-entities"
+ ]
+ },
+ "decode-uri-component@0.2.2": {
+ "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ=="
+ },
+ "decompress-response@6.0.0": {
+ "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+ "dependencies": [
+ "mimic-response@3.1.0"
+ ]
+ },
+ "deep-eql@5.0.2": {
+ "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="
+ },
+ "deep-equal@2.2.3": {
+ "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==",
+ "dependencies": [
+ "array-buffer-byte-length",
+ "call-bind",
+ "es-get-iterator",
+ "get-intrinsic",
+ "is-arguments",
+ "is-array-buffer",
+ "is-date-object",
+ "is-regex",
+ "is-shared-array-buffer",
+ "isarray@2.0.5",
+ "object-is",
+ "object-keys",
+ "object.assign",
+ "regexp.prototype.flags",
+ "side-channel",
+ "which-boxed-primitive",
+ "which-collection",
+ "which-typed-array"
+ ]
+ },
+ "deep-extend@0.6.0": {
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
+ },
+ "deep-is@0.1.4": {
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="
+ },
+ "deepmerge@4.3.1": {
+ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="
+ },
+ "defaults@1.0.4": {
+ "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
+ "dependencies": [
+ "clone"
+ ]
+ },
+ "defer-to-connect@2.0.1": {
+ "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg=="
+ },
+ "define-data-property@1.1.4": {
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dependencies": [
+ "es-define-property",
+ "es-errors",
+ "gopd"
+ ]
+ },
+ "define-lazy-prop@2.0.0": {
+ "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og=="
+ },
+ "define-properties@1.2.1": {
+ "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
+ "dependencies": [
+ "define-data-property",
+ "has-property-descriptors",
+ "object-keys"
+ ]
+ },
+ "define-property@0.2.5": {
+ "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+ "dependencies": [
+ "is-descriptor@0.1.7"
+ ]
+ },
+ "define-property@1.0.0": {
+ "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
+ "dependencies": [
+ "is-descriptor@1.0.3"
+ ]
+ },
+ "define-property@2.0.2": {
+ "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
+ "dependencies": [
+ "is-descriptor@1.0.3",
+ "isobject@3.0.1"
+ ]
+ },
+ "delayed-stream@1.0.0": {
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
+ },
+ "delegates@1.0.0": {
+ "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ=="
+ },
+ "depd@1.1.2": {
+ "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ=="
+ },
+ "depd@2.0.0": {
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
+ },
+ "deprecation@2.3.1": {
+ "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
+ },
+ "dequal@2.0.3": {
+ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="
+ },
+ "destroy@1.2.0": {
+ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="
+ },
+ "detect-libc@2.0.3": {
+ "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw=="
+ },
+ "detective-amd@5.0.2": {
+ "integrity": "sha512-XFd/VEQ76HSpym80zxM68ieB77unNuoMwopU2TFT/ErUk5n4KvUTwW4beafAVUugrjV48l4BmmR0rh2MglBaiA==",
+ "dependencies": [
+ "ast-module-types",
+ "escodegen",
+ "get-amd-module-type",
+ "node-source-walk"
+ ]
+ },
+ "detective-cjs@5.0.1": {
+ "integrity": "sha512-6nTvAZtpomyz/2pmEmGX1sXNjaqgMplhQkskq2MLrar0ZAIkHMrDhLXkRiK2mvbu9wSWr0V5/IfiTrZqAQMrmQ==",
+ "dependencies": [
+ "ast-module-types",
+ "node-source-walk"
+ ]
+ },
+ "detective-es6@4.0.1": {
+ "integrity": "sha512-k3Z5tB4LQ8UVHkuMrFOlvb3GgFWdJ9NqAa2YLUU/jTaWJIm+JJnEh4PsMc+6dfT223Y8ACKOaC0qcj7diIhBKw==",
+ "dependencies": [
+ "node-source-walk"
+ ]
+ },
+ "detective-postcss@6.1.3_postcss@8.4.47": {
+ "integrity": "sha512-7BRVvE5pPEvk2ukUWNQ+H2XOq43xENWbH0LcdCE14mwgTBEAMoAx+Fc1rdp76SmyZ4Sp48HlV7VedUnP6GA1Tw==",
+ "dependencies": [
+ "is-url",
+ "postcss",
+ "postcss-values-parser"
+ ]
+ },
+ "detective-sass@5.0.3": {
+ "integrity": "sha512-YsYT2WuA8YIafp2RVF5CEfGhhyIVdPzlwQgxSjK+TUm3JoHP+Tcorbk3SfG0cNZ7D7+cYWa0ZBcvOaR0O8+LlA==",
+ "dependencies": [
+ "gonzales-pe",
+ "node-source-walk"
+ ]
+ },
+ "detective-scss@4.0.3": {
+ "integrity": "sha512-VYI6cHcD0fLokwqqPFFtDQhhSnlFWvU614J42eY6G0s8c+MBhi9QAWycLwIOGxlmD8I/XvGSOUV1kIDhJ70ZPg==",
+ "dependencies": [
+ "gonzales-pe",
+ "node-source-walk"
+ ]
+ },
+ "detective-stylus@4.0.0": {
+ "integrity": "sha512-TfPotjhszKLgFBzBhTOxNHDsutIxx9GTWjrL5Wh7Qx/ydxKhwUrlSFeLIn+ZaHPF+h0siVBkAQSuy6CADyTxgQ=="
+ },
+ "detective-typescript@11.2.0_typescript@5.6.2": {
+ "integrity": "sha512-ARFxjzizOhPqs1fYC/2NMC3N4jrQ6HvVflnXBTRqNEqJuXwyKLRr9CrJwkRcV/SnZt1sNXgsF6FPm0x57Tq0rw==",
+ "dependencies": [
+ "@typescript-eslint/typescript-estree@5.62.0_typescript@5.6.2",
+ "ast-module-types",
+ "node-source-walk",
+ "typescript@5.6.2"
+ ]
+ },
+ "devlop@1.1.0": {
+ "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
+ "dependencies": [
+ "dequal"
+ ]
+ },
+ "diff@4.0.2": {
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A=="
+ },
+ "dir-glob@3.0.1": {
+ "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+ "dependencies": [
+ "path-type@4.0.0"
+ ]
+ },
+ "doctrine@2.1.0": {
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "dependencies": [
+ "esutils"
+ ]
+ },
+ "doctrine@3.0.0": {
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dependencies": [
+ "esutils"
+ ]
+ },
+ "dot-prop@6.0.1": {
+ "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==",
+ "dependencies": [
+ "is-obj"
+ ]
+ },
+ "dot-prop@7.2.0": {
+ "integrity": "sha512-Ol/IPXUARn9CSbkrdV4VJo7uCy1I3VuSiWCaFSg+8BdUOzF9n3jefIpcgAydvUZbTdEBZs2vEiTiS9m61ssiDA==",
+ "dependencies": [
+ "type-fest@2.19.0"
+ ]
+ },
+ "dotenv@16.0.3": {
+ "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ=="
+ },
+ "dotenv@16.4.5": {
+ "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg=="
+ },
+ "eastasianwidth@0.2.0": {
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
+ },
+ "ecdsa-sig-formatter@1.0.11": {
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "dependencies": [
+ "safe-buffer@5.2.1"
+ ]
+ },
+ "ee-first@1.1.1": {
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
+ },
+ "elegant-spinner@1.0.1": {
+ "integrity": "sha512-B+ZM+RXvRqQaAmkMlO/oSe5nMUOaUnyfGYCEHoR8wrXsZR2mA0XVibsxV1bvTwxdRWah1PkQqso2EzhILGHtEQ=="
+ },
+ "emoji-regex@8.0.0": {
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+ },
+ "emoji-regex@9.2.2": {
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
+ },
+ "enabled@2.0.0": {
+ "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ=="
+ },
+ "encodeurl@1.0.2": {
+ "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="
+ },
+ "end-of-stream@1.4.4": {
+ "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+ "dependencies": [
+ "once"
+ ]
+ },
+ "entities@2.2.0": {
+ "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="
+ },
+ "env-paths@3.0.0": {
+ "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A=="
+ },
+ "envinfo@7.8.1": {
+ "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw=="
+ },
+ "error-ex@1.3.2": {
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dependencies": [
+ "is-arrayish@0.2.1"
+ ]
+ },
+ "error-stack-parser@2.1.4": {
+ "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==",
+ "dependencies": [
+ "stackframe"
+ ]
+ },
+ "error@7.0.2": {
+ "integrity": "sha512-UtVv4l5MhijsYUxPJo4390gzfZvAnTHreNnDjnTZaKIiZ/SemXxAhBkYSKtWa5RtBXbLP8tMgn/n0RUa/H7jXw==",
+ "dependencies": [
+ "string-template",
+ "xtend"
+ ]
+ },
+ "es-abstract@1.23.3": {
+ "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==",
+ "dependencies": [
+ "array-buffer-byte-length",
+ "arraybuffer.prototype.slice",
+ "available-typed-arrays",
+ "call-bind",
+ "data-view-buffer",
+ "data-view-byte-length",
+ "data-view-byte-offset",
+ "es-define-property",
+ "es-errors",
+ "es-object-atoms",
+ "es-set-tostringtag",
+ "es-to-primitive",
+ "function.prototype.name",
+ "get-intrinsic",
+ "get-symbol-description",
+ "globalthis",
+ "gopd",
+ "has-property-descriptors",
+ "has-proto",
+ "has-symbols",
+ "hasown",
+ "internal-slot",
+ "is-array-buffer",
+ "is-callable",
+ "is-data-view",
+ "is-negative-zero",
+ "is-regex",
+ "is-shared-array-buffer",
+ "is-string",
+ "is-typed-array",
+ "is-weakref",
+ "object-inspect",
+ "object-keys",
+ "object.assign",
+ "regexp.prototype.flags",
+ "safe-array-concat",
+ "safe-regex-test",
+ "string.prototype.trim",
+ "string.prototype.trimend",
+ "string.prototype.trimstart",
+ "typed-array-buffer",
+ "typed-array-byte-length",
+ "typed-array-byte-offset",
+ "typed-array-length",
+ "unbox-primitive",
+ "which-typed-array"
+ ]
+ },
+ "es-define-property@1.0.0": {
+ "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
+ "dependencies": [
+ "get-intrinsic"
+ ]
+ },
+ "es-errors@1.3.0": {
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="
+ },
+ "es-get-iterator@1.1.3": {
+ "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==",
+ "dependencies": [
+ "call-bind",
+ "get-intrinsic",
+ "has-symbols",
+ "is-arguments",
+ "is-map",
+ "is-set",
+ "is-string",
+ "isarray@2.0.5",
+ "stop-iteration-iterator"
+ ]
+ },
+ "es-iterator-helpers@1.0.19": {
+ "integrity": "sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==",
+ "dependencies": [
+ "call-bind",
+ "define-properties",
+ "es-abstract",
+ "es-errors",
+ "es-set-tostringtag",
+ "function-bind",
+ "get-intrinsic",
+ "globalthis",
+ "has-property-descriptors",
+ "has-proto",
+ "has-symbols",
+ "internal-slot",
+ "iterator.prototype",
+ "safe-array-concat"
+ ]
+ },
+ "es-module-lexer@1.5.4": {
+ "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw=="
+ },
+ "es-object-atoms@1.0.0": {
+ "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==",
+ "dependencies": [
+ "es-errors"
+ ]
+ },
+ "es-set-tostringtag@2.0.3": {
+ "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==",
+ "dependencies": [
+ "get-intrinsic",
+ "has-tostringtag",
+ "hasown"
+ ]
+ },
+ "es-shim-unscopables@1.0.2": {
+ "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==",
+ "dependencies": [
+ "hasown"
+ ]
+ },
+ "es-to-primitive@1.2.1": {
+ "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+ "dependencies": [
+ "is-callable",
+ "is-date-object",
+ "is-symbol"
+ ]
+ },
+ "es6-promisify@6.1.1": {
+ "integrity": "sha512-HBL8I3mIki5C1Cc9QjKUenHtnG0A5/xA8Q/AllRcfiwl2CZFXGK7ddBiCoRwAix4i2KxcQfjtIVcrVbB3vbmwg=="
+ },
+ "esbuild@0.19.11": {
+ "integrity": "sha512-HJ96Hev2hX/6i5cDVwcqiJBBtuo9+FeIJOtZ9W1kA5M6AMJRHUZlpYZ1/SbEwtO0ioNAW8rUooVpC/WehY2SfA==",
+ "dependencies": [
+ "@esbuild/aix-ppc64@0.19.11",
+ "@esbuild/android-arm@0.19.11",
+ "@esbuild/android-arm64@0.19.11",
+ "@esbuild/android-x64@0.19.11",
+ "@esbuild/darwin-arm64@0.19.11",
+ "@esbuild/darwin-x64@0.19.11",
+ "@esbuild/freebsd-arm64@0.19.11",
+ "@esbuild/freebsd-x64@0.19.11",
+ "@esbuild/linux-arm@0.19.11",
+ "@esbuild/linux-arm64@0.19.11",
+ "@esbuild/linux-ia32@0.19.11",
+ "@esbuild/linux-loong64@0.19.11",
+ "@esbuild/linux-mips64el@0.19.11",
+ "@esbuild/linux-ppc64@0.19.11",
+ "@esbuild/linux-riscv64@0.19.11",
+ "@esbuild/linux-s390x@0.19.11",
+ "@esbuild/linux-x64@0.19.11",
+ "@esbuild/netbsd-x64@0.19.11",
+ "@esbuild/openbsd-x64@0.19.11",
+ "@esbuild/sunos-x64@0.19.11",
+ "@esbuild/win32-arm64@0.19.11",
+ "@esbuild/win32-ia32@0.19.11",
+ "@esbuild/win32-x64@0.19.11"
+ ]
+ },
+ "esbuild@0.19.2": {
+ "integrity": "sha512-G6hPax8UbFakEj3hWO0Vs52LQ8k3lnBhxZWomUJDxfz3rZTLqF5k/FCzuNdLx2RbpBiQQF9H9onlDDH1lZsnjg==",
+ "dependencies": [
+ "@esbuild/android-arm@0.19.2",
+ "@esbuild/android-arm64@0.19.2",
+ "@esbuild/android-x64@0.19.2",
+ "@esbuild/darwin-arm64@0.19.2",
+ "@esbuild/darwin-x64@0.19.2",
+ "@esbuild/freebsd-arm64@0.19.2",
+ "@esbuild/freebsd-x64@0.19.2",
+ "@esbuild/linux-arm@0.19.2",
+ "@esbuild/linux-arm64@0.19.2",
+ "@esbuild/linux-ia32@0.19.2",
+ "@esbuild/linux-loong64@0.19.2",
+ "@esbuild/linux-mips64el@0.19.2",
+ "@esbuild/linux-ppc64@0.19.2",
+ "@esbuild/linux-riscv64@0.19.2",
+ "@esbuild/linux-s390x@0.19.2",
+ "@esbuild/linux-x64@0.19.2",
+ "@esbuild/netbsd-x64@0.19.2",
+ "@esbuild/openbsd-x64@0.19.2",
+ "@esbuild/sunos-x64@0.19.2",
+ "@esbuild/win32-arm64@0.19.2",
+ "@esbuild/win32-ia32@0.19.2",
+ "@esbuild/win32-x64@0.19.2"
+ ]
+ },
+ "esbuild@0.21.5": {
+ "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
+ "dependencies": [
+ "@esbuild/aix-ppc64@0.21.5",
+ "@esbuild/android-arm@0.21.5",
+ "@esbuild/android-arm64@0.21.5",
+ "@esbuild/android-x64@0.21.5",
+ "@esbuild/darwin-arm64@0.21.5",
+ "@esbuild/darwin-x64@0.21.5",
+ "@esbuild/freebsd-arm64@0.21.5",
+ "@esbuild/freebsd-x64@0.21.5",
+ "@esbuild/linux-arm@0.21.5",
+ "@esbuild/linux-arm64@0.21.5",
+ "@esbuild/linux-ia32@0.21.5",
+ "@esbuild/linux-loong64@0.21.5",
+ "@esbuild/linux-mips64el@0.21.5",
+ "@esbuild/linux-ppc64@0.21.5",
+ "@esbuild/linux-riscv64@0.21.5",
+ "@esbuild/linux-s390x@0.21.5",
+ "@esbuild/linux-x64@0.21.5",
+ "@esbuild/netbsd-x64@0.21.5",
+ "@esbuild/openbsd-x64@0.21.5",
+ "@esbuild/sunos-x64@0.21.5",
+ "@esbuild/win32-arm64@0.21.5",
+ "@esbuild/win32-ia32@0.21.5",
+ "@esbuild/win32-x64@0.21.5"
+ ]
+ },
+ "escalade@3.2.0": {
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="
+ },
+ "escape-goat@4.0.0": {
+ "integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg=="
+ },
+ "escape-html@1.0.3": {
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
+ },
+ "escape-string-regexp@1.0.5": {
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="
+ },
+ "escape-string-regexp@4.0.0": {
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
+ },
+ "escape-string-regexp@5.0.0": {
+ "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="
+ },
+ "escodegen@2.1.0": {
+ "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
+ "dependencies": [
+ "esprima",
+ "estraverse",
+ "esutils",
+ "source-map@0.6.1"
+ ]
+ },
+ "eslint-config-prettier@9.1.0_eslint@8.57.1": {
+ "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==",
+ "dependencies": [
+ "eslint"
+ ]
+ },
+ "eslint-import-resolver-node@0.3.9": {
+ "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==",
+ "dependencies": [
+ "debug@3.2.7",
+ "is-core-module",
+ "resolve@1.22.8"
+ ]
+ },
+ "eslint-module-utils@2.12.0": {
+ "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==",
+ "dependencies": [
+ "debug@3.2.7"
+ ]
+ },
+ "eslint-plugin-import@2.31.0_eslint@8.57.1": {
+ "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==",
+ "dependencies": [
+ "@rtsao/scc",
+ "array-includes",
+ "array.prototype.findlastindex",
+ "array.prototype.flat",
+ "array.prototype.flatmap",
+ "debug@3.2.7",
+ "doctrine@2.1.0",
+ "eslint",
+ "eslint-import-resolver-node",
+ "eslint-module-utils",
+ "hasown",
+ "is-core-module",
+ "is-glob",
+ "minimatch@3.1.2",
+ "object.fromentries",
+ "object.groupby",
+ "object.values",
+ "semver@6.3.1",
+ "string.prototype.trimend",
+ "tsconfig-paths"
+ ]
+ },
+ "eslint-plugin-jsx-a11y@6.10.0_eslint@8.57.1": {
+ "integrity": "sha512-ySOHvXX8eSN6zz8Bywacm7CvGNhUtdjvqfQDVe6020TUK34Cywkw7m0KsCCk1Qtm9G1FayfTN1/7mMYnYO2Bhg==",
+ "dependencies": [
+ "aria-query",
+ "array-includes",
+ "array.prototype.flatmap",
+ "ast-types-flow",
+ "axe-core",
+ "axobject-query",
+ "damerau-levenshtein",
+ "emoji-regex@9.2.2",
+ "es-iterator-helpers",
+ "eslint",
+ "hasown",
+ "jsx-ast-utils",
+ "language-tags",
+ "minimatch@3.1.2",
+ "object.fromentries",
+ "safe-regex-test",
+ "string.prototype.includes"
+ ]
+ },
+ "eslint-plugin-prettier@5.2.1_eslint@8.57.1_eslint-config-prettier@9.1.0__eslint@8.57.1_prettier@3.2.5": {
+ "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==",
+ "dependencies": [
+ "eslint",
+ "eslint-config-prettier",
+ "prettier",
+ "prettier-linter-helpers",
+ "synckit"
+ ]
+ },
+ "eslint-plugin-react-hooks@4.6.2_eslint@8.57.1": {
+ "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==",
+ "dependencies": [
+ "eslint"
+ ]
+ },
+ "eslint-plugin-react@7.37.1_eslint@8.57.1": {
+ "integrity": "sha512-xwTnwDqzbDRA8uJ7BMxPs/EXRB3i8ZfnOIp8BsxEQkT0nHPp+WWceqGgo6rKb9ctNi8GJLDT4Go5HAWELa/WMg==",
+ "dependencies": [
+ "array-includes",
+ "array.prototype.findlast",
+ "array.prototype.flatmap",
+ "array.prototype.tosorted",
+ "doctrine@2.1.0",
+ "es-iterator-helpers",
+ "eslint",
+ "estraverse",
+ "hasown",
+ "jsx-ast-utils",
+ "minimatch@3.1.2",
+ "object.entries",
+ "object.fromentries",
+ "object.values",
+ "prop-types",
+ "resolve@2.0.0-next.5",
+ "semver@6.3.1",
+ "string.prototype.matchall",
+ "string.prototype.repeat"
+ ]
+ },
+ "eslint-scope@7.2.2": {
+ "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+ "dependencies": [
+ "esrecurse",
+ "estraverse"
+ ]
+ },
+ "eslint-visitor-keys@3.4.3": {
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="
+ },
+ "eslint@8.57.1": {
+ "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==",
+ "dependencies": [
+ "@eslint-community/eslint-utils",
+ "@eslint-community/regexpp",
+ "@eslint/eslintrc",
+ "@eslint/js",
+ "@humanwhocodes/config-array",
+ "@humanwhocodes/module-importer",
+ "@nodelib/fs.walk",
+ "@ungap/structured-clone",
+ "ajv@6.12.6",
+ "chalk@4.1.2",
+ "cross-spawn",
+ "debug@4.3.7",
+ "doctrine@3.0.0",
+ "escape-string-regexp@4.0.0",
+ "eslint-scope",
+ "eslint-visitor-keys",
+ "espree",
+ "esquery",
+ "esutils",
+ "fast-deep-equal",
+ "file-entry-cache",
+ "find-up@5.0.0",
+ "glob-parent@6.0.2",
+ "globals",
+ "graphemer",
+ "ignore",
+ "imurmurhash",
+ "is-glob",
+ "is-path-inside@3.0.3",
+ "js-yaml",
+ "json-stable-stringify-without-jsonify",
+ "levn",
+ "lodash.merge",
+ "minimatch@3.1.2",
+ "natural-compare",
+ "optionator",
+ "strip-ansi@6.0.1",
+ "text-table"
+ ]
+ },
+ "espree@9.6.1_acorn@8.12.1": {
+ "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+ "dependencies": [
+ "acorn",
+ "acorn-jsx",
+ "eslint-visitor-keys"
+ ]
+ },
+ "esprima@4.0.1": {
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
+ },
+ "esquery@1.6.0": {
+ "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+ "dependencies": [
+ "estraverse"
+ ]
+ },
+ "esrecurse@4.3.0": {
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dependencies": [
+ "estraverse"
+ ]
+ },
+ "estraverse@5.3.0": {
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="
+ },
+ "estree-walker@2.0.2": {
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
+ },
+ "estree-walker@3.0.3": {
+ "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+ "dependencies": [
+ "@types/estree"
+ ]
+ },
+ "esutils@2.0.3": {
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="
+ },
+ "etag@1.8.1": {
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="
+ },
+ "event-target-shim@5.0.1": {
+ "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="
+ },
+ "eventemitter3@4.0.7": {
+ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
+ },
+ "events@3.3.0": {
+ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="
+ },
+ "execa@5.1.1": {
+ "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+ "dependencies": [
+ "cross-spawn",
+ "get-stream@6.0.1",
+ "human-signals@2.1.0",
+ "is-stream@2.0.1",
+ "merge-stream",
+ "npm-run-path@4.0.1",
+ "onetime@5.1.2",
+ "signal-exit@3.0.7",
+ "strip-final-newline@2.0.0"
+ ]
+ },
+ "execa@6.1.0": {
+ "integrity": "sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==",
+ "dependencies": [
+ "cross-spawn",
+ "get-stream@6.0.1",
+ "human-signals@3.0.1",
+ "is-stream@3.0.0",
+ "merge-stream",
+ "npm-run-path@5.3.0",
+ "onetime@6.0.0",
+ "signal-exit@3.0.7",
+ "strip-final-newline@3.0.0"
+ ]
+ },
+ "expand-brackets@2.1.4": {
+ "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==",
+ "dependencies": [
+ "debug@2.6.9",
+ "define-property@0.2.5",
+ "extend-shallow@2.0.1",
+ "posix-character-classes",
+ "regex-not",
+ "snapdragon",
+ "to-regex"
+ ]
+ },
+ "express-logging@1.1.1": {
+ "integrity": "sha512-1KboYwxxCG5kwkJHR5LjFDTD1Mgl8n4PIMcCuhhd/1OqaxlC68P3QKbvvAbZVUtVgtlxEdTgSUwf6yxwzRCuuA==",
+ "dependencies": [
+ "on-headers"
+ ]
+ },
+ "express@4.18.2": {
+ "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
+ "dependencies": [
+ "accepts",
+ "array-flatten",
+ "body-parser",
+ "content-disposition",
+ "content-type",
+ "cookie@0.5.0",
+ "cookie-signature",
+ "debug@2.6.9",
+ "depd@2.0.0",
+ "encodeurl",
+ "escape-html",
+ "etag",
+ "finalhandler",
+ "fresh",
+ "http-errors@2.0.0",
+ "merge-descriptors",
+ "methods",
+ "on-finished",
+ "parseurl",
+ "path-to-regexp",
+ "proxy-addr",
+ "qs",
+ "range-parser",
+ "safe-buffer@5.2.1",
+ "send",
+ "serve-static",
+ "setprototypeof",
+ "statuses@2.0.1",
+ "type-is",
+ "utils-merge",
+ "vary"
+ ]
+ },
+ "ext-list@2.2.2": {
+ "integrity": "sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==",
+ "dependencies": [
+ "mime-db"
+ ]
+ },
+ "ext-name@5.0.0": {
+ "integrity": "sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==",
+ "dependencies": [
+ "ext-list",
+ "sort-keys-length"
+ ]
+ },
+ "extend-shallow@2.0.1": {
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "dependencies": [
+ "is-extendable@0.1.1"
+ ]
+ },
+ "extend-shallow@3.0.2": {
+ "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
+ "dependencies": [
+ "assign-symbols",
+ "is-extendable@1.0.1"
+ ]
+ },
+ "external-editor@3.1.0": {
+ "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
+ "dependencies": [
+ "chardet",
+ "iconv-lite",
+ "tmp@0.0.33"
+ ]
+ },
+ "extglob@2.0.4": {
+ "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+ "dependencies": [
+ "array-unique",
+ "define-property@1.0.0",
+ "expand-brackets",
+ "extend-shallow@2.0.1",
+ "fragment-cache",
+ "regex-not",
+ "snapdragon",
+ "to-regex"
+ ]
+ },
+ "extract-zip@2.0.1": {
+ "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
+ "dependencies": [
+ "@types/yauzl",
+ "debug@4.3.7",
+ "get-stream@5.2.0",
+ "yauzl"
+ ]
+ },
+ "fast-content-type-parse@1.1.0": {
+ "integrity": "sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ=="
+ },
+ "fast-decode-uri-component@1.0.1": {
+ "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg=="
+ },
+ "fast-deep-equal@3.1.3": {
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+ },
+ "fast-diff@1.3.0": {
+ "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw=="
+ },
+ "fast-equals@3.0.3": {
+ "integrity": "sha512-NCe8qxnZFARSHGztGMZOO/PC1qa5MIFB5Hp66WdzbCRAz8U8US3bx1UTgLS49efBQPcUtO9gf5oVEY8o7y/7Kg=="
+ },
+ "fast-fifo@1.3.2": {
+ "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ=="
+ },
+ "fast-glob@3.3.2": {
+ "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
+ "dependencies": [
+ "@nodelib/fs.stat",
+ "@nodelib/fs.walk",
+ "glob-parent@5.1.2",
+ "merge2",
+ "micromatch@4.0.8"
+ ]
+ },
+ "fast-json-stable-stringify@2.1.0": {
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
+ },
+ "fast-json-stringify@5.16.1_ajv@8.17.1": {
+ "integrity": "sha512-KAdnLvy1yu/XrRtP+LJnxbBGrhN+xXu+gt3EUvZhYGKCr3lFHq/7UFJHHFgmJKoqlh6B40bZLEv7w46B0mqn1g==",
+ "dependencies": [
+ "@fastify/merge-json-schemas",
+ "ajv@8.17.1",
+ "ajv-formats@3.0.1_ajv@8.17.1",
+ "fast-deep-equal",
+ "fast-uri@2.4.0",
+ "json-schema-ref-resolver",
+ "rfdc"
+ ]
+ },
+ "fast-levenshtein@2.0.6": {
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="
+ },
+ "fast-querystring@1.1.2": {
+ "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==",
+ "dependencies": [
+ "fast-decode-uri-component"
+ ]
+ },
+ "fast-redact@3.5.0": {
+ "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A=="
+ },
+ "fast-safe-stringify@2.1.1": {
+ "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA=="
+ },
+ "fast-uri@2.4.0": {
+ "integrity": "sha512-ypuAmmMKInk5q7XcepxlnUWDLWv4GFtaJqAzWKqn62IpQ3pejtr5dTVbt3vwqVaMKmkNR55sTT+CqUKIaT21BA=="
+ },
+ "fast-uri@3.0.2": {
+ "integrity": "sha512-GR6f0hD7XXyNJa25Tb9BuIdN0tdr+0BMi6/CJPH3wJO1JjNG3n/VsSw38AwRdKZABm8lGbPfakLRkYzx2V9row=="
+ },
+ "fastest-levenshtein@1.0.16": {
+ "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg=="
+ },
+ "fastify-plugin@4.5.1": {
+ "integrity": "sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ=="
+ },
+ "fastify@4.17.0": {
+ "integrity": "sha512-tzuY1tgWJo2Y6qEKwmLhFvACUmr68Io2pqP/sDKU71KRM6A6R3DrCDqLGqANbeLZcKUfdfY58ut35CGqemcTgg==",
+ "dependencies": [
+ "@fastify/ajv-compiler",
+ "@fastify/error",
+ "@fastify/fast-json-stringify-compiler",
+ "abstract-logging",
+ "avvio",
+ "fast-content-type-parse",
+ "fast-json-stringify",
+ "find-my-way",
+ "light-my-request",
+ "pino",
+ "process-warning@2.3.2",
+ "proxy-addr",
+ "rfdc",
+ "secure-json-parse",
+ "semver@7.6.3",
+ "tiny-lru"
+ ]
+ },
+ "fastq@1.17.1": {
+ "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
+ "dependencies": [
+ "reusify"
+ ]
+ },
+ "fd-slicer@1.1.0": {
+ "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
+ "dependencies": [
+ "pend"
+ ]
+ },
+ "fdir@6.3.0": {
+ "integrity": "sha512-QOnuT+BOtivR77wYvCWHfGt9s4Pz1VIMbD463vegT5MLqNXy8rYFT/lPVEqf/bhYeT6qmqrNHhsX+rWwe3rOCQ=="
+ },
+ "fecha@4.2.3": {
+ "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw=="
+ },
+ "fetch-blob@3.2.0": {
+ "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
+ "dependencies": [
+ "node-domexception",
+ "web-streams-polyfill"
+ ]
+ },
+ "fetch-node-website@7.3.0": {
+ "integrity": "sha512-/wayUHbdVUWrD72aqRNNrr6+MHnCkumZgNugN0RfiWJpbNJUdAkMk4Z18MGayGZVVqYXR1RWrV+bIFEt5HuBZg==",
+ "dependencies": [
+ "cli-progress",
+ "colors-option@4.5.0",
+ "figures@5.0.0",
+ "got",
+ "is-plain-obj@4.1.0"
+ ]
+ },
+ "figures@1.7.0": {
+ "integrity": "sha512-UxKlfCRuCBxSXU4C6t9scbDyWZ4VlaFFdojKtzJuSkuOBQ5CNFum+zZXFwHjo+CxBC1t6zlYPgHIgFjL8ggoEQ==",
+ "dependencies": [
+ "escape-string-regexp@1.0.5",
+ "object-assign"
+ ]
+ },
+ "figures@2.0.0": {
+ "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==",
+ "dependencies": [
+ "escape-string-regexp@1.0.5"
+ ]
+ },
+ "figures@3.2.0": {
+ "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
+ "dependencies": [
+ "escape-string-regexp@1.0.5"
+ ]
+ },
+ "figures@4.0.1": {
+ "integrity": "sha512-rElJwkA/xS04Vfg+CaZodpso7VqBknOYbzi6I76hI4X80RUjkSxO2oAyPmGbuXUppywjqndOrQDl817hDnI++w==",
+ "dependencies": [
+ "escape-string-regexp@5.0.0",
+ "is-unicode-supported"
+ ]
+ },
+ "figures@5.0.0": {
+ "integrity": "sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==",
+ "dependencies": [
+ "escape-string-regexp@5.0.0",
+ "is-unicode-supported"
+ ]
+ },
+ "file-entry-cache@6.0.1": {
+ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "dependencies": [
+ "flat-cache"
+ ]
+ },
+ "file-type@18.7.0": {
+ "integrity": "sha512-ihHtXRzXEziMrQ56VSgU7wkxh55iNchFkosu7Y9/S+tXHdKyrGjVK0ujbqNnsxzea+78MaLhN6PGmfYSAv1ACw==",
+ "dependencies": [
+ "readable-web-to-node-stream",
+ "strtok3",
+ "token-types"
+ ]
+ },
+ "file-uri-to-path@1.0.0": {
+ "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
+ },
+ "filename-reserved-regex@3.0.0": {
+ "integrity": "sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw=="
+ },
+ "filenamify@5.1.1": {
+ "integrity": "sha512-M45CbrJLGACfrPOkrTp3j2EcO9OBkKUYME0eiqOCa7i2poaklU0jhlIaMlr8ijLorT0uLAzrn3qXOp5684CkfA==",
+ "dependencies": [
+ "filename-reserved-regex",
+ "strip-outer",
+ "trim-repeated"
+ ]
+ },
+ "fill-range@4.0.0": {
+ "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==",
+ "dependencies": [
+ "extend-shallow@2.0.1",
+ "is-number@3.0.0",
+ "repeat-string",
+ "to-regex-range@2.1.1"
+ ]
+ },
+ "fill-range@7.1.1": {
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dependencies": [
+ "to-regex-range@5.0.1"
+ ]
+ },
+ "filter-obj@3.0.0": {
+ "integrity": "sha512-oQZM+QmVni8MsYzcq9lgTHD/qeLqaG8XaOPOW7dzuSafVxSUlH1+1ZDefj2OD9f2XsmG5lFl2Euc9NI4jgwFWg=="
+ },
+ "filter-obj@5.1.0": {
+ "integrity": "sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng=="
+ },
+ "finalhandler@1.2.0": {
+ "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
+ "dependencies": [
+ "debug@2.6.9",
+ "encodeurl",
+ "escape-html",
+ "on-finished",
+ "parseurl",
+ "statuses@2.0.1",
+ "unpipe"
+ ]
+ },
+ "find-my-way@7.7.0": {
+ "integrity": "sha512-+SrHpvQ52Q6W9f3wJoJBbAQULJuNEEQwBvlvYwACDhBTLOTMiQ0HYWh4+vC3OivGP2ENcTI1oKlFA2OepJNjhQ==",
+ "dependencies": [
+ "fast-deep-equal",
+ "fast-querystring",
+ "safe-regex2"
+ ]
+ },
+ "find-up@5.0.0": {
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dependencies": [
+ "locate-path@6.0.0",
+ "path-exists@4.0.0"
+ ]
+ },
+ "find-up@6.3.0": {
+ "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==",
+ "dependencies": [
+ "locate-path@7.2.0",
+ "path-exists@5.0.0"
+ ]
+ },
+ "flat-cache@3.2.0": {
+ "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
+ "dependencies": [
+ "flatted",
+ "keyv",
+ "rimraf"
+ ]
+ },
+ "flatted@3.3.1": {
+ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw=="
+ },
+ "flush-write-stream@2.0.0": {
+ "integrity": "sha512-uXClqPxT4xW0lcdSBheb2ObVU+kuqUk3Jk64EwieirEXZx9XUrVwp/JuBfKAWaM4T5Td/VL7QLDWPXp/MvGm/g==",
+ "dependencies": [
+ "inherits",
+ "readable-stream@3.6.2"
+ ]
+ },
+ "fn.name@1.1.0": {
+ "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="
+ },
+ "folder-walker@3.2.0": {
+ "integrity": "sha512-VjAQdSLsl6AkpZNyrQJfO7BXLo4chnStqb055bumZMbRUPpVuPN3a4ktsnRCmrFZjtMlYLkyXiR5rAs4WOpC4Q==",
+ "dependencies": [
+ "from2"
+ ]
+ },
+ "follow-redirects@1.15.9": {
+ "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="
+ },
+ "for-each@0.3.3": {
+ "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
+ "dependencies": [
+ "is-callable"
+ ]
+ },
+ "for-in@1.0.2": {
+ "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ=="
+ },
+ "foreground-child@3.3.0": {
+ "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==",
+ "dependencies": [
+ "cross-spawn",
+ "signal-exit@4.1.0"
+ ]
+ },
+ "form-data-encoder@2.1.4": {
+ "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw=="
+ },
+ "form-data@4.0.0": {
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "dependencies": [
+ "asynckit",
+ "combined-stream",
+ "mime-types"
+ ]
+ },
+ "formdata-polyfill@4.0.10": {
+ "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
+ "dependencies": [
+ "fetch-blob"
+ ]
+ },
+ "forwarded@0.2.0": {
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="
+ },
+ "fragment-cache@0.2.1": {
+ "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==",
+ "dependencies": [
+ "map-cache"
+ ]
+ },
+ "fresh@0.5.2": {
+ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="
+ },
+ "from2-array@0.0.4": {
+ "integrity": "sha512-0G0cAp7sYLobH7ALsr835x98PU/YeVF7wlwxdWbCUaea7wsa7lJfKZUAo6p2YZGZ8F94luCuqHZS3JtFER6uPg==",
+ "dependencies": [
+ "from2"
+ ]
+ },
+ "from2@2.3.0": {
+ "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==",
+ "dependencies": [
+ "inherits",
+ "readable-stream@2.3.8"
+ ]
+ },
+ "fs-constants@1.0.0": {
+ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
+ },
+ "fs-minipass@2.1.0": {
+ "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
+ "dependencies": [
+ "minipass@3.3.6"
+ ]
+ },
+ "fs.realpath@1.0.0": {
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
+ },
+ "fsevents@2.3.3": {
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="
+ },
+ "function-bind@1.1.2": {
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="
+ },
+ "function.prototype.name@1.1.6": {
+ "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==",
+ "dependencies": [
+ "call-bind",
+ "define-properties",
+ "es-abstract",
+ "functions-have-names"
+ ]
+ },
+ "functions-have-names@1.2.3": {
+ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="
+ },
+ "fuzzy@0.1.3": {
+ "integrity": "sha512-/gZffu4ykarLrCiP3Ygsa86UAo1E5vEVlvTrpkKywXSbP9Xhln3oSp9QSV57gEq3JFFpGJ4GZ+5zdEp3FcUh4w=="
+ },
+ "gauge@3.0.2": {
+ "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
+ "dependencies": [
+ "aproba",
+ "color-support",
+ "console-control-strings",
+ "has-unicode",
+ "object-assign",
+ "signal-exit@3.0.7",
+ "string-width@4.2.3",
+ "strip-ansi@6.0.1",
+ "wide-align"
+ ]
+ },
+ "get-amd-module-type@5.0.1": {
+ "integrity": "sha512-jb65zDeHyDjFR1loOVk0HQGM5WNwoGB8aLWy3LKCieMKol0/ProHkhO2X1JxojuN10vbz1qNn09MJ7tNp7qMzw==",
+ "dependencies": [
+ "ast-module-types",
+ "node-source-walk"
+ ]
+ },
+ "get-caller-file@2.0.5": {
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
+ },
+ "get-func-name@2.0.2": {
+ "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ=="
+ },
+ "get-intrinsic@1.2.4": {
+ "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
+ "dependencies": [
+ "es-errors",
+ "function-bind",
+ "has-proto",
+ "has-symbols",
+ "hasown"
+ ]
+ },
+ "get-port@5.1.1": {
+ "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ=="
+ },
+ "get-port@6.1.2": {
+ "integrity": "sha512-BrGGraKm2uPqurfGVj/z97/zv8dPleC6x9JBNRTrDNtCkkRF4rPwrQXFgL7+I+q8QSdU4ntLQX2D7KIxSy8nGw=="
+ },
+ "get-stream@5.2.0": {
+ "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+ "dependencies": [
+ "pump@3.0.0"
+ ]
+ },
+ "get-stream@6.0.1": {
+ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="
+ },
+ "get-symbol-description@1.0.2": {
+ "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==",
+ "dependencies": [
+ "call-bind",
+ "es-errors",
+ "get-intrinsic"
+ ]
+ },
+ "get-tsconfig@4.8.1": {
+ "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==",
+ "dependencies": [
+ "resolve-pkg-maps"
+ ]
+ },
+ "get-value@2.0.6": {
+ "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA=="
+ },
+ "gh-release-fetch@4.0.3": {
+ "integrity": "sha512-TOiP1nwLsH5shG85Yt6v6Kjq5JU/44jXyEpbcfPgmj3C829yeXIlx9nAEwQRaxtRF3SJinn2lz7XUkfG9W/U4g==",
+ "dependencies": [
+ "@xhmikosr/downloader",
+ "node-fetch@3.3.2",
+ "semver@7.6.3"
+ ]
+ },
+ "git-repo-info@2.1.1": {
+ "integrity": "sha512-8aCohiDo4jwjOwma4FmYFd3i97urZulL8XL24nIPxuE+GZnfsAyy/g2Shqx6OjUiFKUXZM+Yy+KHnOmmA3FVcg=="
+ },
+ "gitconfiglocal@2.1.0": {
+ "integrity": "sha512-qoerOEliJn3z+Zyn1HW2F6eoYJqKwS6MgC9cztTLUB/xLWX8gD/6T60pKn4+t/d6tP7JlybI7Z3z+I572CR/Vg==",
+ "dependencies": [
+ "ini@1.3.8"
+ ]
+ },
+ "glob-parent@5.1.2": {
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dependencies": [
+ "is-glob"
+ ]
+ },
+ "glob-parent@6.0.2": {
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dependencies": [
+ "is-glob"
+ ]
+ },
+ "glob@10.4.5": {
+ "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
+ "dependencies": [
+ "foreground-child",
+ "jackspeak",
+ "minimatch@9.0.5",
+ "minipass@7.1.2",
+ "package-json-from-dist",
+ "path-scurry"
+ ]
+ },
+ "glob@7.2.3": {
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dependencies": [
+ "fs.realpath",
+ "inflight",
+ "inherits",
+ "minimatch@3.1.2",
+ "once",
+ "path-is-absolute"
+ ]
+ },
+ "glob@8.1.0": {
+ "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
+ "dependencies": [
+ "fs.realpath",
+ "inflight",
+ "inherits",
+ "minimatch@5.1.6",
+ "once"
+ ]
+ },
+ "global-cache-dir@4.4.0": {
+ "integrity": "sha512-bk0gI6IbbphRjAaCJJn5H+T/CcEck5B3a5KBO2BXSDzjFSV+API17w8GA7YPJ6IXJiasW8M0VsEIig1PCHdfOQ==",
+ "dependencies": [
+ "cachedir",
+ "path-exists@5.0.0"
+ ]
+ },
+ "global-dirs@3.0.1": {
+ "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==",
+ "dependencies": [
+ "ini@2.0.0"
+ ]
+ },
+ "globals@13.24.0": {
+ "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+ "dependencies": [
+ "type-fest@0.20.2"
+ ]
+ },
+ "globalthis@1.0.4": {
+ "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
+ "dependencies": [
+ "define-properties",
+ "gopd"
+ ]
+ },
+ "globby@11.1.0": {
+ "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+ "dependencies": [
+ "array-union",
+ "dir-glob",
+ "fast-glob",
+ "ignore",
+ "merge2",
+ "slash@3.0.0"
+ ]
+ },
+ "globby@13.2.2": {
+ "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==",
+ "dependencies": [
+ "dir-glob",
+ "fast-glob",
+ "ignore",
+ "merge2",
+ "slash@4.0.0"
+ ]
+ },
+ "gonzales-pe@4.3.0": {
+ "integrity": "sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ==",
+ "dependencies": [
+ "minimist"
+ ]
+ },
+ "gopd@1.0.1": {
+ "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
+ "dependencies": [
+ "get-intrinsic"
+ ]
+ },
+ "got@12.6.1": {
+ "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==",
+ "dependencies": [
+ "@sindresorhus/is",
+ "@szmarczak/http-timer",
+ "cacheable-lookup",
+ "cacheable-request",
+ "decompress-response",
+ "form-data-encoder",
+ "get-stream@6.0.1",
+ "http2-wrapper",
+ "lowercase-keys",
+ "p-cancelable",
+ "responselike"
+ ]
+ },
+ "graceful-fs@4.2.10": {
+ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
+ },
+ "graceful-fs@4.2.11": {
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
+ },
+ "graphemer@1.4.0": {
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="
+ },
+ "has-ansi@2.0.0": {
+ "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==",
+ "dependencies": [
+ "ansi-regex@2.1.1"
+ ]
+ },
+ "has-bigints@1.0.2": {
+ "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ=="
+ },
+ "has-flag@3.0.0": {
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="
+ },
+ "has-flag@4.0.0": {
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
+ },
+ "has-own-prop@2.0.0": {
+ "integrity": "sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ=="
+ },
+ "has-property-descriptors@1.0.2": {
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "dependencies": [
+ "es-define-property"
+ ]
+ },
+ "has-proto@1.0.3": {
+ "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q=="
+ },
+ "has-symbols@1.0.3": {
+ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
+ },
+ "has-tostringtag@1.0.2": {
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "dependencies": [
+ "has-symbols"
+ ]
+ },
+ "has-unicode@2.0.1": {
+ "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="
+ },
+ "has-value@0.3.1": {
+ "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==",
+ "dependencies": [
+ "get-value",
+ "has-values@0.1.4",
+ "isobject@2.1.0"
+ ]
+ },
+ "has-value@1.0.0": {
+ "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==",
+ "dependencies": [
+ "get-value",
+ "has-values@1.0.0",
+ "isobject@3.0.1"
+ ]
+ },
+ "has-values@0.1.4": {
+ "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ=="
+ },
+ "has-values@1.0.0": {
+ "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==",
+ "dependencies": [
+ "is-number@3.0.0",
+ "kind-of@4.0.0"
+ ]
+ },
+ "has-yarn@3.0.0": {
+ "integrity": "sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA=="
+ },
+ "hasbin@1.2.3": {
+ "integrity": "sha512-CCd8e/w2w28G8DyZvKgiHnQJ/5XXDz6qiUHnthvtag/6T5acUeN5lqq+HMoBqcmgWueWDhiCplrw0Kb1zDACRg==",
+ "dependencies": [
+ "async@1.5.2"
+ ]
+ },
+ "hasha@5.2.2": {
+ "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==",
+ "dependencies": [
+ "is-stream@2.0.1",
+ "type-fest@0.8.1"
+ ]
+ },
+ "hasown@2.0.2": {
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dependencies": [
+ "function-bind"
+ ]
+ },
+ "hexer@1.5.0": {
+ "integrity": "sha512-dyrPC8KzBzUJ19QTIo1gXNqIISRXQ0NwteW6OeQHRN4ZuZeHkdODfj0zHBdOlHbRY8GqbqK57C9oWSvQZizFsg==",
+ "dependencies": [
+ "ansi-color",
+ "minimist",
+ "process@0.10.1",
+ "xtend"
+ ]
+ },
+ "hosted-git-info@4.1.0": {
+ "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==",
+ "dependencies": [
+ "lru-cache@6.0.0"
+ ]
+ },
+ "hot-shots@10.0.0": {
+ "integrity": "sha512-uy/uGpuJk7yuyiKRfZMBNkF1GAOX5O2ifO9rDCaX9jw8fu6eW9QeWC7WRPDI+O98frW1HQgV3+xwjWsZPECIzQ==",
+ "dependencies": [
+ "unix-dgram"
+ ]
+ },
+ "http-cache-semantics@4.1.1": {
+ "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ=="
+ },
+ "http-errors@1.8.1": {
+ "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==",
+ "dependencies": [
+ "depd@1.1.2",
+ "inherits",
+ "setprototypeof",
+ "statuses@1.5.0",
+ "toidentifier"
+ ]
+ },
+ "http-errors@2.0.0": {
+ "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+ "dependencies": [
+ "depd@2.0.0",
+ "inherits",
+ "setprototypeof",
+ "statuses@2.0.1",
+ "toidentifier"
+ ]
+ },
+ "http-proxy-middleware@2.0.6": {
+ "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==",
+ "dependencies": [
+ "@types/http-proxy",
+ "http-proxy",
+ "is-glob",
+ "is-plain-obj@3.0.0",
+ "micromatch@4.0.8"
+ ]
+ },
+ "http-proxy@1.18.1": {
+ "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
+ "dependencies": [
+ "eventemitter3",
+ "follow-redirects",
+ "requires-port"
+ ]
+ },
+ "http2-wrapper@2.2.1": {
+ "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==",
+ "dependencies": [
+ "quick-lru",
+ "resolve-alpn"
+ ]
+ },
+ "https-proxy-agent@5.0.1": {
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "dependencies": [
+ "agent-base",
+ "debug@4.3.7"
+ ]
+ },
+ "human-signals@2.1.0": {
+ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="
+ },
+ "human-signals@3.0.1": {
+ "integrity": "sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ=="
+ },
+ "husky@8.0.3": {
+ "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg=="
+ },
+ "iconv-lite@0.4.24": {
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dependencies": [
+ "safer-buffer"
+ ]
+ },
+ "ieee754@1.2.1": {
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
+ },
+ "ignore@5.3.2": {
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="
+ },
+ "import-fresh@3.3.0": {
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dependencies": [
+ "parent-module",
+ "resolve-from@4.0.0"
+ ]
+ },
+ "import-lazy@4.0.0": {
+ "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw=="
+ },
+ "imurmurhash@0.1.4": {
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="
+ },
+ "indent-string@3.2.0": {
+ "integrity": "sha512-BYqTHXTGUIvg7t1r4sJNKcbDZkL92nkXA8YtRpbjFHRHGDL/NtUeiBJMeE60kIFN/Mg8ESaWQvftaYMGJzQZCQ=="
+ },
+ "indent-string@5.0.0": {
+ "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg=="
+ },
+ "inflight@1.0.6": {
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "dependencies": [
+ "once",
+ "wrappy"
+ ]
+ },
+ "inherits@2.0.4": {
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "ini@1.3.8": {
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
+ },
+ "ini@2.0.0": {
+ "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA=="
+ },
+ "inquirer-autocomplete-prompt@1.4.0_inquirer@6.5.2": {
+ "integrity": "sha512-qHgHyJmbULt4hI+kCmwX92MnSxDs/Yhdt4wPA30qnoa01OF6uTXV8yvH4hKXgdaTNmkZ9D01MHjqKYEuJN+ONw==",
+ "dependencies": [
+ "ansi-escapes@4.3.2",
+ "chalk@4.1.2",
+ "figures@3.2.0",
+ "inquirer",
+ "run-async",
+ "rxjs"
+ ]
+ },
+ "inquirer@6.5.2": {
+ "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==",
+ "dependencies": [
+ "ansi-escapes@3.2.0",
+ "chalk@2.4.2",
+ "cli-cursor@2.1.0",
+ "cli-width",
+ "external-editor",
+ "figures@2.0.0",
+ "lodash",
+ "mute-stream",
+ "run-async",
+ "rxjs",
+ "string-width@2.1.1",
+ "strip-ansi@5.2.0",
+ "through"
+ ]
+ },
+ "inspect-with-kind@1.0.5": {
+ "integrity": "sha512-MAQUJuIo7Xqk8EVNP+6d3CKq9c80hi4tjIbIAT6lmGW9W6WzlHiu9PS8uSuUYU+Do+j1baiFp3H25XEVxDIG2g==",
+ "dependencies": [
+ "kind-of@6.0.3"
+ ]
+ },
+ "internal-slot@1.0.7": {
+ "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==",
+ "dependencies": [
+ "es-errors",
+ "hasown",
+ "side-channel"
+ ]
+ },
+ "ipaddr.js@1.9.1": {
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
+ },
+ "is-accessor-descriptor@1.0.1": {
+ "integrity": "sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==",
+ "dependencies": [
+ "hasown"
+ ]
+ },
+ "is-arguments@1.1.1": {
+ "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
+ "dependencies": [
+ "call-bind",
+ "has-tostringtag"
+ ]
+ },
+ "is-array-buffer@3.0.4": {
+ "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==",
+ "dependencies": [
+ "call-bind",
+ "get-intrinsic"
+ ]
+ },
+ "is-arrayish@0.2.1": {
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
+ },
+ "is-arrayish@0.3.2": {
+ "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
+ },
+ "is-async-function@2.0.0": {
+ "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==",
+ "dependencies": [
+ "has-tostringtag"
+ ]
+ },
+ "is-bigint@1.0.4": {
+ "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
+ "dependencies": [
+ "has-bigints"
+ ]
+ },
+ "is-binary-path@2.1.0": {
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dependencies": [
+ "binary-extensions"
+ ]
+ },
+ "is-boolean-object@1.1.2": {
+ "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==",
+ "dependencies": [
+ "call-bind",
+ "has-tostringtag"
+ ]
+ },
+ "is-buffer@1.1.6": {
+ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+ },
+ "is-builtin-module@3.2.1": {
+ "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==",
+ "dependencies": [
+ "builtin-modules"
+ ]
+ },
+ "is-callable@1.2.7": {
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="
+ },
+ "is-ci@3.0.1": {
+ "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==",
+ "dependencies": [
+ "ci-info"
+ ]
+ },
+ "is-core-module@2.15.1": {
+ "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==",
+ "dependencies": [
+ "hasown"
+ ]
+ },
+ "is-data-descriptor@1.0.1": {
+ "integrity": "sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw==",
+ "dependencies": [
+ "hasown"
+ ]
+ },
+ "is-data-view@1.0.1": {
+ "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==",
+ "dependencies": [
+ "is-typed-array"
+ ]
+ },
+ "is-date-object@1.0.5": {
+ "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
+ "dependencies": [
+ "has-tostringtag"
+ ]
+ },
+ "is-descriptor@0.1.7": {
+ "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==",
+ "dependencies": [
+ "is-accessor-descriptor",
+ "is-data-descriptor"
+ ]
+ },
+ "is-descriptor@1.0.3": {
+ "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==",
+ "dependencies": [
+ "is-accessor-descriptor",
+ "is-data-descriptor"
+ ]
+ },
+ "is-docker@2.2.1": {
+ "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="
+ },
+ "is-docker@3.0.0": {
+ "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="
+ },
+ "is-extendable@0.1.1": {
+ "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="
+ },
+ "is-extendable@1.0.1": {
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dependencies": [
+ "is-plain-object@2.0.4"
+ ]
+ },
+ "is-extglob@2.1.1": {
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="
+ },
+ "is-finalizationregistry@1.0.2": {
+ "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==",
+ "dependencies": [
+ "call-bind"
+ ]
+ },
+ "is-fullwidth-code-point@1.0.0": {
+ "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==",
+ "dependencies": [
+ "number-is-nan"
+ ]
+ },
+ "is-fullwidth-code-point@2.0.0": {
+ "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w=="
+ },
+ "is-fullwidth-code-point@3.0.0": {
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
+ },
+ "is-fullwidth-code-point@4.0.0": {
+ "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="
+ },
+ "is-generator-function@1.0.10": {
+ "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==",
+ "dependencies": [
+ "has-tostringtag"
+ ]
+ },
+ "is-glob@4.0.3": {
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dependencies": [
+ "is-extglob"
+ ]
+ },
+ "is-installed-globally@0.4.0": {
+ "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==",
+ "dependencies": [
+ "global-dirs",
+ "is-path-inside@3.0.3"
+ ]
+ },
+ "is-interactive@2.0.0": {
+ "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ=="
+ },
+ "is-map@2.0.3": {
+ "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw=="
+ },
+ "is-negative-zero@2.0.3": {
+ "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw=="
+ },
+ "is-npm@6.0.0": {
+ "integrity": "sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ=="
+ },
+ "is-number-object@1.0.7": {
+ "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==",
+ "dependencies": [
+ "has-tostringtag"
+ ]
+ },
+ "is-number@3.0.0": {
+ "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==",
+ "dependencies": [
+ "kind-of@3.2.2"
+ ]
+ },
+ "is-number@7.0.0": {
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
+ },
+ "is-obj@2.0.0": {
+ "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w=="
+ },
+ "is-observable@1.1.0": {
+ "integrity": "sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA==",
+ "dependencies": [
+ "symbol-observable"
+ ]
+ },
+ "is-path-inside@3.0.3": {
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ=="
+ },
+ "is-path-inside@4.0.0": {
+ "integrity": "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA=="
+ },
+ "is-plain-obj@1.1.0": {
+ "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg=="
+ },
+ "is-plain-obj@2.1.0": {
+ "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA=="
+ },
+ "is-plain-obj@3.0.0": {
+ "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA=="
+ },
+ "is-plain-obj@4.1.0": {
+ "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="
+ },
+ "is-plain-object@2.0.4": {
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dependencies": [
+ "isobject@3.0.1"
+ ]
+ },
+ "is-plain-object@5.0.0": {
+ "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="
+ },
+ "is-promise@2.2.2": {
+ "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ=="
+ },
+ "is-regex@1.1.4": {
+ "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
+ "dependencies": [
+ "call-bind",
+ "has-tostringtag"
+ ]
+ },
+ "is-set@2.0.3": {
+ "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg=="
+ },
+ "is-shared-array-buffer@1.0.3": {
+ "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==",
+ "dependencies": [
+ "call-bind"
+ ]
+ },
+ "is-stream@1.1.0": {
+ "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ=="
+ },
+ "is-stream@2.0.1": {
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="
+ },
+ "is-stream@3.0.0": {
+ "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA=="
+ },
+ "is-string@1.0.7": {
+ "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
+ "dependencies": [
+ "has-tostringtag"
+ ]
+ },
+ "is-symbol@1.0.4": {
+ "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==",
+ "dependencies": [
+ "has-symbols"
+ ]
+ },
+ "is-typed-array@1.1.13": {
+ "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==",
+ "dependencies": [
+ "which-typed-array"
+ ]
+ },
+ "is-typedarray@1.0.0": {
+ "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA=="
+ },
+ "is-unicode-supported@1.3.0": {
+ "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ=="
+ },
+ "is-url-superb@4.0.0": {
+ "integrity": "sha512-GI+WjezhPPcbM+tqE9LnmsY5qqjwHzTvjJ36wxYX5ujNXefSUJ/T17r5bqDV8yLhcgB59KTPNOc9O9cmHTPWsA=="
+ },
+ "is-url@1.2.4": {
+ "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww=="
+ },
+ "is-weakmap@2.0.2": {
+ "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w=="
+ },
+ "is-weakref@1.0.2": {
+ "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==",
+ "dependencies": [
+ "call-bind"
+ ]
+ },
+ "is-weakset@2.0.3": {
+ "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==",
+ "dependencies": [
+ "call-bind",
+ "get-intrinsic"
+ ]
+ },
+ "is-windows@1.0.2": {
+ "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA=="
+ },
+ "is-wsl@2.2.0": {
+ "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
+ "dependencies": [
+ "is-docker@2.2.1"
+ ]
+ },
+ "is-yarn-global@0.4.1": {
+ "integrity": "sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ=="
+ },
+ "isarray@1.0.0": {
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
+ },
+ "isarray@2.0.5": {
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="
+ },
+ "iserror@0.0.2": {
+ "integrity": "sha512-oKGGrFVaWwETimP3SiWwjDeY27ovZoyZPHtxblC4hCq9fXxed/jasx+ATWFFjCVSRZng8VTMsN1nDnGo6zMBSw=="
+ },
+ "isexe@2.0.0": {
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
+ },
+ "isobject@2.1.0": {
+ "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==",
+ "dependencies": [
+ "isarray@1.0.0"
+ ]
+ },
+ "isobject@3.0.1": {
+ "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg=="
+ },
+ "iterator.prototype@1.1.2": {
+ "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==",
+ "dependencies": [
+ "define-properties",
+ "get-intrinsic",
+ "has-symbols",
+ "reflect.getprototypeof",
+ "set-function-name"
+ ]
+ },
+ "jackspeak@3.4.3": {
+ "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+ "dependencies": [
+ "@isaacs/cliui",
+ "@pkgjs/parseargs"
+ ]
+ },
+ "jaeger-client@3.19.0": {
+ "integrity": "sha512-M0c7cKHmdyEUtjemnJyx/y9uX16XHocL46yQvyqDlPdvAcwPDbHrIbKjQdBqtiE4apQ/9dmr+ZLJYYPGnurgpw==",
+ "dependencies": [
+ "node-int64",
+ "opentracing",
+ "thriftrw",
+ "uuid@8.3.2",
+ "xorshift"
+ ]
+ },
+ "jest-get-type@27.5.1": {
+ "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw=="
+ },
+ "jest-validate@27.5.1": {
+ "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==",
+ "dependencies": [
+ "@jest/types",
+ "camelcase@6.3.0",
+ "chalk@4.1.2",
+ "jest-get-type",
+ "leven",
+ "pretty-format"
+ ]
+ },
+ "js-string-escape@1.0.1": {
+ "integrity": "sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg=="
+ },
+ "js-tokens@4.0.0": {
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
+ },
+ "js-yaml@4.1.0": {
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dependencies": [
+ "argparse"
+ ]
+ },
+ "json-buffer@3.0.1": {
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
+ },
+ "json-parse-even-better-errors@2.3.1": {
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
+ },
+ "json-schema-ref-resolver@1.0.1": {
+ "integrity": "sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==",
+ "dependencies": [
+ "fast-deep-equal"
+ ]
+ },
+ "json-schema-traverse@0.4.1": {
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
+ },
+ "json-schema-traverse@1.0.0": {
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
+ },
+ "json-stable-stringify-without-jsonify@1.0.1": {
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="
+ },
+ "json5@1.0.2": {
+ "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
+ "dependencies": [
+ "minimist"
+ ]
+ },
+ "jsonc-parser@3.3.1": {
+ "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="
+ },
+ "jsonpointer@5.0.1": {
+ "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ=="
+ },
+ "jsonwebtoken@9.0.1": {
+ "integrity": "sha512-K8wx7eJ5TPvEjuiVSkv167EVboBDv9PZdDoF7BgeQnBLVvZWW9clr2PsQHVJDTKaEIH5JBIwHujGcHp7GgI2eg==",
+ "dependencies": [
+ "jws",
+ "lodash",
+ "ms@2.1.3",
+ "semver@7.6.3"
+ ]
+ },
+ "jsx-ast-utils@3.3.5": {
+ "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==",
+ "dependencies": [
+ "array-includes",
+ "array.prototype.flat",
+ "object.assign",
+ "object.values"
+ ]
+ },
+ "junk@4.0.1": {
+ "integrity": "sha512-Qush0uP+G8ZScpGMZvHUiRfI0YBWuB3gVBYlI0v0vvOJt5FLicco+IkP0a50LqTTQhmts/m6tP5SWE+USyIvcQ=="
+ },
+ "jwa@1.4.1": {
+ "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
+ "dependencies": [
+ "buffer-equal-constant-time",
+ "ecdsa-sig-formatter",
+ "safe-buffer@5.2.1"
+ ]
+ },
+ "jws@3.2.2": {
+ "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+ "dependencies": [
+ "jwa",
+ "safe-buffer@5.2.1"
+ ]
+ },
+ "jwt-decode@3.1.2": {
+ "integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A=="
+ },
+ "keep-func-props@4.0.1": {
+ "integrity": "sha512-87ftOIICfdww3SxR5P1veq3ThBNyRPG0JGL//oaR08v0k2yTicEIHd7s0GqSJfQvlb+ybC3GiDepOweo0LDhvw==",
+ "dependencies": [
+ "mimic-fn@4.0.0"
+ ]
+ },
+ "keyv@4.5.4": {
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dependencies": [
+ "json-buffer"
+ ]
+ },
+ "kind-of@3.2.2": {
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dependencies": [
+ "is-buffer"
+ ]
+ },
+ "kind-of@4.0.0": {
+ "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==",
+ "dependencies": [
+ "is-buffer"
+ ]
+ },
+ "kind-of@6.0.3": {
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="
+ },
+ "kuler@2.0.0": {
+ "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A=="
+ },
+ "lambda-local@2.1.2": {
+ "integrity": "sha512-nGTJn2JxZWcLGpNwXFmXC7UEXL7QCLieQWDiXs46vIv9y/gSPm/uHygEMCaym+HIziniAw0XIm+1VTrXCvG1Zw==",
+ "dependencies": [
+ "commander@10.0.1",
+ "dotenv@16.4.5",
+ "winston@3.14.2"
+ ]
+ },
+ "language-subtag-registry@0.3.23": {
+ "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ=="
+ },
+ "language-tags@1.0.9": {
+ "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==",
+ "dependencies": [
+ "language-subtag-registry"
+ ]
+ },
+ "latest-version@7.0.0": {
+ "integrity": "sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==",
+ "dependencies": [
+ "package-json"
+ ]
+ },
+ "lazystream@1.0.1": {
+ "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==",
+ "dependencies": [
+ "readable-stream@2.3.8"
+ ]
+ },
+ "leven@3.1.0": {
+ "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A=="
+ },
+ "levn@0.4.1": {
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dependencies": [
+ "prelude-ls",
+ "type-check"
+ ]
+ },
+ "light-my-request@5.13.0": {
+ "integrity": "sha512-9IjUN9ZyCS9pTG+KqTDEQo68Sui2lHsYBrfMyVUTTZ3XhH8PMZq7xO94Kr+eP9dhi/kcKsx4N41p2IXEBil1pQ==",
+ "dependencies": [
+ "cookie@0.6.0",
+ "process-warning@3.0.0",
+ "set-cookie-parser"
+ ]
+ },
+ "lines-and-columns@1.2.4": {
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
+ },
+ "listr-silent-renderer@1.1.1": {
+ "integrity": "sha512-L26cIFm7/oZeSNVhWB6faeorXhMg4HNlb/dS/7jHhr708jxlXrtrBWo4YUxZQkc6dGoxEAe6J/D3juTRBUzjtA=="
+ },
+ "listr-update-renderer@0.5.0_listr@0.14.3": {
+ "integrity": "sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA==",
+ "dependencies": [
+ "chalk@1.1.3",
+ "cli-truncate",
+ "elegant-spinner",
+ "figures@1.7.0",
+ "indent-string@3.2.0",
+ "listr",
+ "log-symbols@1.0.2",
+ "log-update@2.3.0",
+ "strip-ansi@3.0.1"
+ ]
+ },
+ "listr-verbose-renderer@0.5.0": {
+ "integrity": "sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw==",
+ "dependencies": [
+ "chalk@2.4.2",
+ "cli-cursor@2.1.0",
+ "date-fns",
+ "figures@2.0.0"
+ ]
+ },
+ "listr@0.14.3": {
+ "integrity": "sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA==",
+ "dependencies": [
+ "@samverschueren/stream-to-observable",
+ "is-observable",
+ "is-promise",
+ "is-stream@1.1.0",
+ "listr-silent-renderer",
+ "listr-update-renderer",
+ "listr-verbose-renderer",
+ "p-map@2.1.0",
+ "rxjs"
+ ]
+ },
+ "locate-path@6.0.0": {
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dependencies": [
+ "p-locate@5.0.0"
+ ]
+ },
+ "locate-path@7.2.0": {
+ "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==",
+ "dependencies": [
+ "p-locate@6.0.0"
+ ]
+ },
+ "lodash-es@4.17.21": {
+ "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
+ },
+ "lodash.camelcase@4.3.0": {
+ "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="
+ },
+ "lodash.defaults@4.2.0": {
+ "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="
+ },
+ "lodash.difference@4.5.0": {
+ "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA=="
+ },
+ "lodash.flatten@4.4.0": {
+ "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g=="
+ },
+ "lodash.isempty@4.4.0": {
+ "integrity": "sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg=="
+ },
+ "lodash.isplainobject@4.0.6": {
+ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
+ },
+ "lodash.merge@4.6.2": {
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
+ },
+ "lodash.transform@4.6.0": {
+ "integrity": "sha512-LO37ZnhmBVx0GvOU/caQuipEh4GN82TcWv3yHlebGDgOxbxiwwzW5Pcx2AcvpIv2WmvmSMoC492yQFNhy/l/UQ=="
+ },
+ "lodash.union@4.6.0": {
+ "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw=="
+ },
+ "lodash@4.17.21": {
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+ },
+ "log-process-errors@8.0.0": {
+ "integrity": "sha512-+SNGqNC1gCMJfhwYzAHr/YgNT/ZJc+V2nCkvtPnjrENMeCe+B/jgShBW0lmWoh6uVV2edFAPc/IUOkDdsjTbTg==",
+ "dependencies": [
+ "colors-option@3.0.0",
+ "figures@4.0.1",
+ "filter-obj@3.0.0",
+ "jest-validate",
+ "map-obj",
+ "moize",
+ "semver@7.6.3"
+ ]
+ },
+ "log-symbols@1.0.2": {
+ "integrity": "sha512-mmPrW0Fh2fxOzdBbFv4g1m6pR72haFLPJ2G5SJEELf1y+iaQrDG6cWCPjy54RHYbZAt7X+ls690Kw62AdWXBzQ==",
+ "dependencies": [
+ "chalk@1.1.3"
+ ]
+ },
+ "log-symbols@5.1.0": {
+ "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==",
+ "dependencies": [
+ "chalk@5.2.0",
+ "is-unicode-supported"
+ ]
+ },
+ "log-update@2.3.0": {
+ "integrity": "sha512-vlP11XfFGyeNQlmEn9tJ66rEW1coA/79m5z6BCkudjbAGE83uhAcGYrBFwfs3AdLiLzGRusRPAbSPK9xZteCmg==",
+ "dependencies": [
+ "ansi-escapes@3.2.0",
+ "cli-cursor@2.1.0",
+ "wrap-ansi@3.0.1"
+ ]
+ },
+ "log-update@5.0.1": {
+ "integrity": "sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw==",
+ "dependencies": [
+ "ansi-escapes@5.0.0",
+ "cli-cursor@4.0.0",
+ "slice-ansi@5.0.0",
+ "strip-ansi@7.1.0",
+ "wrap-ansi@8.1.0"
+ ]
+ },
+ "logform@2.6.1": {
+ "integrity": "sha512-CdaO738xRapbKIMVn2m4F6KTj4j7ooJ8POVnebSgKo3KBz5axNXRAL7ZdRjIV6NOr2Uf4vjtRkxrFETOioCqSA==",
+ "dependencies": [
+ "@colors/colors@1.6.0",
+ "@types/triple-beam",
+ "fecha",
+ "ms@2.1.3",
+ "safe-stable-stringify",
+ "triple-beam"
+ ]
+ },
+ "long@2.4.0": {
+ "integrity": "sha512-ijUtjmO/n2A5PaosNG9ZGDsQ3vxJg7ZW8vsY8Kp0f2yIZWhSJvjmegV7t+9RPQKxKrvj8yKGehhS+po14hPLGQ=="
+ },
+ "long@5.2.3": {
+ "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q=="
+ },
+ "loose-envify@1.4.0": {
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "dependencies": [
+ "js-tokens"
+ ]
+ },
+ "loupe@3.1.1": {
+ "integrity": "sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==",
+ "dependencies": [
+ "get-func-name"
+ ]
+ },
+ "lowercase-keys@3.0.0": {
+ "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ=="
+ },
+ "lru-cache@10.4.3": {
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="
+ },
+ "lru-cache@6.0.0": {
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dependencies": [
+ "yallist"
+ ]
+ },
+ "lunr@2.3.9": {
+ "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow=="
+ },
+ "luxon@3.5.0": {
+ "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ=="
+ },
+ "macos-release@3.3.0": {
+ "integrity": "sha512-tPJQ1HeyiU2vRruNGhZ+VleWuMQRro8iFtJxYgnS4NQe+EukKF6aGiIT+7flZhISAt2iaXBCfFGvAyif7/f8nQ=="
+ },
+ "magic-string@0.30.11": {
+ "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==",
+ "dependencies": [
+ "@jridgewell/sourcemap-codec"
+ ]
+ },
+ "make-dir@3.1.0": {
+ "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+ "dependencies": [
+ "semver@6.3.1"
+ ]
+ },
+ "make-dir@4.0.0": {
+ "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==",
+ "dependencies": [
+ "semver@7.6.3"
+ ]
+ },
+ "make-error@1.3.6": {
+ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="
+ },
+ "map-cache@0.2.2": {
+ "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg=="
+ },
+ "map-obj@5.0.2": {
+ "integrity": "sha512-K6K2NgKnTXimT3779/4KxSvobxOtMmx1LBZ3NwRxT/MDIR3Br/fQ4Q+WCX5QxjyUR8zg5+RV9Tbf2c5pAWTD2A=="
+ },
+ "map-visit@1.0.0": {
+ "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==",
+ "dependencies": [
+ "object-visit"
+ ]
+ },
+ "marked@4.3.0": {
+ "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A=="
+ },
+ "maxstache-stream@1.0.4": {
+ "integrity": "sha512-v8qlfPN0pSp7bdSoLo1NTjG43GXGqk5W2NWFnOCq2GlmFFqebGzPCjLKSbShuqIOVorOtZSAy7O/S1OCCRONUw==",
+ "dependencies": [
+ "maxstache",
+ "pump@1.0.3",
+ "split2@1.1.1",
+ "through2"
+ ]
+ },
+ "maxstache@1.0.7": {
+ "integrity": "sha512-53ZBxHrZM+W//5AcRVewiLpDunHnucfdzZUGz54Fnvo4tE+J3p8EL66kBrs2UhBXvYKTWckWYYWBqJqoTcenqg=="
+ },
+ "md5-hex@3.0.1": {
+ "integrity": "sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==",
+ "dependencies": [
+ "blueimp-md5"
+ ]
+ },
+ "mdast-util-from-markdown@2.0.1": {
+ "integrity": "sha512-aJEUyzZ6TzlsX2s5B4Of7lN7EQtAxvtradMMglCQDyaTFgse6CmtmdJ15ElnVRlCg1vpNyVtbem0PWzlNieZsA==",
+ "dependencies": [
+ "@types/mdast",
+ "@types/unist",
+ "decode-named-character-reference",
+ "devlop",
+ "mdast-util-to-string",
+ "micromark",
+ "micromark-util-decode-numeric-character-reference",
+ "micromark-util-decode-string",
+ "micromark-util-normalize-identifier",
+ "micromark-util-symbol",
+ "micromark-util-types",
+ "unist-util-stringify-position"
+ ]
+ },
+ "mdast-util-to-string@4.0.0": {
+ "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==",
+ "dependencies": [
+ "@types/mdast"
+ ]
+ },
+ "media-typer@0.3.0": {
+ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="
+ },
+ "memoize-one@6.0.0": {
+ "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="
+ },
+ "merge-descriptors@1.0.1": {
+ "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
+ },
+ "merge-options@3.0.4": {
+ "integrity": "sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==",
+ "dependencies": [
+ "is-plain-obj@2.1.0"
+ ]
+ },
+ "merge-stream@2.0.0": {
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="
+ },
+ "merge2@1.4.1": {
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="
+ },
+ "methods@1.1.2": {
+ "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="
+ },
+ "micro-api-client@3.3.0": {
+ "integrity": "sha512-y0y6CUB9RLVsy3kfgayU28746QrNMpSm9O/AYGNsBgOkJr/X/Jk0VLGoO8Ude7Bpa8adywzF+MzXNZRFRsNPhg=="
+ },
+ "micro-memoize@4.1.2": {
+ "integrity": "sha512-+HzcV2H+rbSJzApgkj0NdTakkC+bnyeiUxgT6/m7mjcz1CmM22KYFKp+EVj1sWe4UYcnriJr5uqHQD/gMHLD+g=="
+ },
+ "micromark-core-commonmark@2.0.1": {
+ "integrity": "sha512-CUQyKr1e///ZODyD1U3xit6zXwy1a8q2a1S1HKtIlmgvurrEpaw/Y9y6KSIbF8P59cn/NjzHyO+Q2fAyYLQrAA==",
+ "dependencies": [
+ "decode-named-character-reference",
+ "devlop",
+ "micromark-factory-destination",
+ "micromark-factory-label",
+ "micromark-factory-space",
+ "micromark-factory-title",
+ "micromark-factory-whitespace",
+ "micromark-util-character",
+ "micromark-util-chunked",
+ "micromark-util-classify-character",
+ "micromark-util-html-tag-name",
+ "micromark-util-normalize-identifier",
+ "micromark-util-resolve-all",
+ "micromark-util-subtokenize",
+ "micromark-util-symbol",
+ "micromark-util-types"
+ ]
+ },
+ "micromark-factory-destination@2.0.0": {
+ "integrity": "sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==",
+ "dependencies": [
+ "micromark-util-character",
+ "micromark-util-symbol",
+ "micromark-util-types"
+ ]
+ },
+ "micromark-factory-label@2.0.0": {
+ "integrity": "sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==",
+ "dependencies": [
+ "devlop",
+ "micromark-util-character",
+ "micromark-util-symbol",
+ "micromark-util-types"
+ ]
+ },
+ "micromark-factory-space@2.0.0": {
+ "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==",
+ "dependencies": [
+ "micromark-util-character",
+ "micromark-util-types"
+ ]
+ },
+ "micromark-factory-title@2.0.0": {
+ "integrity": "sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==",
+ "dependencies": [
+ "micromark-factory-space",
+ "micromark-util-character",
+ "micromark-util-symbol",
+ "micromark-util-types"
+ ]
+ },
+ "micromark-factory-whitespace@2.0.0": {
+ "integrity": "sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==",
+ "dependencies": [
+ "micromark-factory-space",
+ "micromark-util-character",
+ "micromark-util-symbol",
+ "micromark-util-types"
+ ]
+ },
+ "micromark-util-character@2.1.0": {
+ "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==",
+ "dependencies": [
+ "micromark-util-symbol",
+ "micromark-util-types"
+ ]
+ },
+ "micromark-util-chunked@2.0.0": {
+ "integrity": "sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==",
+ "dependencies": [
+ "micromark-util-symbol"
+ ]
+ },
+ "micromark-util-classify-character@2.0.0": {
+ "integrity": "sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==",
+ "dependencies": [
+ "micromark-util-character",
+ "micromark-util-symbol",
+ "micromark-util-types"
+ ]
+ },
+ "micromark-util-combine-extensions@2.0.0": {
+ "integrity": "sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==",
+ "dependencies": [
+ "micromark-util-chunked",
+ "micromark-util-types"
+ ]
+ },
+ "micromark-util-decode-numeric-character-reference@2.0.1": {
+ "integrity": "sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==",
+ "dependencies": [
+ "micromark-util-symbol"
+ ]
+ },
+ "micromark-util-decode-string@2.0.0": {
+ "integrity": "sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==",
+ "dependencies": [
+ "decode-named-character-reference",
+ "micromark-util-character",
+ "micromark-util-decode-numeric-character-reference",
+ "micromark-util-symbol"
+ ]
+ },
+ "micromark-util-encode@2.0.0": {
+ "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA=="
+ },
+ "micromark-util-html-tag-name@2.0.0": {
+ "integrity": "sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw=="
+ },
+ "micromark-util-normalize-identifier@2.0.0": {
+ "integrity": "sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==",
+ "dependencies": [
+ "micromark-util-symbol"
+ ]
+ },
+ "micromark-util-resolve-all@2.0.0": {
+ "integrity": "sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==",
+ "dependencies": [
+ "micromark-util-types"
+ ]
+ },
+ "micromark-util-sanitize-uri@2.0.0": {
+ "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==",
+ "dependencies": [
+ "micromark-util-character",
+ "micromark-util-encode",
+ "micromark-util-symbol"
+ ]
+ },
+ "micromark-util-subtokenize@2.0.1": {
+ "integrity": "sha512-jZNtiFl/1aY73yS3UGQkutD0UbhTt68qnRpw2Pifmz5wV9h8gOVsN70v+Lq/f1rKaU/W8pxRe8y8Q9FX1AOe1Q==",
+ "dependencies": [
+ "devlop",
+ "micromark-util-chunked",
+ "micromark-util-symbol",
+ "micromark-util-types"
+ ]
+ },
+ "micromark-util-symbol@2.0.0": {
+ "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw=="
+ },
+ "micromark-util-types@2.0.0": {
+ "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w=="
+ },
+ "micromark@4.0.0": {
+ "integrity": "sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==",
+ "dependencies": [
+ "@types/debug",
+ "debug@4.3.7",
+ "decode-named-character-reference",
+ "devlop",
+ "micromark-core-commonmark",
+ "micromark-factory-space",
+ "micromark-util-character",
+ "micromark-util-chunked",
+ "micromark-util-combine-extensions",
+ "micromark-util-decode-numeric-character-reference",
+ "micromark-util-encode",
+ "micromark-util-normalize-identifier",
+ "micromark-util-resolve-all",
+ "micromark-util-sanitize-uri",
+ "micromark-util-subtokenize",
+ "micromark-util-symbol",
+ "micromark-util-types"
+ ]
+ },
+ "micromatch@3.1.10": {
+ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+ "dependencies": [
+ "arr-diff",
+ "array-unique",
+ "braces@2.3.2",
+ "define-property@2.0.2",
+ "extend-shallow@3.0.2",
+ "extglob",
+ "fragment-cache",
+ "kind-of@6.0.3",
+ "nanomatch",
+ "object.pick",
+ "regex-not",
+ "snapdragon",
+ "to-regex"
+ ]
+ },
+ "micromatch@4.0.8": {
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "dependencies": [
+ "braces@3.0.3",
+ "picomatch"
+ ]
+ },
+ "mime-db@1.52.0": {
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
+ },
+ "mime-types@2.1.35": {
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dependencies": [
+ "mime-db"
+ ]
+ },
+ "mime@1.6.0": {
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
+ },
+ "mime@3.0.0": {
+ "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A=="
+ },
+ "mimic-fn@1.2.0": {
+ "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="
+ },
+ "mimic-fn@2.1.0": {
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="
+ },
+ "mimic-fn@4.0.0": {
+ "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw=="
+ },
+ "mimic-response@3.1.0": {
+ "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="
+ },
+ "mimic-response@4.0.0": {
+ "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg=="
+ },
+ "minimatch@3.1.2": {
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dependencies": [
+ "brace-expansion@1.1.11"
+ ]
+ },
+ "minimatch@5.1.6": {
+ "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+ "dependencies": [
+ "brace-expansion@2.0.1"
+ ]
+ },
+ "minimatch@9.0.5": {
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dependencies": [
+ "brace-expansion@2.0.1"
+ ]
+ },
+ "minimist@1.2.8": {
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="
+ },
+ "minipass@3.3.6": {
+ "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+ "dependencies": [
+ "yallist"
+ ]
+ },
+ "minipass@5.0.0": {
+ "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ=="
+ },
+ "minipass@7.1.2": {
+ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="
+ },
+ "minizlib@2.1.2": {
+ "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
+ "dependencies": [
+ "minipass@3.3.6",
+ "yallist"
+ ]
+ },
+ "mixin-deep@1.3.2": {
+ "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
+ "dependencies": [
+ "for-in",
+ "is-extendable@1.0.1"
+ ]
+ },
+ "mkdirp@0.5.6": {
+ "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+ "dependencies": [
+ "minimist"
+ ]
+ },
+ "mkdirp@1.0.4": {
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
+ },
+ "module-definition@5.0.1": {
+ "integrity": "sha512-kvw3B4G19IXk+BOXnYq/D/VeO9qfHaapMeuS7w7sNUqmGaA6hywdFHMi+VWeR9wUScXM7XjoryTffCZ5B0/8IA==",
+ "dependencies": [
+ "ast-module-types",
+ "node-source-walk"
+ ]
+ },
+ "module-details-from-path@1.0.3": {
+ "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A=="
+ },
+ "moize@6.1.6": {
+ "integrity": "sha512-vSKdIUO61iCmTqhdoIDrqyrtp87nWZUmBPniNjO0fX49wEYmyDO4lvlnFXiGcaH1JLE/s/9HbiK4LSHsbiUY6Q==",
+ "dependencies": [
+ "fast-equals",
+ "micro-memoize"
+ ]
+ },
+ "move-file@3.1.0": {
+ "integrity": "sha512-4aE3U7CCBWgrQlQDMq8da4woBWDGHioJFiOZ8Ie6Yq2uwYQ9V2kGhTz4x3u6Wc+OU17nw0yc3rJ/lQ4jIiPe3A==",
+ "dependencies": [
+ "path-exists@5.0.0"
+ ]
+ },
+ "ms@2.0.0": {
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ },
+ "ms@2.1.2": {
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ },
+ "ms@2.1.3": {
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
+ },
+ "multiparty@4.2.3": {
+ "integrity": "sha512-Ak6EUJZuhGS8hJ3c2fY6UW5MbkGUPMBEGd13djUzoY/BHqV/gTuFWtC6IuVA7A2+v3yjBS6c4or50xhzTQZImQ==",
+ "dependencies": [
+ "http-errors@1.8.1",
+ "safe-buffer@5.2.1",
+ "uid-safe"
+ ]
+ },
+ "mute-stream@0.0.7": {
+ "integrity": "sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ=="
+ },
+ "nan@2.20.0": {
+ "integrity": "sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw=="
+ },
+ "nanoid@3.3.7": {
+ "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g=="
+ },
+ "nanomatch@1.2.13": {
+ "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
+ "dependencies": [
+ "arr-diff",
+ "array-unique",
+ "define-property@2.0.2",
+ "extend-shallow@3.0.2",
+ "fragment-cache",
+ "is-windows",
+ "kind-of@6.0.3",
+ "object.pick",
+ "regex-not",
+ "snapdragon",
+ "to-regex"
+ ]
+ },
+ "natural-compare@1.4.0": {
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="
+ },
+ "negotiator@0.6.3": {
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="
+ },
+ "nested-error-stacks@2.1.1": {
+ "integrity": "sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw=="
+ },
+ "netlify-cli@16.2.0_inquirer@6.5.2": {
+ "integrity": "sha512-I4oDn3ApCzykK6AnejnU6IUr2OiJqpxIR+5+F3YujBakav9AW4s/sNIE9FAryul8u8RGJMfo1J7Irb92+y2uLA==",
+ "dependencies": [
+ "@bugsnag/js",
+ "@fastify/static",
+ "@netlify/build",
+ "@netlify/build-info",
+ "@netlify/config",
+ "@netlify/edge-bundler",
+ "@netlify/local-functions-proxy",
+ "@netlify/serverless-functions-api@1.7.3",
+ "@netlify/zip-it-and-ship-it@9.17.0",
+ "@octokit/rest",
+ "ansi-escapes@6.2.0",
+ "ansi-styles@6.2.1",
+ "ansi-to-html",
+ "ascii-table",
+ "backoff",
+ "better-opn",
+ "boxen",
+ "chalk@5.2.0",
+ "chokidar",
+ "ci-info",
+ "clean-deep",
+ "commander@10.0.1",
+ "comment-json",
+ "concordance",
+ "configstore",
+ "content-type",
+ "cookie@0.5.0",
+ "copy-template-dir",
+ "cron-parser",
+ "debug@4.3.4",
+ "decache",
+ "dot-prop@7.2.0",
+ "dotenv@16.0.3",
+ "env-paths",
+ "envinfo",
+ "etag",
+ "execa@5.1.1",
+ "express",
+ "express-logging",
+ "extract-zip",
+ "fastest-levenshtein",
+ "fastify",
+ "find-up@6.3.0",
+ "flush-write-stream",
+ "folder-walker",
+ "from2-array",
+ "fuzzy",
+ "get-port@5.1.1",
+ "gh-release-fetch",
+ "git-repo-info",
+ "gitconfiglocal",
+ "hasbin",
+ "hasha",
+ "http-proxy",
+ "http-proxy-middleware",
+ "https-proxy-agent",
+ "inquirer",
+ "inquirer-autocomplete-prompt",
+ "is-docker@3.0.0",
+ "is-stream@3.0.0",
+ "is-wsl",
+ "isexe",
+ "jsonwebtoken",
+ "jwt-decode",
+ "lambda-local",
+ "listr",
+ "locate-path@7.2.0",
+ "lodash",
+ "log-symbols@5.1.0",
+ "log-update@5.0.1",
+ "minimist",
+ "multiparty",
+ "netlify",
+ "netlify-headers-parser",
+ "netlify-redirect-parser",
+ "netlify-redirector",
+ "node-fetch@2.6.12",
+ "node-version-alias",
+ "ora",
+ "p-filter",
+ "p-map@5.5.0",
+ "p-wait-for@5.0.2",
+ "parallel-transform",
+ "parse-github-url",
+ "parse-gitignore",
+ "path-key@4.0.0",
+ "prettyjson",
+ "pump@3.0.0",
+ "raw-body@2.5.2",
+ "read-pkg-up",
+ "semver@7.5.4",
+ "source-map-support",
+ "strip-ansi-control-characters",
+ "tabtab",
+ "tempy",
+ "terminal-link",
+ "through2-filter",
+ "through2-map",
+ "to-readable-stream",
+ "toml",
+ "ulid",
+ "unixify",
+ "update-notifier",
+ "uuid@9.0.0",
+ "wait-port",
+ "winston@3.8.2",
+ "write-file-atomic@5.0.1"
+ ]
+ },
+ "netlify-headers-parser@7.1.2": {
+ "integrity": "sha512-DfoboA8PrcLXMan3jIVyLsQtKS+nepKDx6WwZKk5EQDMr2AJoBPCtSHTOLuABzkde1UXdOITf3snmcAmzlNLqw==",
+ "dependencies": [
+ "escape-string-regexp@5.0.0",
+ "fast-safe-stringify",
+ "is-plain-obj@4.1.0",
+ "map-obj",
+ "path-exists@5.0.0",
+ "toml"
+ ]
+ },
+ "netlify-redirect-parser@14.2.0": {
+ "integrity": "sha512-3Mi7sMH7XXZhjvXx/5RtJ/rU/E6zKkE4etcYQbEu8B3r872D0opoYyGdPW/MvaYQyVdfg23XEFaEI4zzQTupaw==",
+ "dependencies": [
+ "fast-safe-stringify",
+ "filter-obj@5.1.0",
+ "is-plain-obj@4.1.0",
+ "path-exists@5.0.0",
+ "toml"
+ ]
+ },
+ "netlify-redirector@0.4.0": {
+ "integrity": "sha512-ssD+V9o2DD9VnilOYC+34i07IrlY8XDsh5mN+qLYA4MxCpdALKXFICcz1KzsHZabuIS5XsF1VP/HzDyx5ubJ2g=="
+ },
+ "netlify@13.1.10": {
+ "integrity": "sha512-ByFz8S08HWVKd9r/lkTahZX7xSq4IRyPCUvuaduI4GHyQaSWEdVNK1krC05vlhL9W0SzDn8Yjowh0Ru4PKrOYw==",
+ "dependencies": [
+ "@netlify/open-api",
+ "lodash-es",
+ "micro-api-client",
+ "node-fetch@3.3.2",
+ "omit.js",
+ "p-wait-for@4.1.0",
+ "qs"
+ ]
+ },
+ "node-domexception@1.0.0": {
+ "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="
+ },
+ "node-fetch@2.6.12": {
+ "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==",
+ "dependencies": [
+ "whatwg-url"
+ ]
+ },
+ "node-fetch@3.3.2": {
+ "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
+ "dependencies": [
+ "data-uri-to-buffer",
+ "fetch-blob",
+ "formdata-polyfill"
+ ]
+ },
+ "node-gyp-build@4.8.2": {
+ "integrity": "sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw=="
+ },
+ "node-int64@0.4.0": {
+ "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw=="
+ },
+ "node-source-walk@6.0.2": {
+ "integrity": "sha512-jn9vOIK/nfqoFCcpK89/VCVaLg1IHE6UVfDOzvqmANaJ/rWCTEdH8RZ1V278nv2jr36BJdyQXIAavBLXpzdlag==",
+ "dependencies": [
+ "@babel/parser"
+ ]
+ },
+ "node-stream-zip@1.15.0": {
+ "integrity": "sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw=="
+ },
+ "node-version-alias@3.4.1": {
+ "integrity": "sha512-Kf3L9spAL6lEHMPyqpwHSTNG3LPkOXBfSUnBMG/YE2TdoC8Qoqf0+qg01nr6K9MFQEcXtWUyTQzLJByRixSBsA==",
+ "dependencies": [
+ "all-node-versions",
+ "filter-obj@5.1.0",
+ "is-plain-obj@4.1.0",
+ "normalize-node-version",
+ "path-exists@5.0.0",
+ "semver@7.6.3"
+ ]
+ },
+ "noop2@2.0.0": {
+ "integrity": "sha512-2bu7Pfpf6uNqashWV8P7yYeutQ3XkLY9MBSYI5sOAFZxuWcW/uJfLbKj5m6SvMDT9U1Y0C+7UFG+7VSiIdXjtA=="
+ },
+ "nopt@5.0.0": {
+ "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
+ "dependencies": [
+ "abbrev"
+ ]
+ },
+ "normalize-node-version@12.4.0": {
+ "integrity": "sha512-0oLZN5xcyKVrSHMk8/9RuNblEe7HEsXAt5Te2xmMiZD9VX7bqWYe0HMyfqSYFD3xv0949lZuXaEwjTqle1uWWQ==",
+ "dependencies": [
+ "all-node-versions",
+ "filter-obj@5.1.0",
+ "semver@7.6.3"
+ ]
+ },
+ "normalize-package-data@3.0.3": {
+ "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==",
+ "dependencies": [
+ "hosted-git-info",
+ "is-core-module",
+ "semver@7.6.3",
+ "validate-npm-package-license"
+ ]
+ },
+ "normalize-path@2.1.1": {
+ "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==",
+ "dependencies": [
+ "remove-trailing-separator"
+ ]
+ },
+ "normalize-path@3.0.0": {
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
+ },
+ "normalize-url@8.0.1": {
+ "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w=="
+ },
+ "npm-run-path@4.0.1": {
+ "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+ "dependencies": [
+ "path-key@3.1.1"
+ ]
+ },
+ "npm-run-path@5.3.0": {
+ "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
+ "dependencies": [
+ "path-key@4.0.0"
+ ]
+ },
+ "npmlog@5.0.1": {
+ "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
+ "dependencies": [
+ "are-we-there-yet",
+ "console-control-strings",
+ "gauge",
+ "set-blocking"
+ ]
+ },
+ "number-is-nan@1.0.1": {
+ "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ=="
+ },
+ "object-assign@4.1.1": {
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
+ },
+ "object-copy@0.1.0": {
+ "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==",
+ "dependencies": [
+ "copy-descriptor",
+ "define-property@0.2.5",
+ "kind-of@3.2.2"
+ ]
+ },
+ "object-inspect@1.13.2": {
+ "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g=="
+ },
+ "object-is@1.1.6": {
+ "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==",
+ "dependencies": [
+ "call-bind",
+ "define-properties"
+ ]
+ },
+ "object-keys@1.1.1": {
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="
+ },
+ "object-visit@1.0.1": {
+ "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==",
+ "dependencies": [
+ "isobject@3.0.1"
+ ]
+ },
+ "object.assign@4.1.5": {
+ "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==",
+ "dependencies": [
+ "call-bind",
+ "define-properties",
+ "has-symbols",
+ "object-keys"
+ ]
+ },
+ "object.entries@1.1.8": {
+ "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==",
+ "dependencies": [
+ "call-bind",
+ "define-properties",
+ "es-object-atoms"
+ ]
+ },
+ "object.fromentries@2.0.8": {
+ "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==",
+ "dependencies": [
+ "call-bind",
+ "define-properties",
+ "es-abstract",
+ "es-object-atoms"
+ ]
+ },
+ "object.groupby@1.0.3": {
+ "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==",
+ "dependencies": [
+ "call-bind",
+ "define-properties",
+ "es-abstract"
+ ]
+ },
+ "object.pick@1.3.0": {
+ "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==",
+ "dependencies": [
+ "isobject@3.0.1"
+ ]
+ },
+ "object.values@1.2.0": {
+ "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==",
+ "dependencies": [
+ "call-bind",
+ "define-properties",
+ "es-object-atoms"
+ ]
+ },
+ "omit.js@2.0.2": {
+ "integrity": "sha512-hJmu9D+bNB40YpL9jYebQl4lsTW6yEHRTroJzNLqQJYHm7c+NQnJGfZmIWh8S3q3KoaxV1aLhV6B3+0N0/kyJg=="
+ },
+ "on-exit-leak-free@2.1.2": {
+ "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA=="
+ },
+ "on-finished@2.4.1": {
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "dependencies": [
+ "ee-first"
+ ]
+ },
+ "on-headers@1.0.2": {
+ "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="
+ },
+ "once@1.4.0": {
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dependencies": [
+ "wrappy"
+ ]
+ },
+ "one-time@1.0.0": {
+ "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==",
+ "dependencies": [
+ "fn.name"
+ ]
+ },
+ "onetime@2.0.1": {
+ "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==",
+ "dependencies": [
+ "mimic-fn@1.2.0"
+ ]
+ },
+ "onetime@5.1.2": {
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "dependencies": [
+ "mimic-fn@2.1.0"
+ ]
+ },
+ "onetime@6.0.0": {
+ "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
+ "dependencies": [
+ "mimic-fn@4.0.0"
+ ]
+ },
+ "open@8.4.2": {
+ "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==",
+ "dependencies": [
+ "define-lazy-prop",
+ "is-docker@2.2.1",
+ "is-wsl"
+ ]
+ },
+ "opentracing@0.14.7": {
+ "integrity": "sha512-vz9iS7MJ5+Bp1URw8Khvdyw1H/hGvzHWlKQ7eRrQojSCDL1/SrWfrY9QebLw97n2deyRtzHRC3MkQfVNUCo91Q=="
+ },
+ "optionator@0.9.4": {
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dependencies": [
+ "deep-is",
+ "fast-levenshtein",
+ "levn",
+ "prelude-ls",
+ "type-check",
+ "word-wrap"
+ ]
+ },
+ "ora@6.3.1": {
+ "integrity": "sha512-ERAyNnZOfqM+Ao3RAvIXkYh5joP220yf59gVe2X/cI6SiCxIdi4c9HZKZD8R6q/RDXEje1THBju6iExiSsgJaQ==",
+ "dependencies": [
+ "chalk@5.2.0",
+ "cli-cursor@4.0.0",
+ "cli-spinners",
+ "is-interactive",
+ "is-unicode-supported",
+ "log-symbols@5.1.0",
+ "stdin-discarder",
+ "strip-ansi@7.1.0",
+ "wcwidth"
+ ]
+ },
+ "os-name@5.1.0": {
+ "integrity": "sha512-YEIoAnM6zFmzw3PQ201gCVCIWbXNyKObGlVvpAVvraAeOHnlYVKFssbA/riRX5R40WA6kKrZ7Dr7dWzO3nKSeQ==",
+ "dependencies": [
+ "macos-release",
+ "windows-release"
+ ]
+ },
+ "os-tmpdir@1.0.2": {
+ "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g=="
+ },
+ "p-cancelable@3.0.0": {
+ "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw=="
+ },
+ "p-event@4.2.0": {
+ "integrity": "sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==",
+ "dependencies": [
+ "p-timeout@3.2.0"
+ ]
+ },
+ "p-event@5.0.1": {
+ "integrity": "sha512-dd589iCQ7m1L0bmC5NLlVYfy3TbBEsMUfWx9PyAgPeIcFZ/E2yaTZ4Rz4MiBmmJShviiftHVXOqfnfzJ6kyMrQ==",
+ "dependencies": [
+ "p-timeout@5.1.0"
+ ]
+ },
+ "p-every@2.0.0": {
+ "integrity": "sha512-MCz9DqD5opPC48Zsd+BHm56O/HfhYIQQtupfDzhXoVgQdg/Ux4F8/JcdRuQ+arq7zD5fB6zP3axbH3d9Nr8dlw==",
+ "dependencies": [
+ "p-map@2.1.0"
+ ]
+ },
+ "p-filter@3.0.0": {
+ "integrity": "sha512-QtoWLjXAW++uTX67HZQz1dbTpqBfiidsB6VtQUC9iR85S120+s0T5sO6s+B5MLzFcZkrEd/DGMmCjR+f2Qpxwg==",
+ "dependencies": [
+ "p-map@5.5.0"
+ ]
+ },
+ "p-finally@1.0.0": {
+ "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow=="
+ },
+ "p-limit@3.1.0": {
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dependencies": [
+ "yocto-queue@0.1.0"
+ ]
+ },
+ "p-limit@4.0.0": {
+ "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==",
+ "dependencies": [
+ "yocto-queue@1.1.1"
+ ]
+ },
+ "p-locate@5.0.0": {
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dependencies": [
+ "p-limit@3.1.0"
+ ]
+ },
+ "p-locate@6.0.0": {
+ "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==",
+ "dependencies": [
+ "p-limit@4.0.0"
+ ]
+ },
+ "p-map@2.1.0": {
+ "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw=="
+ },
+ "p-map@5.5.0": {
+ "integrity": "sha512-VFqfGDHlx87K66yZrNdI4YGtD70IRyd+zSvgks6mzHPRNkoKy+9EKP4SFC77/vTTQYmRmti7dvqC+m5jBrBAcg==",
+ "dependencies": [
+ "aggregate-error"
+ ]
+ },
+ "p-reduce@3.0.0": {
+ "integrity": "sha512-xsrIUgI0Kn6iyDYm9StOpOeK29XM1aboGji26+QEortiFST1hGZaUQOLhtEbqHErPpGW/aSz6allwK2qcptp0Q=="
+ },
+ "p-retry@5.1.2": {
+ "integrity": "sha512-couX95waDu98NfNZV+i/iLt+fdVxmI7CbrrdC2uDWfPdUAApyxT4wmDlyOtR5KtTDmkDO0zDScDjDou9YHhd9g==",
+ "dependencies": [
+ "@types/retry",
+ "retry"
+ ]
+ },
+ "p-timeout@3.2.0": {
+ "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==",
+ "dependencies": [
+ "p-finally"
+ ]
+ },
+ "p-timeout@5.1.0": {
+ "integrity": "sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew=="
+ },
+ "p-timeout@6.1.2": {
+ "integrity": "sha512-UbD77BuZ9Bc9aABo74gfXhNvzC9Tx7SxtHSh1fxvx3jTLLYvmVhiQZZrJzqqU0jKbN32kb5VOKiLEQI/3bIjgQ=="
+ },
+ "p-wait-for@4.1.0": {
+ "integrity": "sha512-i8nE5q++9h8oaQHWltS1Tnnv4IoMDOlqN7C0KFG2OdbK0iFJIt6CROZ8wfBM+K4Pxqfnq4C4lkkpXqTEpB5DZw==",
+ "dependencies": [
+ "p-timeout@5.1.0"
+ ]
+ },
+ "p-wait-for@5.0.2": {
+ "integrity": "sha512-lwx6u1CotQYPVju77R+D0vFomni/AqRfqLmqQ8hekklqZ6gAY9rONh7lBQ0uxWMkC2AuX9b2DVAl8To0NyP1JA==",
+ "dependencies": [
+ "p-timeout@6.1.2"
+ ]
+ },
+ "package-json-from-dist@1.0.1": {
+ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="
+ },
+ "package-json@8.1.1": {
+ "integrity": "sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==",
+ "dependencies": [
+ "got",
+ "registry-auth-token",
+ "registry-url",
+ "semver@7.6.3"
+ ]
+ },
+ "parallel-transform@1.2.0": {
+ "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==",
+ "dependencies": [
+ "cyclist",
+ "inherits",
+ "readable-stream@2.3.8"
+ ]
+ },
+ "parent-module@1.0.1": {
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dependencies": [
+ "callsites"
+ ]
+ },
+ "parse-github-url@1.0.2": {
+ "integrity": "sha512-kgBf6avCbO3Cn6+RnzRGLkUsv4ZVqv/VfAYkRsyBcgkshNvVBkRn1FEZcW0Jb+npXQWm2vHPnnOqFteZxRRGNw=="
+ },
+ "parse-gitignore@2.0.0": {
+ "integrity": "sha512-RmVuCHWsfu0QPNW+mraxh/xjQVw/lhUCUru8Zni3Ctq3AoMhpDTq0OVdKS6iesd6Kqb7viCV3isAL43dciOSog=="
+ },
+ "parse-json@5.2.0": {
+ "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "dependencies": [
+ "@babel/code-frame",
+ "error-ex",
+ "json-parse-even-better-errors",
+ "lines-and-columns"
+ ]
+ },
+ "parse-ms@3.0.0": {
+ "integrity": "sha512-Tpb8Z7r7XbbtBTrM9UhpkzzaMrqA2VXMT3YChzYltwV3P3pM6t8wl7TvpMnSTosz1aQAdVib7kdoys7vYOPerw=="
+ },
+ "parseurl@1.3.3": {
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
+ },
+ "pascalcase@0.1.1": {
+ "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw=="
+ },
+ "path-exists@4.0.0": {
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
+ },
+ "path-exists@5.0.0": {
+ "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ=="
+ },
+ "path-is-absolute@1.0.1": {
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="
+ },
+ "path-key@3.1.1": {
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
+ },
+ "path-key@4.0.0": {
+ "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="
+ },
+ "path-parse@1.0.7": {
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
+ },
+ "path-scurry@1.11.1": {
+ "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+ "dependencies": [
+ "lru-cache@10.4.3",
+ "minipass@7.1.2"
+ ]
+ },
+ "path-to-regexp@0.1.7": {
+ "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
+ },
+ "path-type@4.0.0": {
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="
+ },
+ "path-type@5.0.0": {
+ "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg=="
+ },
+ "pathe@1.1.2": {
+ "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="
+ },
+ "pathval@2.0.0": {
+ "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA=="
+ },
+ "peek-readable@5.2.0": {
+ "integrity": "sha512-U94a+eXHzct7vAd19GH3UQ2dH4Satbng0MyYTMaQatL0pvYYL5CTPR25HBhKtecl+4bfu1/i3vC6k0hydO5Vcw=="
+ },
+ "pend@1.2.0": {
+ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="
+ },
+ "picocolors@1.1.0": {
+ "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw=="
+ },
+ "picomatch@2.3.1": {
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
+ },
+ "pino-abstract-transport@1.2.0": {
+ "integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==",
+ "dependencies": [
+ "readable-stream@4.5.2",
+ "split2@4.2.0"
+ ]
+ },
+ "pino-std-serializers@6.2.2": {
+ "integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA=="
+ },
+ "pino@8.21.0": {
+ "integrity": "sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==",
+ "dependencies": [
+ "atomic-sleep",
+ "fast-redact",
+ "on-exit-leak-free",
+ "pino-abstract-transport",
+ "pino-std-serializers",
+ "process-warning@3.0.0",
+ "quick-format-unescaped",
+ "real-require",
+ "safe-stable-stringify",
+ "sonic-boom",
+ "thread-stream"
+ ]
+ },
+ "pkg-dir@7.0.0": {
+ "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==",
+ "dependencies": [
+ "find-up@6.3.0"
+ ]
+ },
+ "posix-character-classes@0.1.1": {
+ "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg=="
+ },
+ "possible-typed-array-names@1.0.0": {
+ "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q=="
+ },
+ "postcss-values-parser@6.0.2_postcss@8.4.47": {
+ "integrity": "sha512-YLJpK0N1brcNJrs9WatuJFtHaV9q5aAOj+S4DI5S7jgHlRfm0PIbDCAFRYMQD5SHq7Fy6xsDhyutgS0QOAs0qw==",
+ "dependencies": [
+ "color-name@1.1.4",
+ "is-url-superb",
+ "postcss",
+ "quote-unquote"
+ ]
+ },
+ "postcss@8.4.47": {
+ "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
+ "dependencies": [
+ "nanoid",
+ "picocolors",
+ "source-map-js"
+ ]
+ },
+ "precinct@11.0.5": {
+ "integrity": "sha512-oHSWLC8cL/0znFhvln26D14KfCQFFn4KOLSw6hmLhd+LQ2SKt9Ljm89but76Pc7flM9Ty1TnXyrA2u16MfRV3w==",
+ "dependencies": [
+ "@dependents/detective-less",
+ "commander@10.0.1",
+ "detective-amd",
+ "detective-cjs",
+ "detective-es6",
+ "detective-postcss",
+ "detective-sass",
+ "detective-scss",
+ "detective-stylus",
+ "detective-typescript",
+ "module-definition",
+ "node-source-walk"
+ ]
+ },
+ "precond@0.2.3": {
+ "integrity": "sha512-QCYG84SgGyGzqJ/vlMsxeXd/pgL/I94ixdNFyh1PusWmTCyVfPJjZ1K1jvHtsbfnXQs2TSkEP2fR7QiMZAnKFQ=="
+ },
+ "prelude-ls@1.2.1": {
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="
+ },
+ "prettier-linter-helpers@1.0.0": {
+ "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+ "dependencies": [
+ "fast-diff"
+ ]
+ },
+ "prettier-plugin-jsdoc@1.3.0_prettier@3.2.5": {
+ "integrity": "sha512-cQm8xIa0fN9ieJFMXACQd6JPycl+8ouOijAqUqu44EF/s4fXL3Wi9sKXuEaodsEWgCN42Xby/bNhqgM1iWx4uw==",
+ "dependencies": [
+ "binary-searching",
+ "comment-parser",
+ "mdast-util-from-markdown",
+ "prettier"
+ ]
+ },
+ "prettier@3.2.5": {
+ "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A=="
+ },
+ "pretty-format@27.5.1": {
+ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
+ "dependencies": [
+ "ansi-regex@5.0.1",
+ "ansi-styles@5.2.0",
+ "react-is@17.0.2"
+ ]
+ },
+ "pretty-ms@8.0.0": {
+ "integrity": "sha512-ASJqOugUF1bbzI35STMBUpZqdfYKlJugy6JBziGi2EE+AL5JPJGSzvpeVXojxrr0ViUYoToUjb5kjSEGf7Y83Q==",
+ "dependencies": [
+ "parse-ms"
+ ]
+ },
+ "prettyjson@1.2.5": {
+ "integrity": "sha512-rksPWtoZb2ZpT5OVgtmy0KHVM+Dca3iVwWY9ifwhcexfjebtgjg3wmrUt9PvJ59XIYBcknQeYHD8IAnVlh9lAw==",
+ "dependencies": [
+ "colors",
+ "minimist"
+ ]
+ },
+ "process-nextick-args@2.0.1": {
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
+ },
+ "process-warning@2.3.2": {
+ "integrity": "sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA=="
+ },
+ "process-warning@3.0.0": {
+ "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ=="
+ },
+ "process@0.10.1": {
+ "integrity": "sha512-dyIett8dgGIZ/TXKUzeYExt7WA6ldDzys9vTDU/cCA9L17Ypme+KzS+NjQCjpn9xsvi/shbMC+yP/BcFMBz0NA=="
+ },
+ "process@0.11.10": {
+ "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="
+ },
+ "prop-types@15.8.1": {
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "dependencies": [
+ "loose-envify",
+ "object-assign",
+ "react-is@16.13.1"
+ ]
+ },
+ "proto-list@1.2.4": {
+ "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA=="
+ },
+ "protobufjs@7.4.0": {
+ "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==",
+ "dependencies": [
+ "@protobufjs/aspromise",
+ "@protobufjs/base64",
+ "@protobufjs/codegen",
+ "@protobufjs/eventemitter",
+ "@protobufjs/fetch",
+ "@protobufjs/float",
+ "@protobufjs/inquire",
+ "@protobufjs/path",
+ "@protobufjs/pool",
+ "@protobufjs/utf8",
+ "@types/node@22.5.4",
+ "long@5.2.3"
+ ]
+ },
+ "proxy-addr@2.0.7": {
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+ "dependencies": [
+ "forwarded",
+ "ipaddr.js"
+ ]
+ },
+ "proxy-from-env@1.1.0": {
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+ },
+ "ps-list@8.1.1": {
+ "integrity": "sha512-OPS9kEJYVmiO48u/B9qneqhkMvgCxT+Tm28VCEJpheTpl8cJ0ffZRRNgS5mrQRTrX5yRTpaJ+hRDeefXYmmorQ=="
+ },
+ "pump@1.0.3": {
+ "integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==",
+ "dependencies": [
+ "end-of-stream",
+ "once"
+ ]
+ },
+ "pump@3.0.0": {
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "dependencies": [
+ "end-of-stream",
+ "once"
+ ]
+ },
+ "punycode@2.3.1": {
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="
+ },
+ "pupa@3.1.0": {
+ "integrity": "sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==",
+ "dependencies": [
+ "escape-goat"
+ ]
+ },
+ "qs@6.11.0": {
+ "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
+ "dependencies": [
+ "side-channel"
+ ]
+ },
+ "queue-microtask@1.2.3": {
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="
+ },
+ "queue-tick@1.0.1": {
+ "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag=="
+ },
+ "quick-format-unescaped@4.0.4": {
+ "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="
+ },
+ "quick-lru@5.1.1": {
+ "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="
+ },
+ "quote-unquote@1.0.0": {
+ "integrity": "sha512-twwRO/ilhlG/FIgYeKGFqyHhoEhqgnKVkcmqMKi2r524gz3ZbDTcyFt38E9xjJI2vT+KbRNHVbnJ/e0I25Azwg=="
+ },
+ "random-bytes@1.0.0": {
+ "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ=="
+ },
+ "range-parser@1.2.1": {
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
+ },
+ "raw-body@2.5.1": {
+ "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
+ "dependencies": [
+ "bytes",
+ "http-errors@2.0.0",
+ "iconv-lite",
+ "unpipe"
+ ]
+ },
+ "raw-body@2.5.2": {
+ "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
+ "dependencies": [
+ "bytes",
+ "http-errors@2.0.0",
+ "iconv-lite",
+ "unpipe"
+ ]
+ },
+ "rc@1.2.8": {
+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+ "dependencies": [
+ "deep-extend",
+ "ini@1.3.8",
+ "minimist",
+ "strip-json-comments@2.0.1"
+ ]
+ },
+ "react-is@16.13.1": {
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+ },
+ "react-is@17.0.2": {
+ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
+ },
+ "read-pkg-up@9.1.0": {
+ "integrity": "sha512-vaMRR1AC1nrd5CQM0PhlRsO5oc2AAigqr7cCrZ/MW/Rsaflz4RlgzkpL4qoU/z1F6wrbd85iFv1OQj/y5RdGvg==",
+ "dependencies": [
+ "find-up@6.3.0",
+ "read-pkg",
+ "type-fest@2.19.0"
+ ]
+ },
+ "read-pkg@7.1.0": {
+ "integrity": "sha512-5iOehe+WF75IccPc30bWTbpdDQLOCc3Uu8bi3Dte3Eueij81yx1Mrufk8qBx/YAbR4uL1FdUr+7BKXDwEtisXg==",
+ "dependencies": [
+ "@types/normalize-package-data",
+ "normalize-package-data",
+ "parse-json",
+ "type-fest@2.19.0"
+ ]
+ },
+ "readable-stream@2.3.8": {
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+ "dependencies": [
+ "core-util-is",
+ "inherits",
+ "isarray@1.0.0",
+ "process-nextick-args",
+ "safe-buffer@5.1.2",
+ "string_decoder@1.1.1",
+ "util-deprecate"
+ ]
+ },
+ "readable-stream@3.6.2": {
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "dependencies": [
+ "inherits",
+ "string_decoder@1.3.0",
+ "util-deprecate"
+ ]
+ },
+ "readable-stream@4.5.2": {
+ "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==",
+ "dependencies": [
+ "abort-controller",
+ "buffer@6.0.3",
+ "events",
+ "process@0.11.10",
+ "string_decoder@1.3.0"
+ ]
+ },
+ "readable-web-to-node-stream@3.0.2": {
+ "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==",
+ "dependencies": [
+ "readable-stream@3.6.2"
+ ]
+ },
+ "readdir-glob@1.1.3": {
+ "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==",
+ "dependencies": [
+ "minimatch@5.1.6"
+ ]
+ },
+ "readdirp@2.2.1": {
+ "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
+ "dependencies": [
+ "graceful-fs@4.2.11",
+ "micromatch@3.1.10",
+ "readable-stream@2.3.8"
+ ]
+ },
+ "readdirp@3.6.0": {
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dependencies": [
+ "picomatch"
+ ]
+ },
+ "real-require@0.2.0": {
+ "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg=="
+ },
+ "reflect.getprototypeof@1.0.6": {
+ "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==",
+ "dependencies": [
+ "call-bind",
+ "define-properties",
+ "es-abstract",
+ "es-errors",
+ "get-intrinsic",
+ "globalthis",
+ "which-builtin-type"
+ ]
+ },
+ "regex-not@1.0.2": {
+ "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
+ "dependencies": [
+ "extend-shallow@3.0.2",
+ "safe-regex"
+ ]
+ },
+ "regexp-tree@0.1.27": {
+ "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA=="
+ },
+ "regexp.prototype.flags@1.5.3": {
+ "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==",
+ "dependencies": [
+ "call-bind",
+ "define-properties",
+ "es-errors",
+ "set-function-name"
+ ]
+ },
+ "registry-auth-token@5.0.2": {
+ "integrity": "sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==",
+ "dependencies": [
+ "@pnpm/npm-conf"
+ ]
+ },
+ "registry-url@6.0.1": {
+ "integrity": "sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==",
+ "dependencies": [
+ "rc"
+ ]
+ },
+ "remove-trailing-separator@1.1.0": {
+ "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw=="
+ },
+ "repeat-element@1.1.4": {
+ "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ=="
+ },
+ "repeat-string@1.6.1": {
+ "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w=="
+ },
+ "require-directory@2.1.1": {
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="
+ },
+ "require-from-string@2.0.2": {
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="
+ },
+ "require-in-the-middle@6.0.0": {
+ "integrity": "sha512-+dtWQ7l2lqQDxheaG3jjyN1QI37gEwvzACSgjYi4/C2y+ZTUMeRW8BIOm+9NBKvwaMBUSZfPXVOt1skB0vBkRw==",
+ "dependencies": [
+ "debug@4.3.7",
+ "module-details-from-path",
+ "resolve@1.22.8"
+ ]
+ },
+ "require-package-name@2.0.1": {
+ "integrity": "sha512-uuoJ1hU/k6M0779t3VMVIYpb2VMJk05cehCaABFhXaibcbvfgR8wKiozLjVFSzJPmQMRqIcO0HMyTFqfV09V6Q=="
+ },
+ "requires-port@1.0.0": {
+ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
+ },
+ "resolve-alpn@1.2.1": {
+ "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g=="
+ },
+ "resolve-from@4.0.0": {
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="
+ },
+ "resolve-from@5.0.0": {
+ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="
+ },
+ "resolve-pkg-maps@1.0.0": {
+ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="
+ },
+ "resolve-url@0.2.1": {
+ "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg=="
+ },
+ "resolve@1.22.8": {
+ "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
+ "dependencies": [
+ "is-core-module",
+ "path-parse",
+ "supports-preserve-symlinks-flag"
+ ]
+ },
+ "resolve@2.0.0-next.5": {
+ "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==",
+ "dependencies": [
+ "is-core-module",
+ "path-parse",
+ "supports-preserve-symlinks-flag"
+ ]
+ },
+ "responselike@3.0.0": {
+ "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==",
+ "dependencies": [
+ "lowercase-keys"
+ ]
+ },
+ "restore-cursor@2.0.0": {
+ "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==",
+ "dependencies": [
+ "onetime@2.0.1",
+ "signal-exit@3.0.7"
+ ]
+ },
+ "restore-cursor@4.0.0": {
+ "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==",
+ "dependencies": [
+ "onetime@5.1.2",
+ "signal-exit@3.0.7"
+ ]
+ },
+ "ret@0.1.15": {
+ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg=="
+ },
+ "ret@0.2.2": {
+ "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ=="
+ },
+ "retry@0.13.1": {
+ "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="
+ },
+ "reusify@1.0.4": {
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw=="
+ },
+ "rfdc@1.4.1": {
+ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="
+ },
+ "rimraf@3.0.2": {
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "dependencies": [
+ "glob@7.2.3"
+ ]
+ },
+ "rollup@4.24.0": {
+ "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==",
+ "dependencies": [
+ "@rollup/rollup-android-arm-eabi",
+ "@rollup/rollup-android-arm64",
+ "@rollup/rollup-darwin-arm64",
+ "@rollup/rollup-darwin-x64",
+ "@rollup/rollup-linux-arm-gnueabihf",
+ "@rollup/rollup-linux-arm-musleabihf",
+ "@rollup/rollup-linux-arm64-gnu",
+ "@rollup/rollup-linux-arm64-musl",
+ "@rollup/rollup-linux-powerpc64le-gnu",
+ "@rollup/rollup-linux-riscv64-gnu",
+ "@rollup/rollup-linux-s390x-gnu",
+ "@rollup/rollup-linux-x64-gnu",
+ "@rollup/rollup-linux-x64-musl",
+ "@rollup/rollup-win32-arm64-msvc",
+ "@rollup/rollup-win32-ia32-msvc",
+ "@rollup/rollup-win32-x64-msvc",
+ "@types/estree",
+ "fsevents"
+ ]
+ },
+ "run-async@2.4.1": {
+ "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ=="
+ },
+ "run-parallel@1.2.0": {
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dependencies": [
+ "queue-microtask"
+ ]
+ },
+ "rxjs@6.6.7": {
+ "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
+ "dependencies": [
+ "tslib@1.14.1"
+ ]
+ },
+ "safe-array-concat@1.1.2": {
+ "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==",
+ "dependencies": [
+ "call-bind",
+ "get-intrinsic",
+ "has-symbols",
+ "isarray@2.0.5"
+ ]
+ },
+ "safe-buffer@5.1.2": {
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ },
+ "safe-buffer@5.2.1": {
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+ },
+ "safe-json-stringify@1.2.0": {
+ "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg=="
+ },
+ "safe-regex-test@1.0.3": {
+ "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==",
+ "dependencies": [
+ "call-bind",
+ "es-errors",
+ "is-regex"
+ ]
+ },
+ "safe-regex2@2.0.0": {
+ "integrity": "sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==",
+ "dependencies": [
+ "ret@0.2.2"
+ ]
+ },
+ "safe-regex@1.1.0": {
+ "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==",
+ "dependencies": [
+ "ret@0.1.15"
+ ]
+ },
+ "safe-stable-stringify@2.5.0": {
+ "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA=="
+ },
+ "safer-buffer@2.1.2": {
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+ },
+ "secure-json-parse@2.7.0": {
+ "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw=="
+ },
+ "seek-bzip@1.0.6": {
+ "integrity": "sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==",
+ "dependencies": [
+ "commander@2.20.3"
+ ]
+ },
+ "semver-diff@4.0.0": {
+ "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==",
+ "dependencies": [
+ "semver@7.6.3"
+ ]
+ },
+ "semver@6.3.1": {
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="
+ },
+ "semver@7.5.4": {
+ "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+ "dependencies": [
+ "lru-cache@6.0.0"
+ ]
+ },
+ "semver@7.6.3": {
+ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A=="
+ },
+ "send@0.18.0": {
+ "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
+ "dependencies": [
+ "debug@2.6.9",
+ "depd@2.0.0",
+ "destroy",
+ "encodeurl",
+ "escape-html",
+ "etag",
+ "fresh",
+ "http-errors@2.0.0",
+ "mime@1.6.0",
+ "ms@2.1.3",
+ "on-finished",
+ "range-parser",
+ "statuses@2.0.1"
+ ]
+ },
+ "serve-static@1.15.0": {
+ "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
+ "dependencies": [
+ "encodeurl",
+ "escape-html",
+ "parseurl",
+ "send"
+ ]
+ },
+ "set-blocking@2.0.0": {
+ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
+ },
+ "set-cookie-parser@2.7.0": {
+ "integrity": "sha512-lXLOiqpkUumhRdFF3k1osNXCy9akgx/dyPZ5p8qAg9seJzXr5ZrlqZuWIMuY6ejOsVLE6flJ5/h3lsn57fQ/PQ=="
+ },
+ "set-function-length@1.2.2": {
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "dependencies": [
+ "define-data-property",
+ "es-errors",
+ "function-bind",
+ "get-intrinsic",
+ "gopd",
+ "has-property-descriptors"
+ ]
+ },
+ "set-function-name@2.0.2": {
+ "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
+ "dependencies": [
+ "define-data-property",
+ "es-errors",
+ "functions-have-names",
+ "has-property-descriptors"
+ ]
+ },
+ "set-value@2.0.1": {
+ "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
+ "dependencies": [
+ "extend-shallow@2.0.1",
+ "is-extendable@0.1.1",
+ "is-plain-object@2.0.4",
+ "split-string"
+ ]
+ },
+ "setprototypeof@1.2.0": {
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
+ },
+ "shebang-command@2.0.0": {
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dependencies": [
+ "shebang-regex"
+ ]
+ },
+ "shebang-regex@3.0.0": {
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="
+ },
+ "shiki@0.14.7": {
+ "integrity": "sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==",
+ "dependencies": [
+ "ansi-sequence-parser",
+ "jsonc-parser",
+ "vscode-oniguruma",
+ "vscode-textmate"
+ ]
+ },
+ "shimmer@1.2.1": {
+ "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw=="
+ },
+ "side-channel@1.0.6": {
+ "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
+ "dependencies": [
+ "call-bind",
+ "es-errors",
+ "get-intrinsic",
+ "object-inspect"
+ ]
+ },
+ "siginfo@2.0.0": {
+ "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="
+ },
+ "signal-exit@3.0.7": {
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
+ },
+ "signal-exit@4.1.0": {
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="
+ },
+ "simple-swizzle@0.2.2": {
+ "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
+ "dependencies": [
+ "is-arrayish@0.3.2"
+ ]
+ },
+ "slash@3.0.0": {
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="
+ },
+ "slash@4.0.0": {
+ "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew=="
+ },
+ "slice-ansi@0.0.4": {
+ "integrity": "sha512-up04hB2hR92PgjpyU3y/eg91yIBILyjVY26NvvciY3EVVPjybkMszMpXQ9QAkcS3I5rtJBDLoTxxg+qvW8c7rw=="
+ },
+ "slice-ansi@5.0.0": {
+ "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==",
+ "dependencies": [
+ "ansi-styles@6.2.1",
+ "is-fullwidth-code-point@4.0.0"
+ ]
+ },
+ "snapdragon-node@2.1.1": {
+ "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
+ "dependencies": [
+ "define-property@1.0.0",
+ "isobject@3.0.1",
+ "snapdragon-util"
+ ]
+ },
+ "snapdragon-util@3.0.1": {
+ "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
+ "dependencies": [
+ "kind-of@3.2.2"
+ ]
+ },
+ "snapdragon@0.8.2": {
+ "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
+ "dependencies": [
+ "base",
+ "debug@2.6.9",
+ "define-property@0.2.5",
+ "extend-shallow@2.0.1",
+ "map-cache",
+ "source-map@0.5.7",
+ "source-map-resolve",
+ "use"
+ ]
+ },
+ "sonic-boom@3.8.1": {
+ "integrity": "sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==",
+ "dependencies": [
+ "atomic-sleep"
+ ]
+ },
+ "sort-keys-length@1.0.1": {
+ "integrity": "sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==",
+ "dependencies": [
+ "sort-keys"
+ ]
+ },
+ "sort-keys@1.1.2": {
+ "integrity": "sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==",
+ "dependencies": [
+ "is-plain-obj@1.1.0"
+ ]
+ },
+ "source-map-js@1.2.1": {
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="
+ },
+ "source-map-resolve@0.5.3": {
+ "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==",
+ "dependencies": [
+ "atob",
+ "decode-uri-component",
+ "resolve-url",
+ "source-map-url",
+ "urix"
+ ]
+ },
+ "source-map-support@0.5.21": {
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "dependencies": [
+ "buffer-from",
+ "source-map@0.6.1"
+ ]
+ },
+ "source-map-url@0.4.1": {
+ "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw=="
+ },
+ "source-map@0.5.7": {
+ "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="
+ },
+ "source-map@0.6.1": {
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+ },
+ "spdx-correct@3.2.0": {
+ "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==",
+ "dependencies": [
+ "spdx-expression-parse",
+ "spdx-license-ids"
+ ]
+ },
+ "spdx-exceptions@2.5.0": {
+ "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w=="
+ },
+ "spdx-expression-parse@3.0.1": {
+ "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+ "dependencies": [
+ "spdx-exceptions",
+ "spdx-license-ids"
+ ]
+ },
+ "spdx-license-ids@3.0.20": {
+ "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw=="
+ },
+ "split-string@3.1.0": {
+ "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
+ "dependencies": [
+ "extend-shallow@3.0.2"
+ ]
+ },
+ "split2@1.1.1": {
+ "integrity": "sha512-cfurE2q8LamExY+lJ9Ex3ZfBwqAPduzOKVscPDXNCLLMvyaeD3DTz1yk7fVIs6Chco+12XeD0BB6HEoYzPYbXA==",
+ "dependencies": [
+ "through2"
+ ]
+ },
+ "split2@4.2.0": {
+ "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="
+ },
+ "stack-generator@2.0.10": {
+ "integrity": "sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==",
+ "dependencies": [
+ "stackframe"
+ ]
+ },
+ "stack-trace@0.0.10": {
+ "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg=="
+ },
+ "stackback@0.0.2": {
+ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="
+ },
+ "stackframe@1.3.4": {
+ "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw=="
+ },
+ "static-extend@0.1.2": {
+ "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==",
+ "dependencies": [
+ "define-property@0.2.5",
+ "object-copy"
+ ]
+ },
+ "statuses@1.5.0": {
+ "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA=="
+ },
+ "statuses@2.0.1": {
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="
+ },
+ "std-env@3.7.0": {
+ "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg=="
+ },
+ "stdin-discarder@0.1.0": {
+ "integrity": "sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==",
+ "dependencies": [
+ "bl@5.1.0"
+ ]
+ },
+ "stop-iteration-iterator@1.0.0": {
+ "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==",
+ "dependencies": [
+ "internal-slot"
+ ]
+ },
+ "streamx@2.20.1": {
+ "integrity": "sha512-uTa0mU6WUC65iUvzKH4X9hEdvSW7rbPxPtwfWiLMSj3qTdQbAiUboZTxauKfpFuGIGa1C2BYijZ7wgdUXICJhA==",
+ "dependencies": [
+ "bare-events",
+ "fast-fifo",
+ "queue-tick",
+ "text-decoder"
+ ]
+ },
+ "string-template@0.2.1": {
+ "integrity": "sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw=="
+ },
+ "string-width@1.0.2": {
+ "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==",
+ "dependencies": [
+ "code-point-at",
+ "is-fullwidth-code-point@1.0.0",
+ "strip-ansi@3.0.1"
+ ]
+ },
+ "string-width@2.1.1": {
+ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "dependencies": [
+ "is-fullwidth-code-point@2.0.0",
+ "strip-ansi@4.0.0"
+ ]
+ },
+ "string-width@4.2.3": {
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dependencies": [
+ "emoji-regex@8.0.0",
+ "is-fullwidth-code-point@3.0.0",
+ "strip-ansi@6.0.1"
+ ]
+ },
+ "string-width@5.1.2": {
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+ "dependencies": [
+ "eastasianwidth",
+ "emoji-regex@9.2.2",
+ "strip-ansi@7.1.0"
+ ]
+ },
+ "string.prototype.includes@2.0.0": {
+ "integrity": "sha512-E34CkBgyeqNDcrbU76cDjL5JLcVrtSdYq0MEh/B10r17pRP4ciHLwTgnuLV8Ay6cgEMLkcBkFCKyFZ43YldYzg==",
+ "dependencies": [
+ "define-properties",
+ "es-abstract"
+ ]
+ },
+ "string.prototype.matchall@4.0.11": {
+ "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==",
+ "dependencies": [
+ "call-bind",
+ "define-properties",
+ "es-abstract",
+ "es-errors",
+ "es-object-atoms",
+ "get-intrinsic",
+ "gopd",
+ "has-symbols",
+ "internal-slot",
+ "regexp.prototype.flags",
+ "set-function-name",
+ "side-channel"
+ ]
+ },
+ "string.prototype.repeat@1.0.0": {
+ "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==",
+ "dependencies": [
+ "define-properties",
+ "es-abstract"
+ ]
+ },
+ "string.prototype.trim@1.2.9": {
+ "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==",
+ "dependencies": [
+ "call-bind",
+ "define-properties",
+ "es-abstract",
+ "es-object-atoms"
+ ]
+ },
+ "string.prototype.trimend@1.0.8": {
+ "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==",
+ "dependencies": [
+ "call-bind",
+ "define-properties",
+ "es-object-atoms"
+ ]
+ },
+ "string.prototype.trimstart@1.0.8": {
+ "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==",
+ "dependencies": [
+ "call-bind",
+ "define-properties",
+ "es-object-atoms"
+ ]
+ },
+ "string_decoder@1.1.1": {
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dependencies": [
+ "safe-buffer@5.1.2"
+ ]
+ },
+ "string_decoder@1.3.0": {
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "dependencies": [
+ "safe-buffer@5.2.1"
+ ]
+ },
+ "strip-ansi-control-characters@2.0.0": {
+ "integrity": "sha512-Q0/k5orrVGeaOlIOUn1gybGU0IcAbgHQT1faLo5hik4DqClKVSaka5xOhNNoRgtfztHVxCYxi7j71mrWom0bIw=="
+ },
+ "strip-ansi@3.0.1": {
+ "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
+ "dependencies": [
+ "ansi-regex@2.1.1"
+ ]
+ },
+ "strip-ansi@4.0.0": {
+ "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==",
+ "dependencies": [
+ "ansi-regex@3.0.1"
+ ]
+ },
+ "strip-ansi@5.2.0": {
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dependencies": [
+ "ansi-regex@4.1.1"
+ ]
+ },
+ "strip-ansi@6.0.1": {
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dependencies": [
+ "ansi-regex@5.0.1"
+ ]
+ },
+ "strip-ansi@7.1.0": {
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "dependencies": [
+ "ansi-regex@6.1.0"
+ ]
+ },
+ "strip-bom@3.0.0": {
+ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="
+ },
+ "strip-dirs@3.0.0": {
+ "integrity": "sha512-I0sdgcFTfKQlUPZyAqPJmSG3HLO9rWDFnxonnIbskYNM3DwFOeTNB5KzVq3dA1GdRAc/25b5Y7UO2TQfKWw4aQ==",
+ "dependencies": [
+ "inspect-with-kind",
+ "is-plain-obj@1.1.0"
+ ]
+ },
+ "strip-final-newline@2.0.0": {
+ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="
+ },
+ "strip-final-newline@3.0.0": {
+ "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="
+ },
+ "strip-json-comments@2.0.1": {
+ "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="
+ },
+ "strip-json-comments@3.1.1": {
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="
+ },
+ "strip-outer@2.0.0": {
+ "integrity": "sha512-A21Xsm1XzUkK0qK1ZrytDUvqsQWict2Cykhvi0fBQntGG5JSprESasEyV1EZ/4CiR5WB5KjzLTrP/bO37B0wPg=="
+ },
+ "strtok3@7.1.1": {
+ "integrity": "sha512-mKX8HA/cdBqMKUr0MMZAFssCkIGoZeSCMXgnt79yKxNFguMLVFgRe6wB+fsL0NmoHDbeyZXczy7vEPSoo3rkzg==",
+ "dependencies": [
+ "@tokenizer/token",
+ "peek-readable"
+ ]
+ },
+ "supports-color@2.0.0": {
+ "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g=="
+ },
+ "supports-color@5.5.0": {
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dependencies": [
+ "has-flag@3.0.0"
+ ]
+ },
+ "supports-color@7.2.0": {
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dependencies": [
+ "has-flag@4.0.0"
+ ]
+ },
+ "supports-color@9.4.0": {
+ "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw=="
+ },
+ "supports-hyperlinks@2.3.0": {
+ "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==",
+ "dependencies": [
+ "has-flag@4.0.0",
+ "supports-color@7.2.0"
+ ]
+ },
+ "supports-preserve-symlinks-flag@1.0.0": {
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="
+ },
+ "symbol-observable@1.2.0": {
+ "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ=="
+ },
+ "synckit@0.9.1": {
+ "integrity": "sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==",
+ "dependencies": [
+ "@pkgr/core",
+ "tslib@2.7.0"
+ ]
+ },
+ "tabtab@3.0.2": {
+ "integrity": "sha512-jANKmUe0sIQc/zTALTBy186PoM/k6aPrh3A7p6AaAfF6WPSbTx1JYeGIGH162btpH+mmVEXln+UxwViZHO2Jhg==",
+ "dependencies": [
+ "debug@4.3.7",
+ "es6-promisify",
+ "inquirer",
+ "minimist",
+ "mkdirp@0.5.6",
+ "untildify"
+ ]
+ },
+ "tar-stream@2.2.0": {
+ "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
+ "dependencies": [
+ "bl@4.1.0",
+ "end-of-stream",
+ "fs-constants",
+ "inherits",
+ "readable-stream@3.6.2"
+ ]
+ },
+ "tar-stream@3.1.7": {
+ "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==",
+ "dependencies": [
+ "b4a",
+ "fast-fifo",
+ "streamx"
+ ]
+ },
+ "tar@6.2.1": {
+ "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
+ "dependencies": [
+ "chownr",
+ "fs-minipass",
+ "minipass@5.0.0",
+ "minizlib",
+ "mkdirp@1.0.4",
+ "yallist"
+ ]
+ },
+ "temp-dir@2.0.0": {
+ "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg=="
+ },
+ "tempy@3.0.0": {
+ "integrity": "sha512-B2I9X7+o2wOaW4r/CWMkpOO9mdiTRCxXNgob6iGvPmfPWgH/KyUD6Uy5crtWBxIBe3YrNZKR2lSzv1JJKWD4vA==",
+ "dependencies": [
+ "is-stream@3.0.0",
+ "temp-dir",
+ "type-fest@2.19.0",
+ "unique-string"
+ ]
+ },
+ "terminal-link@3.0.0": {
+ "integrity": "sha512-flFL3m4wuixmf6IfhFJd1YPiLiMuxEc8uHRM1buzIeZPm22Au2pDqBJQgdo7n1WfPU1ONFGv7YDwpFBmHGF6lg==",
+ "dependencies": [
+ "ansi-escapes@5.0.0",
+ "supports-hyperlinks"
+ ]
+ },
+ "text-decoder@1.2.0": {
+ "integrity": "sha512-n1yg1mOj9DNpk3NeZOx7T6jchTbyJS3i3cucbNN6FcdPriMZx7NsgrGpWWdWZZGxD7ES1XB+3uoqHMgOKaN+fg==",
+ "dependencies": [
+ "b4a"
+ ]
+ },
+ "text-hex@1.0.0": {
+ "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg=="
+ },
+ "text-table@0.2.0": {
+ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="
+ },
+ "thread-stream@2.7.0": {
+ "integrity": "sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==",
+ "dependencies": [
+ "real-require"
+ ]
+ },
+ "thriftrw@3.11.4": {
+ "integrity": "sha512-UcuBd3eanB3T10nXWRRMwfwoaC6VMk7qe3/5YIWP2Jtw+EbHqJ0p1/K3x8ixiR5dozKSSfcg1W+0e33G1Di3XA==",
+ "dependencies": [
+ "bufrw",
+ "error",
+ "long@2.4.0"
+ ]
+ },
+ "through2-filter@3.0.0": {
+ "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==",
+ "dependencies": [
+ "through2",
+ "xtend"
+ ]
+ },
+ "through2-map@3.0.0": {
+ "integrity": "sha512-Ms68QPbSJKjRYY7fmqZHB0VGt+vD0/tjmDHUWgxltjifCof6hZWWeQAEi27Wjbs7jyNlIIyerQw/TVj7gHkd/Q==",
+ "dependencies": [
+ "through2",
+ "xtend"
+ ]
+ },
+ "through2@2.0.5": {
+ "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
+ "dependencies": [
+ "readable-stream@2.3.8",
+ "xtend"
+ ]
+ },
+ "through@2.3.8": {
+ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="
+ },
+ "time-zone@1.0.0": {
+ "integrity": "sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA=="
+ },
+ "tiny-lru@11.2.11": {
+ "integrity": "sha512-27BIW0dIWTYYoWNnqSmoNMKe5WIbkXsc0xaCQHd3/3xT2XMuMJrzHdrO9QBFR14emBz1Bu0dOAs2sCBBrvgPQA=="
+ },
+ "tinybench@2.9.0": {
+ "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="
+ },
+ "tinyexec@0.3.0": {
+ "integrity": "sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg=="
+ },
+ "tinypool@1.0.1": {
+ "integrity": "sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA=="
+ },
+ "tinyrainbow@1.2.0": {
+ "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ=="
+ },
+ "tinyspy@3.0.2": {
+ "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q=="
+ },
+ "tmp-promise@3.0.3": {
+ "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==",
+ "dependencies": [
+ "tmp@0.2.3"
+ ]
+ },
+ "tmp@0.0.33": {
+ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+ "dependencies": [
+ "os-tmpdir"
+ ]
+ },
+ "tmp@0.2.3": {
+ "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w=="
+ },
+ "to-fast-properties@2.0.0": {
+ "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog=="
+ },
+ "to-object-path@0.3.0": {
+ "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==",
+ "dependencies": [
+ "kind-of@3.2.2"
+ ]
+ },
+ "to-readable-stream@3.0.0": {
+ "integrity": "sha512-vD2LytT6DxPynBa1xbMtswY9gGqj27wNbh2uvI5OhBe+mrGLurRWRQZyQn3812sqlQRtUJwaKVshG+PoGwbPDQ=="
+ },
+ "to-regex-range@2.1.1": {
+ "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==",
+ "dependencies": [
+ "is-number@3.0.0",
+ "repeat-string"
+ ]
+ },
+ "to-regex-range@5.0.1": {
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dependencies": [
+ "is-number@7.0.0"
+ ]
+ },
+ "to-regex@3.0.2": {
+ "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
+ "dependencies": [
+ "define-property@2.0.2",
+ "extend-shallow@3.0.2",
+ "regex-not",
+ "safe-regex"
+ ]
+ },
+ "toidentifier@1.0.1": {
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="
+ },
+ "token-types@5.0.1": {
+ "integrity": "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==",
+ "dependencies": [
+ "@tokenizer/token",
+ "ieee754"
+ ]
+ },
+ "toml@3.0.0": {
+ "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w=="
+ },
+ "tomlify-j0.4@3.0.0": {
+ "integrity": "sha512-2Ulkc8T7mXJ2l0W476YC/A209PR38Nw8PuaCNtk9uI3t1zzFdGQeWYGQvmj2PZkVvRC/Yoi4xQKMRnWc/N29tQ=="
+ },
+ "tr46@0.0.3": {
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
+ },
+ "trim-repeated@2.0.0": {
+ "integrity": "sha512-QUHBFTJGdOwmp0tbOG505xAgOp/YliZP/6UgafFXYZ26WT1bvQmSMJUvkeVSASuJJHbqsFbynTvkd5W8RBTipg==",
+ "dependencies": [
+ "escape-string-regexp@5.0.0"
+ ]
+ },
+ "triple-beam@1.4.1": {
+ "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg=="
+ },
+ "ts-api-utils@1.3.0_typescript@5.6.2": {
+ "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==",
+ "dependencies": [
+ "typescript@5.6.2"
+ ]
+ },
+ "ts-node@10.9.2_@types+node@22.5.4_typescript@5.6.2": {
+ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
+ "dependencies": [
+ "@cspotcode/source-map-support",
+ "@tsconfig/node10",
+ "@tsconfig/node12",
+ "@tsconfig/node14",
+ "@tsconfig/node16",
+ "@types/node@22.5.4",
+ "acorn",
+ "acorn-walk",
+ "arg",
+ "create-require",
+ "diff",
+ "make-error",
+ "typescript@5.6.2",
+ "v8-compile-cache-lib",
+ "yn"
+ ]
+ },
+ "tsconfig-paths@3.15.0": {
+ "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==",
+ "dependencies": [
+ "@types/json5",
+ "json5",
+ "minimist",
+ "strip-bom"
+ ]
+ },
+ "tslib@1.14.1": {
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+ },
+ "tslib@2.7.0": {
+ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA=="
+ },
+ "tsutils@3.21.0_typescript@5.6.2": {
+ "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
+ "dependencies": [
+ "tslib@1.14.1",
+ "typescript@5.6.2"
+ ]
+ },
+ "type-check@0.4.0": {
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dependencies": [
+ "prelude-ls"
+ ]
+ },
+ "type-fest@0.20.2": {
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ=="
+ },
+ "type-fest@0.21.3": {
+ "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="
+ },
+ "type-fest@0.8.1": {
+ "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA=="
+ },
+ "type-fest@1.4.0": {
+ "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA=="
+ },
+ "type-fest@2.19.0": {
+ "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA=="
+ },
+ "type-fest@3.13.1": {
+ "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g=="
+ },
+ "type-is@1.6.18": {
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "dependencies": [
+ "media-typer",
+ "mime-types"
+ ]
+ },
+ "typed-array-buffer@1.0.2": {
+ "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==",
+ "dependencies": [
+ "call-bind",
+ "es-errors",
+ "is-typed-array"
+ ]
+ },
+ "typed-array-byte-length@1.0.1": {
+ "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==",
+ "dependencies": [
+ "call-bind",
+ "for-each",
+ "gopd",
+ "has-proto",
+ "is-typed-array"
+ ]
+ },
+ "typed-array-byte-offset@1.0.2": {
+ "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==",
+ "dependencies": [
+ "available-typed-arrays",
+ "call-bind",
+ "for-each",
+ "gopd",
+ "has-proto",
+ "is-typed-array"
+ ]
+ },
+ "typed-array-length@1.0.6": {
+ "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==",
+ "dependencies": [
+ "call-bind",
+ "for-each",
+ "gopd",
+ "has-proto",
+ "is-typed-array",
+ "possible-typed-array-names"
+ ]
+ },
+ "typedarray-to-buffer@3.1.5": {
+ "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
+ "dependencies": [
+ "is-typedarray"
+ ]
+ },
+ "typedoc-plugin-missing-exports@2.3.0_typedoc@0.25.13__typescript@5.4.5": {
+ "integrity": "sha512-iI9ITNNLlbsLCBBeYDyu0Qqp3GN/9AGyWNKg8bctRXuZEPT7G1L+0+MNWG9MsHcf/BFmNbXL0nQ8mC/tXRicog==",
+ "dependencies": [
+ "typedoc"
+ ]
+ },
+ "typedoc@0.25.13_typescript@5.4.5": {
+ "integrity": "sha512-pQqiwiJ+Z4pigfOnnysObszLiU3mVLWAExSPf+Mu06G/qsc3wzbuM56SZQvONhHLncLUhYzOVkjFFpFfL5AzhQ==",
+ "dependencies": [
+ "lunr",
+ "marked",
+ "minimatch@9.0.5",
+ "shiki",
+ "typescript@5.4.5"
+ ]
+ },
+ "typescript@5.4.5": {
+ "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ=="
+ },
+ "typescript@5.6.2": {
+ "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw=="
+ },
+ "uid-safe@2.1.5": {
+ "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
+ "dependencies": [
+ "random-bytes"
+ ]
+ },
+ "ulid@2.3.0": {
+ "integrity": "sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw=="
+ },
+ "unbox-primitive@1.0.2": {
+ "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==",
+ "dependencies": [
+ "call-bind",
+ "has-bigints",
+ "has-symbols",
+ "which-boxed-primitive"
+ ]
+ },
+ "unbzip2-stream@1.4.3": {
+ "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==",
+ "dependencies": [
+ "buffer@5.7.1",
+ "through"
+ ]
+ },
+ "undici-types@6.19.8": {
+ "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="
+ },
+ "union-value@1.0.1": {
+ "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
+ "dependencies": [
+ "arr-union",
+ "get-value",
+ "is-extendable@0.1.1",
+ "set-value"
+ ]
+ },
+ "unique-string@3.0.0": {
+ "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==",
+ "dependencies": [
+ "crypto-random-string"
+ ]
+ },
+ "unist-util-stringify-position@4.0.0": {
+ "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==",
+ "dependencies": [
+ "@types/unist"
+ ]
+ },
+ "universal-user-agent@6.0.1": {
+ "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ=="
+ },
+ "unix-dgram@2.0.6": {
+ "integrity": "sha512-AURroAsb73BZ6CdAyMrTk/hYKNj3DuYYEuOaB8bYMOHGKupRNScw90Q5C71tWJc3uE7dIeXRyuwN0xLLq3vDTg==",
+ "dependencies": [
+ "bindings",
+ "nan"
+ ]
+ },
+ "unixify@1.0.0": {
+ "integrity": "sha512-6bc58dPYhCMHHuwxldQxO3RRNZ4eCogZ/st++0+fcC1nr0jiGUtAdBJ2qzmLQWSxbtz42pWt4QQMiZ9HvZf5cg==",
+ "dependencies": [
+ "normalize-path@2.1.1"
+ ]
+ },
+ "unpipe@1.0.0": {
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="
+ },
+ "unset-value@1.0.0": {
+ "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==",
+ "dependencies": [
+ "has-value@0.3.1",
+ "isobject@3.0.1"
+ ]
+ },
+ "untildify@3.0.3": {
+ "integrity": "sha512-iSk/J8efr8uPT/Z4eSUywnqyrQU7DSdMfdqK4iWEaUVVmcP5JcnpRqmVMwcwcnmI1ATFNgC5V90u09tBynNFKA=="
+ },
+ "update-notifier@6.0.2": {
+ "integrity": "sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==",
+ "dependencies": [
+ "boxen",
+ "chalk@5.2.0",
+ "configstore",
+ "has-yarn",
+ "import-lazy",
+ "is-ci",
+ "is-installed-globally",
+ "is-npm",
+ "is-yarn-global",
+ "latest-version",
+ "pupa",
+ "semver@7.6.3",
+ "semver-diff",
+ "xdg-basedir"
+ ]
+ },
+ "uri-js@4.4.1": {
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dependencies": [
+ "punycode"
+ ]
+ },
+ "urix@0.1.0": {
+ "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg=="
+ },
+ "urlpattern-polyfill@8.0.2": {
+ "integrity": "sha512-Qp95D4TPJl1kC9SKigDcqgyM2VDVO4RiJc2d4qe5GrYm+zbIQCWWKAFaJNQ4BhdFeDGwBmAxqJBwWSJDb9T3BQ=="
+ },
+ "use@3.1.1": {
+ "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ=="
+ },
+ "util-deprecate@1.0.2": {
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
+ },
+ "utils-merge@1.0.1": {
+ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="
+ },
+ "uuid@8.3.2": {
+ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
+ },
+ "uuid@9.0.0": {
+ "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg=="
+ },
+ "v8-compile-cache-lib@3.0.1": {
+ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg=="
+ },
+ "validate-npm-package-license@3.0.4": {
+ "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+ "dependencies": [
+ "spdx-correct",
+ "spdx-expression-parse"
+ ]
+ },
+ "validate-npm-package-name@4.0.0": {
+ "integrity": "sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q==",
+ "dependencies": [
+ "builtins"
+ ]
+ },
+ "vary@1.1.2": {
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="
+ },
+ "vite-node@2.1.1_@types+node@20.16.10": {
+ "integrity": "sha512-N/mGckI1suG/5wQI35XeR9rsMsPqKXzq1CdUndzVstBj/HvyxxGctwnK6WX43NGt5L3Z5tcRf83g4TITKJhPrA==",
+ "dependencies": [
+ "cac",
+ "debug@4.3.7",
+ "pathe",
+ "vite"
+ ]
+ },
+ "vite@5.4.8_@types+node@20.16.10": {
+ "integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==",
+ "dependencies": [
+ "@types/node@20.16.10",
+ "esbuild@0.21.5",
+ "fsevents",
+ "postcss",
+ "rollup"
+ ]
+ },
+ "vitest@2.1.1_@types+node@20.16.10_@vitest+spy@2.1.1_vite@5.4.8__@types+node@20.16.10": {
+ "integrity": "sha512-97We7/VC0e9X5zBVkvt7SGQMGrRtn3KtySFQG5fpaMlS+l62eeXRQO633AYhSTC3z7IMebnPPNjGXVGNRFlxBA==",
+ "dependencies": [
+ "@types/node@20.16.10",
+ "@vitest/expect",
+ "@vitest/mocker",
+ "@vitest/pretty-format@2.1.2",
+ "@vitest/runner",
+ "@vitest/snapshot",
+ "@vitest/spy",
+ "@vitest/utils",
+ "chai",
+ "debug@4.3.7",
+ "magic-string",
+ "pathe",
+ "std-env",
+ "tinybench",
+ "tinyexec",
+ "tinypool",
+ "tinyrainbow",
+ "vite",
+ "vite-node",
+ "why-is-node-running"
+ ]
+ },
+ "vscode-oniguruma@1.7.0": {
+ "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA=="
+ },
+ "vscode-textmate@8.0.0": {
+ "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg=="
+ },
+ "wait-port@1.0.4": {
+ "integrity": "sha512-w8Ftna3h6XSFWWc2JC5gZEgp64nz8bnaTp5cvzbJSZ53j+omktWTDdwXxEF0jM8YveviLgFWvNGrSvRHnkyHyw==",
+ "dependencies": [
+ "chalk@4.1.2",
+ "commander@9.5.0",
+ "debug@4.3.7"
+ ]
+ },
+ "wcwidth@1.0.1": {
+ "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
+ "dependencies": [
+ "defaults"
+ ]
+ },
+ "web-streams-polyfill@3.3.3": {
+ "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="
+ },
+ "webidl-conversions@3.0.1": {
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
+ },
+ "well-known-symbols@2.0.0": {
+ "integrity": "sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q=="
+ },
+ "whatwg-url@5.0.0": {
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "dependencies": [
+ "tr46",
+ "webidl-conversions"
+ ]
+ },
+ "which-boxed-primitive@1.0.2": {
+ "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
+ "dependencies": [
+ "is-bigint",
+ "is-boolean-object",
+ "is-number-object",
+ "is-string",
+ "is-symbol"
+ ]
+ },
+ "which-builtin-type@1.1.4": {
+ "integrity": "sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==",
+ "dependencies": [
+ "function.prototype.name",
+ "has-tostringtag",
+ "is-async-function",
+ "is-date-object",
+ "is-finalizationregistry",
+ "is-generator-function",
+ "is-regex",
+ "is-weakref",
+ "isarray@2.0.5",
+ "which-boxed-primitive",
+ "which-collection",
+ "which-typed-array"
+ ]
+ },
+ "which-collection@1.0.2": {
+ "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==",
+ "dependencies": [
+ "is-map",
+ "is-set",
+ "is-weakmap",
+ "is-weakset"
+ ]
+ },
+ "which-typed-array@1.1.15": {
+ "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==",
+ "dependencies": [
+ "available-typed-arrays",
+ "call-bind",
+ "for-each",
+ "gopd",
+ "has-tostringtag"
+ ]
+ },
+ "which@2.0.2": {
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dependencies": [
+ "isexe"
+ ]
+ },
+ "why-is-node-running@2.3.0": {
+ "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
+ "dependencies": [
+ "siginfo",
+ "stackback"
+ ]
+ },
+ "wide-align@1.1.5": {
+ "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
+ "dependencies": [
+ "string-width@4.2.3"
+ ]
+ },
+ "widest-line@4.0.1": {
+ "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==",
+ "dependencies": [
+ "string-width@5.1.2"
+ ]
+ },
+ "windows-release@5.1.1": {
+ "integrity": "sha512-NMD00arvqcq2nwqc5Q6KtrSRHK+fVD31erE5FEMahAw5PmVCgD7MUXodq3pdZSUkqA9Cda2iWx6s1XYwiJWRmw==",
+ "dependencies": [
+ "execa@5.1.1"
+ ]
+ },
+ "winston-transport@4.8.0": {
+ "integrity": "sha512-qxSTKswC6llEMZKgCQdaWgDuMJQnhuvF5f2Nk3SNXc4byfQ+voo2mX1Px9dkNOuR8p0KAjfPG29PuYUSIb+vSA==",
+ "dependencies": [
+ "logform",
+ "readable-stream@4.5.2",
+ "triple-beam"
+ ]
+ },
+ "winston@3.14.2": {
+ "integrity": "sha512-CO8cdpBB2yqzEf8v895L+GNKYJiEq8eKlHU38af3snQBQ+sdAIUepjMSguOIJC7ICbzm0ZI+Af2If4vIJrtmOg==",
+ "dependencies": [
+ "@colors/colors@1.6.0",
+ "@dabh/diagnostics",
+ "async@3.2.6",
+ "is-stream@2.0.1",
+ "logform",
+ "one-time",
+ "readable-stream@3.6.2",
+ "safe-stable-stringify",
+ "stack-trace",
+ "triple-beam",
+ "winston-transport"
+ ]
+ },
+ "winston@3.8.2": {
+ "integrity": "sha512-MsE1gRx1m5jdTTO9Ld/vND4krP2To+lgDoMEHGGa4HIlAUyXJtfc7CxQcGXVyz2IBpw5hbFkj2b/AtUdQwyRew==",
+ "dependencies": [
+ "@colors/colors@1.5.0",
+ "@dabh/diagnostics",
+ "async@3.2.6",
+ "is-stream@2.0.1",
+ "logform",
+ "one-time",
+ "readable-stream@3.6.2",
+ "safe-stable-stringify",
+ "stack-trace",
+ "triple-beam",
+ "winston-transport"
+ ]
+ },
+ "word-wrap@1.2.5": {
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="
+ },
+ "wrap-ansi@3.0.1": {
+ "integrity": "sha512-iXR3tDXpbnTpzjKSylUJRkLuOrEC7hwEB221cgn6wtF8wpmz28puFXAEfPT5zrjM3wahygB//VuWEr1vTkDcNQ==",
+ "dependencies": [
+ "string-width@2.1.1",
+ "strip-ansi@4.0.0"
+ ]
+ },
+ "wrap-ansi@7.0.0": {
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dependencies": [
+ "ansi-styles@4.3.0",
+ "string-width@4.2.3",
+ "strip-ansi@6.0.1"
+ ]
+ },
+ "wrap-ansi@8.1.0": {
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "dependencies": [
+ "ansi-styles@6.2.1",
+ "string-width@5.1.2",
+ "strip-ansi@7.1.0"
+ ]
+ },
+ "wrappy@1.0.2": {
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
+ },
+ "write-file-atomic@3.0.3": {
+ "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
+ "dependencies": [
+ "imurmurhash",
+ "is-typedarray",
+ "signal-exit@3.0.7",
+ "typedarray-to-buffer"
+ ]
+ },
+ "write-file-atomic@4.0.2": {
+ "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==",
+ "dependencies": [
+ "imurmurhash",
+ "signal-exit@3.0.7"
+ ]
+ },
+ "write-file-atomic@5.0.1": {
+ "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==",
+ "dependencies": [
+ "imurmurhash",
+ "signal-exit@4.1.0"
+ ]
+ },
+ "xdg-basedir@5.1.0": {
+ "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ=="
+ },
+ "xorshift@1.2.0": {
+ "integrity": "sha512-iYgNnGyeeJ4t6U11NpA/QiKy+PXn5Aa3Azg5qkwIFz1tBLllQrjjsk9yzD7IAK0naNU4JxdeDgqW9ov4u/hc4g=="
+ },
+ "xtend@4.0.2": {
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
+ },
+ "y18n@5.0.8": {
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="
+ },
+ "yallist@4.0.0": {
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+ },
+ "yaml@2.5.1": {
+ "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q=="
+ },
+ "yargs-parser@21.1.1": {
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="
+ },
+ "yargs@17.7.2": {
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "dependencies": [
+ "cliui",
+ "escalade",
+ "get-caller-file",
+ "require-directory",
+ "string-width@4.2.3",
+ "y18n",
+ "yargs-parser"
+ ]
+ },
+ "yauzl@2.10.0": {
+ "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
+ "dependencies": [
+ "buffer-crc32@0.2.13",
+ "fd-slicer"
+ ]
+ },
+ "yn@3.1.1": {
+ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q=="
+ },
+ "yocto-queue@0.1.0": {
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="
+ },
+ "yocto-queue@1.1.1": {
+ "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g=="
+ },
+ "zip-stream@4.1.1": {
+ "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==",
+ "dependencies": [
+ "archiver-utils@3.0.4",
+ "compress-commons@4.1.2",
+ "readable-stream@3.6.2"
+ ]
+ },
+ "zip-stream@6.0.1": {
+ "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==",
+ "dependencies": [
+ "archiver-utils@5.0.2",
+ "compress-commons@6.0.2",
+ "readable-stream@4.5.2"
+ ]
+ },
+ "zod@3.23.8": {
+ "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g=="
+ }
+ },
+ "workspace": {
+ "packageJson": {
+ "dependencies": [
+ "npm:@types/node@^20.11.5",
+ "npm:@typescript-eslint/eslint-plugin@^7.8.0",
+ "npm:@typescript-eslint/parser@^7.8.0",
+ "npm:eslint-config-prettier@^9.1.0",
+ "npm:eslint-plugin-import@^2.29.1",
+ "npm:eslint-plugin-jsx-a11y@^6.6.1",
+ "npm:eslint-plugin-prettier@^5.1.3",
+ "npm:eslint-plugin-react-hooks@^4.3.0",
+ "npm:eslint-plugin-react@^7.28.0",
+ "npm:eslint@^8.57.0",
+ "npm:husky@^8.0.3",
+ "npm:netlify-cli@16.2.0",
+ "npm:prettier-plugin-jsdoc@^1.3.0",
+ "npm:prettier@3.2.5",
+ "npm:typedoc-plugin-missing-exports@^2.1.0",
+ "npm:typedoc@~0.25.3",
+ "npm:typescript@^5.4.5",
+ "npm:vite@^5.0.12",
+ "npm:vitest@^2.0.5"
+ ]
+ }
+ }
+}
diff --git a/browser/e2e/tests/e2e.spec.ts b/browser/e2e/tests/e2e.spec.ts
index df9646aac..e6e8846d2 100644
--- a/browser/e2e/tests/e2e.spec.ts
+++ b/browser/e2e/tests/e2e.spec.ts
@@ -65,9 +65,9 @@ test.describe('data-browser', async () => {
// Sign out
await openAgentPage(page);
await page.click('[data-test="sign-out"]');
- await expect(page.locator('text=Enter your Agent secret')).toBeVisible();
+ await expect(page.locator('text=Enter your Secret')).toBeVisible();
await page.reload();
- await expect(page.locator('text=Enter your Agent secret')).toBeVisible();
+ await expect(page.locator('text=Enter your Secret')).toBeVisible();
});
test('sign up and edit document atomicdata.dev', async ({ page }) => {
diff --git a/browser/lib/src/authentication.ts b/browser/lib/src/authentication.ts
index fcda6adca..84c47b6bd 100644
--- a/browser/lib/src/authentication.ts
+++ b/browser/lib/src/authentication.ts
@@ -1,6 +1,9 @@
-import type { Agent } from './agent.js';
+import { Agent } from './agent.js';
import type { HeadersObject } from './client.js';
-import { getTimestampNow, signToBase64 } from './commit.js';
+import { generateKeyPair, getTimestampNow, signToBase64 } from './commit.js';
+import type { Store } from './store.js';
+import type { Resource } from './resource.js';
+import { core } from './ontologies/core.js';
/** Returns a JSON-AD resource of an Authentication */
export async function createAuthentication(subject: string, agent: Agent) {
@@ -72,6 +75,7 @@ export async function signRequest(
}
const ONE_DAY = 24 * 60 * 60 * 1000;
+const COOKIE_NAME_AUTH = 'atomic_session';
const setCookieExpires = (
name: string,
@@ -88,8 +92,6 @@ const setCookieExpires = (
document.cookie = cookieString;
};
-const COOKIE_NAME_AUTH = 'atomic_session';
-
/** Sets a cookie for the current Agent, signing the Authentication. It expires after some default time. */
export const setCookieAuthentication = (serverURL: string, agent: Agent) => {
createAuthentication(serverURL, agent).then(auth => {
@@ -97,6 +99,12 @@ export const setCookieAuthentication = (serverURL: string, agent: Agent) => {
});
};
+export const removeCookieAuthentication = () => {
+ if (typeof document !== 'undefined') {
+ document.cookie = `${COOKIE_NAME_AUTH}=;Max-Age=-99999999`;
+ }
+};
+
/** Returns false if the auth cookie is not set / expired */
export const checkAuthenticationCookie = (): boolean => {
const matches = document.cookie.match(
@@ -110,6 +118,150 @@ export const checkAuthenticationCookie = (): boolean => {
return matches.length > 0;
};
-export const removeCookieAuthentication = () => {
- document.cookie = `${COOKIE_NAME_AUTH}=;Max-Age=-99999999`;
-};
+/** Only allows lowercase chars and numbers */
+export const nameRegex = '^[a-z0-9_-]+';
+
+export async function serverSupportsRegister(store: Store) {
+ const url = new URL('/register', store.getServerUrl());
+ const resource = await store.getResource(url.toString());
+
+ if (!resource) {
+ return false;
+ }
+
+ if (resource.error) {
+ return false;
+ }
+
+ return true;
+}
+
+/** Run this after making a call to an endpoint. Throws if something went wrong. */
+function checkResourceSuccess(resource?: Resource) {
+ if (!resource) {
+ throw new Error('No resource received');
+ }
+
+ if (resource.error) {
+ throw resource.error;
+ }
+
+ const respName = resource.get(core.properties.name) as string;
+
+ if (!respName.includes('Success')) {
+ throw new Error('Expected a `success` message, did not receive one');
+ }
+}
+
+/** Asks the server to create an Agent + a Drive.
+ * Sends the confirmation email to the user.
+ * Throws if the name is not available or the email is invalid.
+ * The Agent and Drive are only created after the Email is confirmed. */
+export async function register(
+ store: Store,
+ name: string,
+ email: string,
+): Promise {
+ const url = new URL('/register', store.getServerUrl());
+ url.searchParams.set('name', name);
+ url.searchParams.set('email', email);
+ const resource = await store.getResourceAsync(url.toString());
+ checkResourceSuccess(resource);
+
+ return;
+}
+
+/** Asks the server to add a public key to an account. Will lead to a confirmation link being sent */
+export async function addPublicKey(store: Store, email: string): Promise {
+ if (!email) {
+ throw new Error('No email provided');
+ }
+
+ const url = new URL('/add-public-key', store.getServerUrl());
+ url.searchParams.set('email', email);
+ const resource = await store.getResourceAsync(url.toString());
+ checkResourceSuccess(resource);
+
+ return;
+}
+
+/** When the user receives a confirmation link, call this function with the provided URL.
+ * If there is no agent in the store, a new one will be created. */
+export async function confirmEmail(
+ store: Store,
+ /** Full http URL including the `token` query parameter */
+ tokenURL: string,
+): Promise<{ agent: Agent; destination: string }> {
+ const url = new URL(tokenURL);
+ const token = url.searchParams.get('token');
+
+ if (!token) {
+ throw new Error('No token provided');
+ }
+
+ const parsed = parseJwt(token);
+
+ if (!parsed.name || !parsed.email) {
+ throw new Error('token does not contain name or email');
+ }
+
+ let agent = store.getAgent();
+
+ // No agent, create a new one
+ if (!agent) {
+ const keypair = await generateKeyPair();
+ const newAgent = new Agent(keypair.privateKey);
+ newAgent.subject = `${store.getServerUrl()}/agents/${parsed.name}`;
+ agent = newAgent;
+ }
+
+ // An agent already exists, make sure it matches the confirm email token
+ if (!agent?.subject?.includes(parsed.name)) {
+ throw new Error(
+ 'You cannot confirm this email, you are already logged in as a different user',
+ );
+ }
+
+ url.searchParams.set('public-key', await agent.getPublicKey());
+ const resource = await store.getResource(url.toString());
+
+ if (!resource) {
+ throw new Error('no resource!');
+ }
+
+ if (resource.error) {
+ throw resource.error;
+ }
+
+ const destination = resource.get(
+ 'https://atomicdata.dev/properties/destination',
+ ) as string;
+
+ if (!destination) {
+ throw new Error('No redirect destination in response');
+ }
+
+ store.setAgent(agent);
+
+ return { agent, destination };
+}
+
+function parseJwt(token: string) {
+ try {
+ const base64Url = token.split('.')[1];
+ const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
+ const jsonPayload = decodeURIComponent(
+ window
+ .atob(base64)
+ .split('')
+ .map(function (c) {
+ return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
+ })
+ .join(''),
+ );
+
+ return JSON.parse(jsonPayload);
+ } catch (e) {
+ throw new Error('Invalid token: ' + e);
+ }
+}
diff --git a/browser/lib/src/resource.ts b/browser/lib/src/resource.ts
index 5b1602957..679f65b74 100644
--- a/browser/lib/src/resource.ts
+++ b/browser/lib/src/resource.ts
@@ -1,3 +1,4 @@
+import { commits } from './ontologies/commits.js';
import { EventManager } from './EventManager.js';
import type { Agent } from './agent.js';
import { Client } from './client.js';
@@ -415,19 +416,25 @@ export class Resource {
public async getHistory(
progressCallback?: (percentage: number) => void,
): Promise {
- const commitsCollection = await this.store.fetchResourceFromServer(
- this.getCommitsCollectionSubject(),
- );
- const commits = commitsCollection.get(
- properties.collection.members,
- ) as string[];
+ const commitsCollection = await new CollectionBuilder(this.store)
+ .setPageSize(9999)
+ .setProperty(commits.properties.subject)
+ .setValue(this.subject)
+ .setSortBy(commits.properties.createdAt)
+ .buildAndFetch();
+
+ const commitSubjects = await commitsCollection.getAllMembers();
const builtVersions: Version[] = [];
+ if (!commitSubjects) {
+ return builtVersions;
+ }
+
let previousResource = new Resource(this.subject);
- for (let i = 0; i < commits.length; i++) {
- const commitResource = await this.store.getResource(commits[i]);
+ for (let i = 0; i < commitSubjects.length; i++) {
+ const commitResource = await this.store.getResource(commitSubjects[i]);
const parsedCommit = parseCommitResource(commitResource);
const builtResource = applyCommitToResource(
previousResource.clone(),
@@ -441,7 +448,7 @@ export class Resource {
// Every 30 cycles we report the progress
if (progressCallback && i % 30 === 0) {
- progressCallback(Math.round((i / commits.length) * 100));
+ progressCallback(Math.round((i / commitSubjects.length) * 100));
await WaitForImmediate();
}
}
diff --git a/browser/lib/src/store.ts b/browser/lib/src/store.ts
index 1834ad79f..646ffbe6b 100644
--- a/browser/lib/src/store.ts
+++ b/browser/lib/src/store.ts
@@ -2,6 +2,7 @@ import { ulid } from 'ulidx';
import type { Agent } from './agent.js';
import {
removeCookieAuthentication,
+ serverSupportsRegister,
setCookieAuthentication,
} from './authentication.js';
import { Client, type FileOrFileLike } from './client.js';
@@ -105,6 +106,10 @@ export interface ResourceTreeTemplate {
/** Returns True if the client has WebSocket support */
const supportsWebSockets = () => typeof WebSocket !== 'undefined';
+export type ServerSupports = {
+ emailRegister: boolean;
+};
+
/**
* An in memory store that has a bunch of usefful methods for retrieving Atomic
* Data Resources. It is also resposible for keeping the Resources in sync with
@@ -752,6 +757,13 @@ export class Store {
}
}
+ /** Checks which features the current Server instance supports */
+ public async getServerSupports(): Promise {
+ return {
+ emailRegister: await serverSupportsRegister(this),
+ };
+ }
+
/**
* Registers a callback for when the a resource is updated. When you call
* this
diff --git a/browser/lib/src/urls.ts b/browser/lib/src/urls.ts
index 4f936fa3e..cb9695979 100644
--- a/browser/lib/src/urls.ts
+++ b/browser/lib/src/urls.ts
@@ -70,6 +70,10 @@ export const properties = {
write: 'https://atomicdata.dev/properties/write',
displayStyle: 'https://atomicdata.dev/property/display-style',
publishedAt: 'https://atomicdata.dev/properties/published-at',
+ article: {
+ publishedAt: 'https://atomicdata.dev/properties/published-at',
+ tags: 'https://atomicdata.dev/properties/tags',
+ },
agent: {
publicKey: 'https://atomicdata.dev/properties/publicKey',
},
diff --git a/browser/react/src/index.ts b/browser/react/src/index.ts
index 5b2a6069f..dc7639124 100644
--- a/browser/react/src/index.ts
+++ b/browser/react/src/index.ts
@@ -33,4 +33,5 @@ export * from './useCollection.js';
export * from './useMemberFromCollection.js';
export * from './useCollectionPage.js';
export * from './components/Image.js';
+export * from './useServerSupports.js';
export * from '@tomic/lib';
diff --git a/browser/react/src/useServerSupports.ts b/browser/react/src/useServerSupports.ts
new file mode 100644
index 000000000..ad2695e13
--- /dev/null
+++ b/browser/react/src/useServerSupports.ts
@@ -0,0 +1,21 @@
+import { ServerSupports, useServerURL, useStore } from './index.js';
+import { useEffect, useState } from 'react';
+
+export function useServerSupports(): ServerSupports {
+ const store = useStore();
+ const serverURL = useServerURL();
+ const [supports, setSupports] = useState({
+ emailRegister: true,
+ });
+
+ useEffect(() => {
+ console.log('useEffect');
+ // async function check() {
+ // const res = await store.getServerSupports();
+ // setSupports(res);
+ // }
+ // check();
+ }, [store, serverURL]);
+
+ return supports;
+}
diff --git a/cli/src/commit.rs b/cli/src/commit.rs
index 1251a0721..88bf8ee13 100644
--- a/cli/src/commit.rs
+++ b/cli/src/commit.rs
@@ -8,7 +8,7 @@ pub fn set(context: &Context, subject: &str, property: &str, value: &str) -> Ato
Ok(r) => r,
Err(_) => atomic_lib::Resource::new(subject.into()),
};
- resource.set_shortname(&property, &value, &context.store)?;
+ resource.set_shortname(property, value, &context.store)?;
resource.save(&context.store)?;
Ok(())
}
@@ -17,19 +17,19 @@ pub fn set(context: &Context, subject: &str, property: &str, value: &str) -> Ato
#[cfg(feature = "native")]
pub fn edit(context: &Context, subject: &str, prop: &str) -> AtomicResult<()> {
// If the resource is not found, create it
- let mut resource = match context.store.get_resource(&subject) {
+ let mut resource = match context.store.get_resource(subject) {
Ok(r) => r,
Err(_) => atomic_lib::Resource::new(subject.into()),
};
// If the prop is not found, create it
- let current_val = match resource.get_shortname(&prop, &context.store) {
+ let current_val = match resource.get_shortname(prop, &context.store) {
Ok(val) => val.to_string(),
Err(_) => "".to_string(),
};
let edited = edit::edit(current_val)?;
// Remove newline - or else I can's save shortnames or numbers using vim;
let trimmed = edited.trim_end_matches('\n');
- resource.set_shortname(&prop, trimmed, &context.store)?;
+ resource.set_shortname(prop, trimmed, &context.store)?;
resource.save(&context.store)?;
Ok(())
}
@@ -37,7 +37,7 @@ pub fn edit(context: &Context, subject: &str, prop: &str) -> AtomicResult<()> {
/// Apply a Commit using the Remove method - removes a property from a resource
pub fn remove(context: &Context, subject: &str, prop: &str) -> AtomicResult<()> {
let mut resource = context.store.get_resource(subject)?;
- resource.remove_propval_shortname(&prop, &context.store)?;
+ resource.remove_propval_shortname(prop, &context.store)?;
resource.save(&context.store)?;
Ok(())
}
diff --git a/cli/src/main.rs b/cli/src/main.rs
index 756432f5c..7e2b15982 100644
--- a/cli/src/main.rs
+++ b/cli/src/main.rs
@@ -113,9 +113,9 @@ pub enum SerializeOptions {
NTriples,
}
-impl Into for SerializeOptions {
- fn into(self) -> Format {
- match self {
+impl From<&SerializeOptions> for Format {
+ fn from(val: &SerializeOptions) -> Self {
+ match val {
SerializeOptions::Pretty => Format::Pretty,
SerializeOptions::Json => Format::Json,
SerializeOptions::NTriples => Format::NTriples,
@@ -165,7 +165,7 @@ fn set_agent_config() -> CLIResult {
"No config found at {:?}. Let's create one!",
&agent_config_path
);
- let server = promptly::prompt("What's the base url of your Atomic Server?")?;
+ let server: String = promptly::prompt("What's the base url of your Atomic Server?")?;
let agent = promptly::prompt("What's the URL of your Agent?")?;
let private_key = promptly::prompt("What's the private key of this Agent?")?;
let config = atomic_lib::config::Config {
diff --git a/cli/src/path.rs b/cli/src/path.rs
index 9d7a5fdce..145677a7b 100644
--- a/cli/src/path.rs
+++ b/cli/src/path.rs
@@ -4,7 +4,7 @@ use atomic_lib::{agents::ForAgent, errors::AtomicResult, serialize, storelike, A
/// Resolves an Atomic Path query
pub fn get_path(
context: &mut Context,
- path_vec: &Vec,
+ path_vec: &[String],
serialize: &SerializeOptions,
) -> AtomicResult<()> {
// let subcommand_matches = context.matches.subcommand_matches("get").unwrap();
diff --git a/cli/src/print.rs b/cli/src/print.rs
index a05f55276..83a568f13 100644
--- a/cli/src/print.rs
+++ b/cli/src/print.rs
@@ -7,7 +7,7 @@ use colored::*;
use crate::{Context, SerializeOptions};
-/// Prints a resource for the terminal with readble formatting and colors
+/// Prints a resource for the terminal with readable formatting and colors
pub fn pretty_print_resource(resource: &Resource, store: &impl Storelike) -> AtomicResult {
let mut output = String::new();
output.push_str(&format!(
@@ -32,7 +32,7 @@ pub fn print_resource(
resource: &Resource,
serialize: &SerializeOptions,
) -> AtomicResult<()> {
- let format: Format = serialize.clone().into();
+ let format: Format = serialize.into();
let out = match format {
Format::Json => resource.to_json(&context.store)?,
Format::JsonLd => resource.to_json_ld(&context.store)?,
diff --git a/docs/src/atomicserver/faq.md b/docs/src/atomicserver/faq.md
index a2467a372..e2e0b1e61 100644
--- a/docs/src/atomicserver/faq.md
+++ b/docs/src/atomicserver/faq.md
@@ -8,12 +8,14 @@
No, AtomicServer has its own HTTPS support. Just pass a `--https` flag!
-## Can / should I create backups?
+## Can I create backups?
-You should.
-Run `atomic-server export` to create a JSON-AD backup in your `~/.config/atomic/backups` folder.
+There are two ways you can create backups:
+
+1. **Export the JSON-AD**. Run `atomic-server export` to create a JSON-AD backup in your `~/.config/atomic/backups` folder.
Import them using `atomic-server import -p ~/.config/atomic/backups/${date}.json`.'
You could also copy all folders `atomic-server` uses. To see what these are, see `atomic-server show-config`.
+1. **Backup the database file**. use `atomic-server show-config` to find the `store_path` and copy the path to some place where you store the backup.
## I lost the key / secret to my Root Agent, and the `/setup` invite is no longer usable! What now?
@@ -26,7 +28,7 @@ This could especially be helpful if you're running at `localhost:9883` and want
## How do I reset my database?
-`atomic-server reset`
+`atomic-server reset`. This deletes all of your data. Be careful!
## How do I make my data private, yet available online?
diff --git a/docs/src/react/useStore.md b/docs/src/react/useStore.md
index f14ce5150..9db6812ad 100644
--- a/docs/src/react/useStore.md
+++ b/docs/src/react/useStore.md
@@ -23,10 +23,10 @@ export const Login = () => {
return (
- Agent Secret
+ Secret
setAgentSecret(e.target.value)}
/>
diff --git a/lib/Cargo.toml b/lib/Cargo.toml
index ac777b038..4ae341025 100644
--- a/lib/Cargo.toml
+++ b/lib/Cargo.toml
@@ -15,6 +15,9 @@ harness = false
name = "benchmarks"
# path = "benches/benchmarks.rs"
+[package.metadata.docs.rs]
+all-features = true
+
[dependencies]
base64 = "0.21"
bincode = { version = "1", optional = true }
@@ -23,6 +26,7 @@ html2md = { version = "0.2.14", optional = true }
kuchikiki = { version = "0.8.2", optional = true }
lol_html = { version = "1", optional = true }
rand = { version = "0.8" }
+lazy_static = "1"
regex = "1"
ring = "0.17.6"
rio_api = { version = "0.8", optional = true }
@@ -37,6 +41,10 @@ ureq = "2"
url = "2"
urlencoding = "2"
ulid = "1.1.3"
+mail-send = {version= "0.2", optional = true}
+async-mutex = {version = "1.4.0", optional = true}
+tokio = {version = "1.22.0", optional = true, features = ["full"] }
+jwt-simple = {version = "0.11.2", optional = true}
[dev-dependencies]
criterion = "0.5"
@@ -46,6 +54,9 @@ ntest = "0.9"
[features]
config = ["directories", "toml"]
-db = ["sled", "bincode"]
html = ["kuchikiki", "lol_html", "html2md"]
+db = ["sled", "bincode", "mail-send", "tokio", "async-mutex", "jwt-simple"]
rdf = ["rio_api", "rio_turtle"]
+
+[profile.dev]
+incremental = true
diff --git a/lib/benches/benchmarks.rs b/lib/benches/benchmarks.rs
index 2218d802a..524461c68 100644
--- a/lib/benches/benchmarks.rs
+++ b/lib/benches/benchmarks.rs
@@ -37,7 +37,7 @@ fn random_resource(atom: &Atom) -> Resource {
}
fn criterion_benchmark(c: &mut Criterion) {
- let store = Db::init_temp("bench").unwrap();
+ let store = Db::init_temp().unwrap();
c.bench_function("add_resource", |b| {
b.iter(|| {
diff --git a/lib/defaults/default_base_models.json b/lib/defaults/default_base_models.json
index 4731f33d4..f5262cbef 100644
--- a/lib/defaults/default_base_models.json
+++ b/lib/defaults/default_base_models.json
@@ -78,17 +78,23 @@
},
{
"@id": "https://atomicdata.dev/classes/Property",
- "https://atomicdata.dev/properties/description": "A Resource that should redirect the browser to a new location. It can also set a `redirectAgent`, which is used in Invites to create an Agent Resource on the Server from a Public Key that the user posesses. See the [Invite docs](https://docs.atomicdata.dev/invitations.html).",
+ "https://atomicdata.dev/properties/description": "A Property is a single field in a Class. It's the thing that a property field in an Atom points to. An example is `birthdate`. An instance of Property requires various Properties, most notably a `datatype` (e.g. `string` or `integer`), a human readable `description` (such as the thing you're reading), and a `shortname`.",
"https://atomicdata.dev/properties/isA": [
"https://atomicdata.dev/classes/Class"
],
- "https://atomicdata.dev/properties/requires": [
- "https://atomicdata.dev/properties/destination"
- ],
+ "https://atomicdata.dev/properties/parent": "https://atomicdata.dev/classes",
"https://atomicdata.dev/properties/recommends": [
- "https://atomicdata.dev/properties/invite/redirectAgent"
+ "https://atomicdata.dev/properties/classtype",
+ "https://atomicdata.dev/properties/isDynamic",
+ "https://atomicdata.dev/properties/isLocked",
+ "https://atomicdata.dev/properties/allowsOnly"
+ ],
+ "https://atomicdata.dev/properties/requires": [
+ "https://atomicdata.dev/properties/shortname",
+ "https://atomicdata.dev/properties/datatype",
+ "https://atomicdata.dev/properties/description"
],
- "https://atomicdata.dev/properties/shortname": "redirect"
+ "https://atomicdata.dev/properties/shortname": "property"
},
{
"@id": "https://atomicdata.dev/classes/Class",
diff --git a/lib/defaults/default_store.json b/lib/defaults/default_store.json
index d2e25c1b1..341c7e66c 100644
--- a/lib/defaults/default_store.json
+++ b/lib/defaults/default_store.json
@@ -525,11 +525,17 @@
"@id": "https://atomicdata.dev/properties/localId",
"https://atomicdata.dev/properties/datatype": "https://atomicdata.dev/datatypes/string",
"https://atomicdata.dev/properties/description": "A `localId` is used to derive the Subject (`@id`, the URL) of an imported Resource. This is useful when some data owner has Atomic Data but does not have the technology to host all Resources at their respective URLs. [See docs](https://docs.atomicdata.dev/create-json-ad.html#preventing-duplication-with-localid).",
+ "https://atomicdata.dev/properties/shortname": "local-id"
+ },
+ {
+ "@id": "https://atomicdata.dev/properties/email",
+ "https://atomicdata.dev/properties/datatype": "https://atomicdata.dev/datatypes/string",
+ "https://atomicdata.dev/properties/description": "Primary email address ",
"https://atomicdata.dev/properties/isA": [
"https://atomicdata.dev/classes/Property"
],
"https://atomicdata.dev/properties/parent": "https://atomicdata.dev/properties",
- "https://atomicdata.dev/properties/shortname": "local-id"
+ "https://atomicdata.dev/properties/shortname": "email"
},
{
"@id": "https://atomicdata.dev/properties/incomplete",
@@ -808,7 +814,6 @@
"https://atomicdata.dev/properties/isA": [
"https://atomicdata.dev/classes/Property"
],
- "https://atomicdata.dev/properties/lastCommit": "https://atomicdata.dev/commits/VB3gtWMkysTX5hKjbYjIM1hfVGPywT3pEPL8c7NwaUAJID6RzptGRPzmix8aKKDeb8Pj1WFv0UPV0YVPxcduBg==",
"https://atomicdata.dev/properties/parent": "https://atomicdata.dev/properties",
"https://atomicdata.dev/properties/shortname": "sub-resources"
},
@@ -820,10 +825,19 @@
"https://atomicdata.dev/properties/isA": [
"https://atomicdata.dev/classes/Property"
],
- "https://atomicdata.dev/properties/lastCommit": "https://atomicdata.dev/commits/fS0krtm1wDk0lodH0psnUKmBHBMKLuxnjkd7E7QbkzDk/irQ43gNW3lWxkwQj58ZNg6rUAUMDGJrLy1X3cHwBQ==",
"https://atomicdata.dev/properties/parent": "https://atomicdata.dev/properties",
"https://atomicdata.dev/properties/shortname": "tags"
},
+ {
+ "@id": "https://atomicdata.dev/properties/token",
+ "https://atomicdata.dev/properties/datatype": "https://atomicdata.dev/datatypes/string",
+ "https://atomicdata.dev/properties/description": "A server-generated string that should not mean anything to the client. It could be a JWT token, or something else.",
+ "https://atomicdata.dev/properties/isA": [
+ "https://atomicdata.dev/classes/Property"
+ ],
+ "https://atomicdata.dev/properties/parent": "https://atomicdata.dev/properties",
+ "https://atomicdata.dev/properties/shortname": "token"
+ },
{
"@id": "https://atomicdata.dev/properties/write",
"https://atomicdata.dev/properties/classtype": "https://atomicdata.dev/classes/Agent",
diff --git a/lib/src/agents.rs b/lib/src/agents.rs
index fcf5facae..a353e4b9d 100644
--- a/lib/src/agents.rs
+++ b/lib/src/agents.rs
@@ -5,7 +5,7 @@
use base64::{engine::general_purpose, Engine};
use serde_json::from_slice;
-use crate::{errors::AtomicResult, urls, Resource, Storelike, Value};
+use crate::{errors::AtomicResult, urls, Query, Resource, Storelike, Value};
/// None represents no right checks will be performed, effectively SUDO mode.
#[derive(Clone, Debug, PartialEq)]
@@ -81,11 +81,46 @@ impl Agent {
Ok(resource)
}
+ pub fn from_email(email: &str, store: &impl Storelike) -> AtomicResult {
+ let mut query = Query::new();
+ query.property = Some(urls::EMAIL.into());
+ query.value = Some(Value::String(email.to_string()));
+ let response = store.query(&query)?;
+ if response.resources.is_empty() {
+ return Err(format!("Agent with Email {} not found", email).into());
+ }
+ if response.resources.len() > 1 {
+ return Err(format!(
+ "Email {} is not unique, {} agents have this email",
+ email, response.count
+ )
+ .into());
+ }
+ let resource = response.resources.first().unwrap();
+ Agent::from_resource(resource.clone())
+ }
+
+ pub fn from_resource(resource: Resource) -> AtomicResult {
+ let name = if let Ok(name) = resource.get(urls::NAME) {
+ Some(name.to_string())
+ } else {
+ None
+ };
+
+ return Ok(Self {
+ created_at: resource.get(urls::CREATED_AT)?.to_int()?,
+ name,
+ public_key: resource.get(urls::PUBLIC_KEY)?.to_string(),
+ private_key: None,
+ subject: resource.get_subject().into(),
+ });
+ }
+
/// Creates a new Agent, generates a new Keypair.
pub fn new(name: Option<&str>, store: &impl Storelike) -> AtomicResult {
let keypair = generate_keypair()?;
- Ok(Agent::new_from_private_key(name, store, &keypair.private))
+ Agent::new_from_private_key(name, store, &keypair.private)
}
/// Creates a new Agent on this server, using the server's Server URL.
@@ -94,16 +129,21 @@ impl Agent {
name: Option<&str>,
store: &impl Storelike,
private_key: &str,
- ) -> Agent {
+ ) -> AtomicResult {
let keypair = generate_public_key(private_key);
+ let subject = store
+ .get_server_url()
+ .url()
+ .join(&format!("agents/{}", &keypair.public))?
+ .to_string();
- Agent {
+ Ok(Agent {
private_key: Some(keypair.private),
- public_key: keypair.public.clone(),
- subject: format!("{}/agents/{}", store.get_server_url(), keypair.public),
+ public_key: keypair.public,
+ subject,
name: name.map(|x| x.to_owned()),
created_at: crate::utils::now(),
- }
+ })
}
/// Creates a new Agent on this server, using the server's Server URL.
@@ -114,7 +154,11 @@ impl Agent {
Ok(Agent {
private_key: None,
public_key: public_key.into(),
- subject: format!("{}/agents/{}", store.get_server_url(), public_key),
+ subject: store
+ .get_server_url()
+ .clone()
+ .set_path(&format!("agents/{}", public_key))
+ .to_string(),
name: None,
created_at: crate::utils::now(),
})
diff --git a/lib/src/atomic_url.rs b/lib/src/atomic_url.rs
new file mode 100644
index 000000000..778c89590
--- /dev/null
+++ b/lib/src/atomic_url.rs
@@ -0,0 +1,196 @@
+use serde::{Deserialize, Serialize, Serializer};
+use url::Url;
+
+use crate::{errors::AtomicResult, utils::random_string};
+
+pub enum Routes {
+ Agents,
+ AllVersions,
+ Collections,
+ Commits,
+ CommitsUnsigned,
+ Endpoints,
+ Import,
+ Tpf,
+ Version,
+ Setup,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+/// Wrapper for URLs / subjects.
+/// Has a bunch of methods for finding or creating commonly used paths.
+pub struct AtomicUrl {
+ url: Url,
+}
+
+impl AtomicUrl {
+ pub fn new(url: Url) -> Self {
+ Self { url }
+ }
+
+ pub fn as_str(&self) -> &str {
+ self.url.as_str()
+ }
+
+ /// Returns the route to some common Endpoint
+ pub fn set_route(&self, route: Routes) -> Self {
+ let path = match route {
+ Routes::AllVersions => "/all-versions".to_string(),
+ Routes::Agents => "/collections/agents".to_string(),
+ Routes::Collections => "/collections".to_string(),
+ Routes::Commits => "/collections/commits".to_string(),
+ Routes::CommitsUnsigned => "/commits-unsigned".to_string(),
+ Routes::Endpoints => "/endpoints".to_string(),
+ Routes::Import => "/import".to_string(),
+ Routes::Tpf => "/tpf".to_string(),
+ Routes::Version => "/version".to_string(),
+ Routes::Setup => "/setup".to_string(),
+ };
+ let mut new = self.url.clone();
+ new.set_path(&path);
+ Self::new(new)
+ }
+
+ /// Returns a new URL generated from the provided path_shortname and a random string.
+ /// ```
+ /// let url = atomic_lib::AtomicUrl::try_from("https://example.com").unwrap();
+ /// let generated = url.generate_random("my-type");
+ /// assert!(generated.to_string().starts_with("https://example.com/my-type/"));
+ /// ```
+ pub fn generate_random(&self, path_shortname: &str) -> Self {
+ let mut url = self.url.clone();
+ let path = format!("{path_shortname}/{}", random_string(10));
+ url.set_path(&path);
+ Self { url }
+ }
+
+ /// Adds a sub-path to a URL.
+ /// Adds a slash to the existing URL, followed by the passed path.
+ ///
+ /// ```
+ /// use atomic_lib::AtomicUrl;
+ /// let start = "http://localhost";
+ /// let mut url = AtomicUrl::try_from(start).unwrap();
+ /// assert_eq!(url.to_string(), "http://localhost/");
+ /// url.append("/");
+ /// assert_eq!(url.to_string(), "http://localhost/");
+ /// url.append("someUrl/123");
+ /// assert_eq!(url.to_string(), "http://localhost/someUrl/123");
+ /// url.append("/345");
+ /// assert_eq!(url.to_string(), "http://localhost/someUrl/123/345");
+ /// ```
+ pub fn append(&mut self, path: &str) -> &Self {
+ let mut new_path = self.url.path().to_string();
+ match (new_path.ends_with('/'), path.starts_with('/')) {
+ (true, true) => {
+ new_path.pop();
+ }
+ (false, false) => new_path.push('/'),
+ _other => {}
+ };
+
+ // Remove first slash if it exists
+ if new_path.starts_with('/') {
+ new_path.remove(0);
+ }
+
+ new_path.push_str(path);
+
+ self.url.set_path(&new_path);
+ self
+ }
+
+ /// Sets the subdomain of the URL.
+ /// Removes an existing subdomain if the subdomain is None
+ pub fn set_subdomain(&mut self, subdomain: Option<&str>) -> AtomicResult<&Self> {
+ let mut host = self.url.host_str().unwrap().to_string();
+ if let Some(subdomain) = subdomain {
+ host = format!("{}.{}", subdomain, host);
+ }
+ self.url.set_host(Some(host.as_str()))?;
+ Ok(self)
+ }
+
+ /// Removes existing path, sets the new one. Escapes special characters
+ pub fn set_path(mut self, path: &str) -> Self {
+ self.url.set_path(path);
+ self
+ }
+
+ pub fn subdomain(&self) -> Option {
+ let url = self.url.clone();
+ let host = url.host_str().unwrap();
+ let parts: Vec<&str> = host.split('.').collect();
+ if parts.len() > 2 {
+ Some(parts[0].to_string())
+ } else {
+ None
+ }
+ }
+
+ /// Returns the inner {url::Url} struct that has a bunch of regular URL methods
+ /// Useful if you need the host or something.
+ pub fn url(&self) -> Url {
+ self.url.clone()
+ }
+}
+
+impl TryFrom<&str> for AtomicUrl {
+ type Error = url::ParseError;
+
+ fn try_from(value: &str) -> Result {
+ let url = Url::parse(value)?;
+ Ok(Self { url })
+ }
+}
+
+impl Serialize for AtomicUrl {
+ fn serialize(&self, serializer: S) -> Result
+ where
+ S: Serializer,
+ {
+ serializer.serialize_str(self.url.as_str())
+ }
+}
+
+impl<'de> Deserialize<'de> for AtomicUrl {
+ fn deserialize(deserializer: D) -> Result
+ where
+ D: serde::Deserializer<'de>,
+ {
+ let s = String::deserialize(deserializer)?;
+ let url = Url::parse(&s).map_err(serde::de::Error::custom)?;
+ Ok(Self { url })
+ }
+}
+
+impl std::fmt::Display for AtomicUrl {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", self.url)
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn test_url() {
+ let _should_fail = AtomicUrl::try_from("nonsense").unwrap_err();
+ let _should_succeed = AtomicUrl::try_from("http://localhost/someUrl").unwrap();
+ }
+
+ #[test]
+ fn subdomain() {
+ let sub = "http://test.example.com";
+ assert_eq!(
+ AtomicUrl::try_from(sub).unwrap().subdomain(),
+ Some("test".to_string())
+ );
+ let mut no_sub = AtomicUrl::try_from("http://example.com").unwrap();
+ assert_eq!(no_sub.subdomain(), None);
+
+ let to_sub = no_sub.set_subdomain(Some("mysub")).unwrap();
+ assert_eq!(to_sub.subdomain(), Some("mysub".to_string()));
+ }
+}
diff --git a/lib/src/authentication.rs b/lib/src/authentication.rs
index 407fc4ee2..692ecdb88 100644
--- a/lib/src/authentication.rs
+++ b/lib/src/authentication.rs
@@ -41,7 +41,7 @@ pub fn check_auth_signature(subject: &str, auth_header: &AuthValues) -> AtomicRe
.verify(message.as_bytes(), &signature_bytes)
.map_err(|_e| {
format!(
- "Incorrect signature for auth headers. This could be due to an error during signing or serialization of the commit. Compare this to the serialized message in the client: {}",
+ "Incorrect signature for auth headers. This could be due to an error during signing or serialization of the commit. Compare this to the serialized message in the client: '{}'",
message,
)
})?;
diff --git a/lib/src/client/helpers.rs b/lib/src/client/helpers.rs
index 5122cf2d3..ee7ec6a64 100644
--- a/lib/src/client/helpers.rs
+++ b/lib/src/client/helpers.rs
@@ -51,6 +51,7 @@ pub fn fetch_body(
content_type: &str,
client_agent: Option<&Agent>,
) -> AtomicResult {
+ println!("Fetching body from {}", url);
if !url.starts_with("http") {
return Err(format!("Could not fetch url '{}', must start with http.", url).into());
}
@@ -59,7 +60,7 @@ pub fn fetch_body(
}
let agent = ureq::builder()
- .timeout(std::time::Duration::from_secs(2))
+ .timeout(std::time::Duration::from_secs(10))
.build();
let resp = agent
.get(url)
diff --git a/lib/src/collections.rs b/lib/src/collections.rs
index fa52713c0..64e654cd0 100644
--- a/lib/src/collections.rs
+++ b/lib/src/collections.rs
@@ -1,10 +1,8 @@
//! Collections are dynamic resources that refer to multiple resources.
//! They are constructed using a [Query]
use crate::{
- agents::ForAgent,
- errors::AtomicResult,
- storelike::{Query, ResourceCollection},
- urls, Resource, Storelike, Value,
+ agents::ForAgent, errors::AtomicResult, storelike::ResourceCollection, urls, Query, Resource,
+ Storelike, Value,
};
const DEFAULT_PAGE_SIZE: usize = 30;
@@ -88,7 +86,7 @@ impl CollectionBuilder {
store: &impl Storelike,
) -> CollectionBuilder {
CollectionBuilder {
- subject: format!("{}/{}", store.get_server_url(), path),
+ subject: store.get_server_url().clone().set_path(path).to_string(),
property: Some(urls::IS_A.into()),
value: Some(class_url.into()),
sort_by: None,
@@ -387,6 +385,10 @@ pub fn create_collection_resource_for_class(
) -> AtomicResult {
let class = store.get_class(class_subject)?;
+ // We use the `Collections` collection as the parent for all collections.
+ // This also influences their URLs.
+ let is_collections_collection = class.subject == urls::COLLECTION;
+
// Pluralize the shortname
let pluralized = match class.shortname.as_ref() {
"class" => "classes".to_string(),
@@ -394,7 +396,13 @@ pub fn create_collection_resource_for_class(
other => format!("{}s", other),
};
- let mut collection = CollectionBuilder::class_collection(&class.subject, &pluralized, store);
+ let path = if is_collections_collection {
+ "/collections".to_string()
+ } else {
+ format!("/collections/{}", pluralized)
+ };
+
+ let mut collection = CollectionBuilder::class_collection(&class.subject, &path, store);
collection.sort_by = match class_subject {
urls::COMMIT => Some(urls::CREATED_AT.to_string()),
@@ -415,10 +423,12 @@ pub fn create_collection_resource_for_class(
.ok_or("No self_url present in store, can't populate collections")?;
// Let the Collections collection be the top level item
- let parent = if class.subject == urls::COLLECTION {
- drive
+ let parent = if is_collections_collection {
+ drive.to_string()
} else {
- format!("{}/collections", drive)
+ drive
+ .set_route(crate::atomic_url::Routes::Collections)
+ .to_string()
};
collection_resource.set_string(urls::PARENT.into(), &parent, store)?;
@@ -524,7 +534,7 @@ mod test {
println!("{:?}", subjects);
let collections_collection = store
.get_resource_extended(
- &format!("{}/collections", store.get_server_url()),
+ &format!("{}collections", store.get_server_url()),
false,
&ForAgent::Public,
)
diff --git a/lib/src/commit.rs b/lib/src/commit.rs
index 9470bdf0d..d4583b0e7 100644
--- a/lib/src/commit.rs
+++ b/lib/src/commit.rs
@@ -6,6 +6,7 @@ use urls::{SET, SIGNER};
use crate::{
agents::{decode_base64, encode_base64},
+ atomic_url::Routes,
datatype::DataType,
errors::AtomicResult,
resources::PropVals,
@@ -240,7 +241,7 @@ impl Commit {
let default_parent = store.get_self_url().ok_or("There is no self_url set, and no parent in the Commit. The commit can not be applied.")?;
resource_old.set(
urls::PARENT.into(),
- Value::AtomicUrl(default_parent),
+ Value::AtomicUrl(default_parent.to_string()),
store,
)?;
}
@@ -429,10 +430,18 @@ impl Commit {
#[tracing::instrument(skip(store))]
pub fn into_resource(&self, store: &impl Storelike) -> AtomicResult {
let commit_subject = match self.signature.as_ref() {
- Some(sig) => format!("{}/commits/{}", store.get_server_url(), sig),
+ Some(sig) => store
+ .get_server_url()
+ .set_route(Routes::Commits)
+ .append(sig)
+ .to_string(),
None => {
let now = crate::utils::now();
- format!("{}/commitsUnsigned/{}", store.get_server_url(), now)
+ store
+ .get_server_url()
+ .set_route(Routes::CommitsUnsigned)
+ .append(&now.to_string())
+ .to_string()
}
};
let mut resource = Resource::new_instance(urls::COMMIT, store)?;
@@ -742,10 +751,10 @@ mod test {
let private_key = "CapMWIhFUT+w7ANv9oCPqrHrwZpkP2JhzF9JnyT6WcI=";
let store = crate::Store::init().unwrap();
store.populate().unwrap();
- let agent = Agent::new_from_private_key(None, &store, private_key);
+ let agent = Agent::new_from_private_key(None, &store, private_key).unwrap();
assert_eq!(
&agent.subject,
- "local:store/agents/7LsjMW5gOfDdJzK/atgjQ1t20J/rw8MjVg6xwqm+h8U="
+ "http://noresolve.localhost/agents/7LsjMW5gOfDdJzK/atgjQ1t20J/rw8MjVg6xwqm+h8U="
);
store.add_resource(&agent.to_resource().unwrap()).unwrap();
let subject = "https://localhost/new_thing";
@@ -760,8 +769,8 @@ mod test {
let signature = commit.signature.clone().unwrap();
let serialized = commit.serialize_deterministically_json_ad(&store).unwrap();
- assert_eq!(serialized, "{\"https://atomicdata.dev/properties/createdAt\":0,\"https://atomicdata.dev/properties/isA\":[\"https://atomicdata.dev/classes/Commit\"],\"https://atomicdata.dev/properties/set\":{\"https://atomicdata.dev/properties/description\":\"Some value\",\"https://atomicdata.dev/properties/shortname\":\"someval\"},\"https://atomicdata.dev/properties/signer\":\"local:store/agents/7LsjMW5gOfDdJzK/atgjQ1t20J/rw8MjVg6xwqm+h8U=\",\"https://atomicdata.dev/properties/subject\":\"https://localhost/new_thing\"}");
- assert_eq!(signature, "JOGRyp1NCulc0RNuuNozgIagQPRoZy0Y5+mbSpHY2DKiN3vqUNYLjXbAPYT6Cga6vSG9zztEIa/ZcbQPo7wgBg==");
+ assert_eq!(serialized, "{\"https://atomicdata.dev/properties/createdAt\":0,\"https://atomicdata.dev/properties/isA\":[\"https://atomicdata.dev/classes/Commit\"],\"https://atomicdata.dev/properties/set\":{\"https://atomicdata.dev/properties/description\":\"Some value\",\"https://atomicdata.dev/properties/shortname\":\"someval\"},\"https://atomicdata.dev/properties/signer\":\"http://noresolve.localhost/agents/7LsjMW5gOfDdJzK/atgjQ1t20J/rw8MjVg6xwqm+h8U=\",\"https://atomicdata.dev/properties/subject\":\"https://localhost/new_thing\"}");
+ assert_eq!(signature, "CZbjUJW/tokEKSZTCFjEHWbWqGW+jyhZWYs82K9wt0SArxu9xGg+D3IniAlygQp0F3KcI4Z876th3/X3fJIVAQ==");
}
#[test]
diff --git a/lib/src/db.rs b/lib/src/db.rs
index f64fec2a8..d2b264a0e 100644
--- a/lib/src/db.rs
+++ b/lib/src/db.rs
@@ -1,5 +1,6 @@
//! Persistent, ACID compliant, threadsafe to-disk store.
//! Powered by Sled - an embedded database.
+//! See [Db]
mod migrations;
mod prop_val_sub_index;
@@ -12,28 +13,31 @@ mod val_prop_sub_index;
use std::{
collections::{HashMap, HashSet},
fs,
- sync::{Arc, Mutex},
+ sync::Arc,
vec,
};
-use tracing::{info, instrument};
+use mail_send::{Connected, Transport};
+use tracing::info;
+use tracing::instrument;
use trees::{Method, Operation, Transaction, Tree};
use crate::{
agents::ForAgent,
+ atomic_url::{AtomicUrl, Routes},
atoms::IndexAtom,
commit::{CommitOpts, CommitResponse},
- db::{
- query_index::{requires_query_index, NO_VALUE},
- val_prop_sub_index::find_in_val_prop_sub_index,
- },
- endpoints::{default_endpoints, Endpoint, HandleGetContext},
+ db::{query_index::requires_query_index, val_prop_sub_index::find_in_val_prop_sub_index},
+ email::{self, MailMessage},
+ endpoints::{build_default_endpoints, Endpoint, HandleGetContext},
errors::{AtomicError, AtomicResult},
+ plugins,
+ query::QueryResult,
resources::PropVals,
- storelike::{Query, QueryResult, Storelike},
+ storelike::Storelike,
urls,
values::SortableValue,
- Atom, Commit, Resource,
+ Atom, Commit, Query, Resource, Value,
};
use self::{
@@ -41,7 +45,7 @@ use self::{
prop_val_sub_index::{add_atom_to_prop_val_sub_index, find_in_prop_val_sub_index},
query_index::{
check_if_atom_matches_watched_query_filters, query_sorted_indexed, should_include_resource,
- update_indexed_member, IndexIterator, QueryFilter,
+ update_indexed_member, IndexIterator, QueryFilter, NO_VALUE,
},
val_prop_sub_index::add_atom_to_valpropsub_index,
};
@@ -53,21 +57,22 @@ type HandleCommit = Box;
/// The String on the left represents a Property URL, and the second one is the set of subjects.
pub type PropSubjectMap = HashMap>;
-/// The Db is a persistent on-disk Atomic Data store.
+/// A persistent on-disk Atomic Data store.
/// It's an implementation of [Storelike].
/// It uses [sled::Tree]s as Key Value stores.
/// It stores [Resource]s as [PropVals]s by their subject as key.
/// It builds a value index for performant [Query]s.
/// It keeps track of Queries and updates their index when [crate::Commit]s are applied.
-/// You can pass a custom `on_commit` function to run at Commit time.
/// `Db` should be easily, cheaply clone-able, as users of this library could have one `Db` per connection.
+/// Note that [plugins](crate::plugins) can add their own endpoints to the [Db],
+/// and can use [tokio::spawn] to start concurrent tasks.
#[derive(Clone)]
pub struct Db {
/// The Key-Value store that contains all data.
/// Resources can be found using their Subject.
/// Try not to use this directly, but use the Trees.
db: sled::Db,
- default_agent: Arc>>,
+ default_agent: Arc>>,
/// Stores all resources. The Key is the Subject as a `string.as_bytes()`, the value a [PropVals]. Propvals must be serialized using [bincode].
resources: sled::Tree,
/// [Tree::ValPropSub]
@@ -79,20 +84,22 @@ pub struct Db {
/// [Tree::WatchedQueries]
watched_queries: sled::Tree,
/// The address where the db will be hosted, e.g. http://localhost/
- server_url: String,
+ server_url: AtomicUrl,
/// Endpoints are checked whenever a resource is requested. They calculate (some properties of) the resource and return it.
endpoints: Vec,
- /// Function called whenever a Commit is applied.
- on_commit: Option>,
/// Where the DB is stored on disk.
path: std::path::PathBuf,
+ /// Function called whenever a Commit is applied.
+ handle_commit: Option>,
+ /// Email SMTP client for sending email.
+ smtp_client: Option>>>,
}
impl Db {
/// Creates a new store at the specified path, or opens the store if it already exists.
/// The server_url is the domain where the db will be hosted, e.g. http://localhost/
/// It is used for distinguishing locally defined items from externally defined ones.
- pub fn init(path: &std::path::Path, server_url: String) -> AtomicResult {
+ pub fn init(path: &std::path::Path, server_url: &str) -> AtomicResult {
tracing::info!("Opening database at {:?}", path);
let db = sled::open(path).map_err(|e|format!("Failed opening DB at this location: {:?} . Is another instance of Atomic Server running? {}", path, e))?;
@@ -104,15 +111,16 @@ impl Db {
let store = Db {
path: path.into(),
db,
- default_agent: Arc::new(Mutex::new(None)),
+ default_agent: Arc::new(std::sync::Mutex::new(None)),
resources,
reference_index,
query_index,
prop_val_sub_index,
- server_url,
+ server_url: AtomicUrl::try_from(server_url)?,
watched_queries,
- endpoints: default_endpoints(),
- on_commit: None,
+ endpoints: Vec::new(),
+ handle_commit: None,
+ smtp_client: None,
};
migrate_maybe(&store).map(|e| format!("Error during migration of database: {:?}", e))?;
crate::populate::populate_base_models(&store)
@@ -122,13 +130,10 @@ impl Db {
/// Create a temporary Db in `.temp/db/{id}`. Useful for testing.
/// Populates the database, creates a default agent, and sets the server_url to "http://localhost/".
- pub fn init_temp(id: &str) -> AtomicResult {
- let tmp_dir_path = format!(".temp/db/{}", id);
- let _try_remove_existing = std::fs::remove_dir_all(&tmp_dir_path);
- let store = Db::init(
- std::path::Path::new(&tmp_dir_path),
- "https://localhost".into(),
- )?;
+ pub fn init_temp() -> AtomicResult {
+ let random_id = crate::utils::random_string(10);
+ let tmp_dir_path = format!(".temp/db/{}", random_id);
+ let store = Db::init(std::path::Path::new(&tmp_dir_path), "https://localhost")?;
let agent = store.create_agent(None)?;
store.set_default_agent(agent);
store.populate()?;
@@ -208,6 +213,12 @@ impl Db {
Ok(())
}
+ /// Closes the database.
+ pub fn flush(&self) -> AtomicResult<()> {
+ self.db.flush()?;
+ Ok(())
+ }
+
/// Internal method for fetching Resource data.
#[instrument(skip(self))]
fn set_propvals(&self, subject: &str, propvals: &PropVals) -> AtomicResult<()> {
@@ -219,7 +230,7 @@ impl Db {
/// Sets a function that is called whenever a [Commit::apply] is called.
/// This can be used to listen to events.
pub fn set_handle_commit(&mut self, on_commit: HandleCommit) {
- self.on_commit = Some(Arc::new(on_commit));
+ self.handle_commit = Some(Arc::new(on_commit));
}
/// Finds resource by Subject, return PropVals HashMap
@@ -242,7 +253,7 @@ impl Db {
Ok(propval)
}
None => Err(AtomicError::not_found(format!(
- "Resource {} not found",
+ "Resource {} does not exist",
subject
))),
}
@@ -275,6 +286,9 @@ impl Db {
let (subject, resource_bin) = item.expect(DB_CORRUPT_MSG);
let subject: String = String::from_utf8_lossy(&subject).to_string();
+ // if !include_external && self.is_external_subject(&subject).ok()? {
+ // return None;
+ // }
if !include_external && !subject.starts_with(&self_url) {
return None;
}
@@ -285,6 +299,29 @@ impl Db {
Some(Resource::from_propvals(propvals, subject))
}
+ pub fn register_default_endpoints(&mut self) -> AtomicResult<()> {
+ // First we delete all existing endpoint resources, as they might not be there in this new run
+ let found_endpoints = self.query(&Query::new_class(urls::ENDPOINT))?.resources;
+
+ for mut found in found_endpoints {
+ found.destroy(self)?;
+ }
+
+ let mut endpoints = build_default_endpoints();
+
+ if self.smtp_client.is_some() {
+ tracing::info!("SMTP client is set, adding register endpoints");
+ endpoints.push(plugins::register::register_endpoint());
+ endpoints.push(plugins::register::confirm_email_endpoint());
+ }
+
+ for endpoint in endpoints {
+ self.register_endpoint(endpoint)?;
+ }
+
+ Ok(())
+ }
+
fn build_index_for_atom(
&self,
atom: &IndexAtom,
@@ -386,10 +423,6 @@ impl Db {
}
fn query_basic(&self, q: &Query) -> AtomicResult {
- let self_url = self
- .get_self_url()
- .ok_or("No self_url set, required for Queries")?;
-
let mut subjects: Vec = vec![];
let mut resources: Vec = vec![];
let mut total_count = 0;
@@ -397,14 +430,15 @@ impl Db {
let atoms = self.get_index_iterator_for_query(q);
for (i, atom_res) in atoms.enumerate() {
- let atom = atom_res?;
- if !q.include_external && !atom.subject.starts_with(&self_url) {
+ total_count += 1;
+
+ if q.offset > i {
continue;
}
- total_count += 1;
+ let atom = atom_res?;
- if q.offset > i {
+ if !q.include_external && self.is_external_subject(&atom.subject)? {
continue;
}
@@ -479,10 +513,55 @@ impl Db {
}
Ok(())
}
+
+ /// Adds an [Endpoint] to the store. This means adding a route with custom behavior.
+ pub fn register_endpoint(&mut self, endpoint: Endpoint) -> AtomicResult<()> {
+ let mut resource = endpoint.to_resource(self)?;
+ let endpoints_collection = self.get_server_url().set_route(Routes::Endpoints);
+ resource.set(
+ urls::PARENT.into(),
+ Value::AtomicUrl(endpoints_collection.to_string()),
+ self,
+ )?;
+ resource.save_locally(self)?;
+ self.endpoints.push(endpoint);
+ Ok(())
+ }
+
+ /// Registers an SMTP client to the store, allowing the store to send emails.
+ pub async fn set_smtp_config(
+ &mut self,
+ smtp_config: crate::email::SmtpConfig,
+ ) -> AtomicResult<()> {
+ self.smtp_client = Some(Arc::new(tokio::sync::Mutex::new(
+ crate::email::get_smtp_client(smtp_config).await?,
+ )));
+ Ok(())
+ }
+
+ pub async fn send_email(&self, message: MailMessage) -> AtomicResult<()> {
+ let mut client = self
+ .smtp_client
+ .as_ref()
+ .ok_or_else(|| {
+ AtomicError::other_error(
+ "No SMTP client configured. Please call set_smtp_config first.".into(),
+ )
+ })?
+ .lock()
+ .await;
+ email::send_mail(&mut client, message).await?;
+ Ok(())
+ }
}
impl Drop for Db {
fn drop(&mut self) {
+ // Remove if it's a temporary test database
+ if cfg!(test) && self.path.to_string_lossy().contains(".temp/db/") {
+ let _ = std::fs::remove_dir_all(&self.path);
+ return;
+ }
match self.db.flush() {
Ok(..) => (),
Err(e) => eprintln!("Failed to flush the database: {}", e),
@@ -660,14 +739,14 @@ impl Storelike for Db {
Ok(commit_response)
}
- fn get_server_url(&self) -> &str {
+ fn get_server_url(&self) -> &AtomicUrl {
&self.server_url
}
- // Since the DB is often also the server, this should make sense.
- // Some edge cases might appear later on (e.g. a slave DB that only stores copies?)
- fn get_self_url(&self) -> Option {
- Some(self.get_server_url().into())
+ fn get_self_url(&self) -> Option<&AtomicUrl> {
+ // Since the DB is often also the server, this should make sense.
+ // Some edge cases might appear later on (e.g. a slave DB that only stores copies?)
+ Some(self.get_server_url())
}
fn get_default_agent(&self) -> AtomicResult {
@@ -686,7 +765,7 @@ impl Storelike for Db {
let resource = crate::resources::Resource::from_propvals(propvals, subject.into());
Ok(resource)
}
- Err(e) => self.handle_not_found(subject, e, None),
+ Err(e) => self.handle_not_found(subject, e),
}
}
@@ -700,17 +779,13 @@ impl Storelike for Db {
let url_span = tracing::span!(tracing::Level::TRACE, "URL parse").entered();
// This might add a trailing slash
let url = url::Url::parse(subject)?;
- let mut removed_query_params = {
+
+ let removed_query_params = {
let mut url_altered = url.clone();
url_altered.set_query(None);
url_altered.to_string()
};
- // Remove trailing slash
- if removed_query_params.ends_with('/') {
- removed_query_params.pop();
- }
-
url_span.exit();
let endpoint_span = tracing::span!(tracing::Level::TRACE, "Endpoint").entered();
@@ -809,7 +884,7 @@ impl Storelike for Db {
}
fn handle_commit(&self, commit_response: &CommitResponse) {
- if let Some(fun) = &self.on_commit {
+ if let Some(fun) = &self.handle_commit {
fun(commit_response);
}
}
@@ -833,7 +908,8 @@ impl Storelike for Db {
) -> Box> {
let self_url = self
.get_self_url()
- .expect("No self URL set, is required in DB");
+ .expect("No self URL set, is required in DB")
+ .to_string();
let result = self.resources.into_iter().filter_map(move |item| {
Db::map_sled_item_to_resource(item, self_url.clone(), include_external)
diff --git a/lib/src/db/migrations.rs b/lib/src/db/migrations.rs
index 610b0e6c4..b572184ae 100644
--- a/lib/src/db/migrations.rs
+++ b/lib/src/db/migrations.rs
@@ -12,15 +12,25 @@ Therefore, we need migrations to convert the old schema to the new one.
- Update the Tree key used in [crate::db::trees]
*/
-use crate::{errors::AtomicResult, Db};
+use crate::{errors::AtomicResult, resources::PropVals, Db};
+
+const RESOURCE_TREE_V0: &str = "resources";
+const RESOURCE_TREE_V1: &str = "resources_v1";
+const RESOURCE_TREE_V2: &str = "resources_v2";
+pub const RESOURCE_TREE_CURRENT: &str = RESOURCE_TREE_V2;
+
+const REFERENCE_INDEX_V0: &str = "reference_index";
+const REFERENCE_INDEX_V1: &str = "reference_index_v1";
+pub const REFERENCE_INDEX_CURRENT: &str = REFERENCE_INDEX_V1;
/// Checks the current version(s) of the internal Store, and performs migrations if needed.
pub fn migrate_maybe(store: &Db) -> AtomicResult<()> {
for tree in store.db.tree_names() {
match String::from_utf8_lossy(&tree).as_ref() {
// Add migrations for outdated Trees to this list
- "resources" => v0_to_v1(store)?,
- "reference_index" => ref_v0_to_v1(store)?,
+ RESOURCE_TREE_V0 => res_v0_to_v1(store)?,
+ RESOURCE_TREE_V1 => res_v1_to_v2(store)?,
+ REFERENCE_INDEX_V0 => ref_v0_to_v1(store)?,
_other => {}
}
}
@@ -28,17 +38,16 @@ pub fn migrate_maybe(store: &Db) -> AtomicResult<()> {
}
/// Change the subjects from `bincode` to `.as_bytes()`
-fn v0_to_v1(store: &Db) -> AtomicResult<()> {
+fn res_v0_to_v1(store: &Db) -> AtomicResult<()> {
tracing::warn!("Migrating resources schema from v0 to v1...");
- let new = store.db.open_tree("resources_v1")?;
- let old_key = "resources";
+ let new = store.db.open_tree(RESOURCE_TREE_V1)?;
+ let old_key = RESOURCE_TREE_V0;
let old = store.db.open_tree(old_key)?;
let mut count = 0;
for item in old.into_iter() {
let (subject, resource_bin) = item.expect("Unable to convert into iterable");
- let subject: String =
- bincode::deserialize(&subject).expect("Unable to deserialize subject");
+ let subject: String = String::from_utf8_lossy(&subject).to_string();
new.insert(subject.as_bytes(), resource_bin)?;
count += 1;
}
@@ -73,9 +82,91 @@ fn v0_to_v1(store: &Db) -> AtomicResult<()> {
Ok(())
}
+/// add a trailing slash to all "home" subjects
+fn res_v1_to_v2(store: &Db) -> AtomicResult<()> {
+ tracing::warn!("Migrating resources schema from v1 to v2...");
+ let new = store.db.open_tree(RESOURCE_TREE_V2)?;
+ let old_key = RESOURCE_TREE_V1;
+ let old = store.db.open_tree(old_key)?;
+ let mut count = 0;
+ let mut failed_subjects: Vec = Vec::new();
+
+ for item in old.into_iter() {
+ let mut migrate_subject = |subject: &mut str| {
+ let url = match url::Url::parse(subject) {
+ Ok(url) => url,
+ Err(e) => {
+ tracing::error!(
+ "Unable to parse subject URL of '{}', keeping original: {}",
+ subject,
+ e
+ );
+ failed_subjects.push(subject.to_string());
+ return subject.to_string();
+ }
+ };
+
+ if subject != &url.to_string() {
+ println!("Migrating: {} -> {}", subject, url);
+ };
+ url.to_string()
+ };
+
+ let (subject, resource_bin) = item.expect("Unable to convert intos iterable");
+ let subject: String = String::from_utf8_lossy(&subject).to_string();
+
+ let mut propvals: PropVals = bincode::deserialize(&resource_bin)?;
+
+ for (_prop, val) in propvals.iter_mut() {
+ match val {
+ crate::Value::AtomicUrl(a) => {
+ *a = migrate_subject(a);
+ }
+ crate::Value::ResourceArray(arr) => {
+ for url in arr.iter_mut() {
+ match url {
+ crate::values::SubResource::Subject(s) => {
+ *s = migrate_subject(s);
+ }
+ // This skips nested resources
+ _other => {}
+ }
+ }
+ }
+ // This skips nested resources
+ _other => {}
+ };
+ }
+ new.insert(
+ migrate_subject(&mut subject.clone()).as_bytes(),
+ bincode::serialize(&propvals)?,
+ )?;
+ count += 1;
+ }
+
+ assert_eq!(
+ new.len(),
+ store.resources.len(),
+ "Not all resources were migrated."
+ );
+
+ assert!(
+ store.db.drop_tree(old_key)?,
+ "Old resources tree not properly removed."
+ );
+
+ tracing::warn!("Rebuilding indexes due to migrating to new version...");
+ store.db.drop_tree(old_key)?;
+ store.build_index(true)?;
+ tracing::warn!("Rebuilding index finished!");
+
+ tracing::warn!("Finished migration of {} resources", count);
+ Ok(())
+}
+
/// Add `prop_val_sub` index
fn ref_v0_to_v1(store: &Db) -> AtomicResult<()> {
- tracing::warn!("Rebuilding indexes...");
+ tracing::warn!("Rebuilding indexes due to migrating to new version...");
store.db.drop_tree("reference_index")?;
store.build_index(true)?;
tracing::warn!("Rebuilding index finished!");
diff --git a/lib/src/db/query_index.rs b/lib/src/db/query_index.rs
index 325b0da0c..0a65861af 100644
--- a/lib/src/db/query_index.rs
+++ b/lib/src/db/query_index.rs
@@ -2,8 +2,8 @@
//! It relies on lexicographic ordering of keys, which Sled utilizes using `scan_prefix` queries.
use crate::{
- agents::ForAgent, atoms::IndexAtom, errors::AtomicResult, storelike::Query,
- values::SortableValue, Atom, Db, Resource, Storelike, Value,
+ agents::ForAgent, atoms::IndexAtom, errors::AtomicResult, values::SortableValue, Atom, Db,
+ Query, Resource, Storelike, Value,
};
use serde::{Deserialize, Serialize};
@@ -101,10 +101,6 @@ pub fn query_sorted_indexed(
let mut resources: Vec = vec![];
let mut count = 0;
- let self_url = store
- .get_self_url()
- .ok_or("No self_url set, required for Queries")?;
-
let limit = q.limit.unwrap_or(usize::MAX);
for (i, kv) in iter.enumerate() {
@@ -117,7 +113,7 @@ pub fn query_sorted_indexed(
let (_q_filter, _val, subject) = parse_collection_members_key(&k)?;
// If no external resources should be included, skip this one if it's an external resource
- if !q.include_external && !subject.starts_with(&self_url) {
+ if !q.include_external && store.is_external_subject(subject)? {
continue;
}
@@ -495,79 +491,95 @@ pub mod test {
#[test]
fn should_update_or_not() {
- let store = &Db::init_temp("should_update_or_not").unwrap();
+ let instant = std::time::Instant::now();
- let prop = urls::IS_A.to_string();
- let class = urls::AGENT;
+ {
+ let store = &Db::init_temp().unwrap();
- let qf_prop_val = QueryFilter {
- property: Some(prop.clone()),
- value: Some(Value::AtomicUrl(class.to_string())),
- sort_by: None,
- };
+ let prop = urls::IS_A.to_string();
+ let class = urls::AGENT;
- let qf_prop = QueryFilter {
- property: Some(prop.clone()),
- value: None,
- sort_by: None,
- };
+ let qf_prop_val = QueryFilter {
+ property: Some(prop.clone()),
+ value: Some(Value::AtomicUrl(class.to_string())),
+ sort_by: None,
+ };
- let qf_val = QueryFilter {
- property: None,
- value: Some(Value::AtomicUrl(class.to_string())),
- sort_by: None,
- };
+ let qf_prop = QueryFilter {
+ property: Some(prop.clone()),
+ value: None,
+ sort_by: None,
+ };
- let resource_correct_class = Resource::new_instance(class, store).unwrap();
+ let qf_val = QueryFilter {
+ property: None,
+ value: Some(Value::AtomicUrl(class.to_string())),
+ sort_by: None,
+ };
- let subject: String = "https://example.com/someAgent".into();
+ let resource_correct_class = Resource::new_instance(class, store).unwrap();
- let index_atom = IndexAtom {
- subject,
- property: prop.clone(),
- ref_value: class.to_string(),
- sort_value: class.to_string(),
- };
+ let subject: String = "https://example.com/someAgent".into();
- // We should be able to find the resource by propval, val, and / or prop.
- assert!(should_update_property(&qf_val, &index_atom, &resource_correct_class).is_some());
- assert!(
- should_update_property(&qf_prop_val, &index_atom, &resource_correct_class,).is_some()
- );
- assert!(should_update_property(&qf_prop, &index_atom, &resource_correct_class).is_some());
-
- // Test when a different value is passed
- let resource_wrong_class = Resource::new_instance(urls::PARAGRAPH, store).unwrap();
- assert!(should_update_property(&qf_prop, &index_atom, &resource_wrong_class).is_some());
- assert!(should_update_property(&qf_val, &index_atom, &resource_wrong_class).is_none());
- assert!(should_update_property(&qf_prop_val, &index_atom, &resource_wrong_class).is_none());
-
- let qf_prop_val_sort = QueryFilter {
- property: Some(prop.clone()),
- value: Some(Value::AtomicUrl(class.to_string())),
- sort_by: Some(urls::DESCRIPTION.to_string()),
- };
- let qf_prop_sort = QueryFilter {
- property: Some(prop.clone()),
- value: None,
- sort_by: Some(urls::DESCRIPTION.to_string()),
- };
- let qf_val_sort = QueryFilter {
- property: Some(prop),
- value: Some(Value::AtomicUrl(class.to_string())),
- sort_by: Some(urls::DESCRIPTION.to_string()),
- };
+ let index_atom = IndexAtom {
+ subject,
+ property: prop.clone(),
+ ref_value: class.to_string(),
+ sort_value: class.to_string(),
+ };
- // We should update with a sort_by attribute
- assert!(
- should_update_property(&qf_prop_val_sort, &index_atom, &resource_correct_class,)
- .is_some()
- );
- assert!(
- should_update_property(&qf_prop_sort, &index_atom, &resource_correct_class,).is_some()
- );
- assert!(
- should_update_property(&qf_val_sort, &index_atom, &resource_correct_class,).is_some()
- );
+ // We should be able to find the resource by propval, val, and / or prop.
+ assert!(
+ should_update_property(&qf_val, &index_atom, &resource_correct_class).is_some()
+ );
+ assert!(
+ should_update_property(&qf_prop_val, &index_atom, &resource_correct_class,)
+ .is_some()
+ );
+ assert!(
+ should_update_property(&qf_prop, &index_atom, &resource_correct_class).is_some()
+ );
+
+ // Test when a different value is passed
+ let resource_wrong_class = Resource::new_instance(urls::PARAGRAPH, store).unwrap();
+ assert!(should_update_property(&qf_prop, &index_atom, &resource_wrong_class).is_some());
+ assert!(should_update_property(&qf_val, &index_atom, &resource_wrong_class).is_none());
+ assert!(
+ should_update_property(&qf_prop_val, &index_atom, &resource_wrong_class).is_none()
+ );
+
+ let qf_prop_val_sort = QueryFilter {
+ property: Some(prop.clone()),
+ value: Some(Value::AtomicUrl(class.to_string())),
+ sort_by: Some(urls::DESCRIPTION.to_string()),
+ };
+ let qf_prop_sort = QueryFilter {
+ property: Some(prop.clone()),
+ value: None,
+ sort_by: Some(urls::DESCRIPTION.to_string()),
+ };
+ let qf_val_sort = QueryFilter {
+ property: Some(prop),
+ value: Some(Value::AtomicUrl(class.to_string())),
+ sort_by: Some(urls::DESCRIPTION.to_string()),
+ };
+
+ // We should update with a sort_by attribute
+ assert!(should_update_property(
+ &qf_prop_val_sort,
+ &index_atom,
+ &resource_correct_class,
+ )
+ .is_some());
+ assert!(
+ should_update_property(&qf_prop_sort, &index_atom, &resource_correct_class,)
+ .is_some()
+ );
+ assert!(
+ should_update_property(&qf_val_sort, &index_atom, &resource_correct_class,)
+ .is_some()
+ );
+ }
+ println!("Took {:?}", instant.elapsed())
}
}
diff --git a/lib/src/db/test.rs b/lib/src/db/test.rs
index f3ec91cd3..0675b819e 100644
--- a/lib/src/db/test.rs
+++ b/lib/src/db/test.rs
@@ -1,4 +1,4 @@
-use crate::{agents::ForAgent, urls, Value};
+use crate::{agents::ForAgent, atomic_url::Routes, urls, Value};
use super::*;
use ntest::timeout;
@@ -9,7 +9,7 @@ use ntest::timeout;
use lazy_static::lazy_static; // 1.4.0
use std::sync::Mutex;
lazy_static! {
- pub static ref DB: Mutex = Mutex::new(Db::init_temp("shared").unwrap());
+ pub static ref DB: Mutex = Mutex::new(Db::init_temp().unwrap());
}
#[test]
@@ -59,13 +59,13 @@ fn basic() {
#[test]
fn populate_collections() {
- let store = Db::init_temp("populate_collections").unwrap();
+ let store = Db::init_temp().unwrap();
let subjects: Vec = store
.all_resources(false)
.map(|r| r.get_subject().into())
.collect();
println!("{:?}", subjects);
- let collections_collection_url = format!("{}/collections", store.get_server_url());
+ let collections_collection_url = format!("{}collections", store.get_server_url());
let collections_resource = store
.get_resource_extended(&collections_collection_url, false, &ForAgent::Public)
.unwrap();
@@ -89,9 +89,9 @@ fn populate_collections() {
/// Check if a resource is properly removed from the DB after a delete command.
/// Also counts commits.
fn destroy_resource_and_check_collection_and_commits() {
- let store = Db::init_temp("counter").unwrap();
+ let store = Db::init_temp().unwrap();
+ let agents_url = store.get_server_url().set_route(Routes::Agents).to_string();
let for_agent = &ForAgent::Public;
- let agents_url = format!("{}/agents", store.get_server_url());
let agents_collection_1 = store
.get_resource_extended(&agents_url, false, for_agent)
.unwrap();
@@ -110,7 +110,10 @@ fn destroy_resource_and_check_collection_and_commits() {
);
// We will count the commits, and check if they've incremented later on.
- let commits_url = format!("{}/commits", store.get_server_url());
+ let commits_url = store
+ .get_server_url()
+ .set_route(Routes::Commits)
+ .to_string();
let commits_collection_1 = store
.get_resource_extended(&commits_url, false, for_agent)
.unwrap();
@@ -195,9 +198,9 @@ fn destroy_resource_and_check_collection_and_commits() {
#[test]
fn get_extended_resource_pagination() {
- let store = Db::init_temp("get_extended_resource_pagination").unwrap();
+ let store = Db::init_temp().unwrap();
let subject = format!(
- "{}/commits?current_page=2&page_size=99999",
+ "{}collections/commits?current_page=2&page_size=99999",
store.get_server_url()
);
let for_agent = &ForAgent::Public;
@@ -207,6 +210,7 @@ fn get_extended_resource_pagination() {
{
panic!("Page 2 should not exist, because page size is set to a high value.")
}
+ let num = 2;
// let subject = "https://atomicdata.dev/classes?current_page=2&page_size=1";
let subject_with_page_size = format!("{}&page_size=1", subject);
let resource = store
@@ -217,7 +221,7 @@ fn get_extended_resource_pagination() {
.unwrap()
.to_int()
.unwrap();
- assert_eq!(cur_page, 2);
+ assert_eq!(cur_page, num);
assert_eq!(resource.get_subject(), &subject_with_page_size);
}
@@ -227,7 +231,7 @@ fn get_extended_resource_pagination() {
fn queries() {
// Re-using the same instance can cause issues with testing concurrently.
// let store = &DB.lock().unwrap().clone();
- let store = &Db::init_temp("queries").unwrap();
+ let store = &Db::init_temp().unwrap();
let demo_val = Value::Slug("myval".to_string());
let demo_reference = Value::AtomicUrl(urls::PARAGRAPH.into());
@@ -324,7 +328,7 @@ fn queries() {
r.set(sort_by.into(), Value::Markdown("!first".into()), store)
.unwrap();
let resp = r.save(store).unwrap();
- resource_changed_order_opt = resp.resource_new.clone();
+ resource_changed_order_opt = resp.resource_new;
}
prev_resource = r.clone();
}
@@ -393,7 +397,7 @@ fn queries() {
/// Check if `include_external` is respected.
#[test]
fn query_include_external() {
- let store = &Db::init_temp("query_include_external").unwrap();
+ let store = &Db::init_temp().unwrap();
let mut q = Query {
property: Some(urls::DESCRIPTION.into()),
@@ -419,9 +423,23 @@ fn query_include_external() {
);
}
+#[test]
+fn drop_speed() {
+ let start = std::time::Instant::now();
+ println!("Start: {:?}", start.elapsed());
+ assert!(
+ std::time::Duration::from_secs(1) > start.elapsed(),
+ "initializing DB too slow"
+ );
+ let store = Db::init_temp().unwrap();
+ println!("Init db: {:?}", start.elapsed());
+ drop(store);
+ println!("Drop db: {:?}", start.elapsed());
+}
+
#[test]
fn test_db_resources_all() {
- let store = &Db::init_temp("resources_all").unwrap();
+ let store = &Db::init_temp().unwrap();
let res_no_include = store.all_resources(false).count();
let res_include = store.all_resources(true).count();
assert!(
@@ -433,7 +451,7 @@ fn test_db_resources_all() {
#[test]
/// Changing these values actually correctly updates the index.
fn index_invalidate_cache() {
- let store = &Db::init_temp("invalidate_cache").unwrap();
+ let store = &Db::init_temp().unwrap();
// Make sure to use Properties that are not in the default store
diff --git a/lib/src/db/trees.rs b/lib/src/db/trees.rs
index 74607e129..e22516781 100644
--- a/lib/src/db/trees.rs
+++ b/lib/src/db/trees.rs
@@ -1,6 +1,10 @@
use crate::atoms::IndexAtom;
-use super::{prop_val_sub_index::propvalsub_key, val_prop_sub_index::valpropsub_key};
+use super::{
+ migrations::{REFERENCE_INDEX_CURRENT, RESOURCE_TREE_CURRENT},
+ prop_val_sub_index::propvalsub_key,
+ val_prop_sub_index::valpropsub_key,
+};
#[derive(Debug)]
pub enum Tree {
@@ -18,8 +22,8 @@ pub enum Tree {
ValPropSub,
}
-const RESOURCES: &str = "resources_v1";
-const VALPROPSUB: &str = "reference_index_v1";
+const RESOURCES: &str = RESOURCE_TREE_CURRENT;
+const VALPROPSUB: &str = REFERENCE_INDEX_CURRENT;
const QUERY_MEMBERS: &str = "members_index";
const PROPVALSUB: &str = "prop_val_sub_index";
const QUERIES_WATCHED: &str = "watched_queries";
@@ -27,7 +31,7 @@ const QUERIES_WATCHED: &str = "watched_queries";
impl std::fmt::Display for Tree {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
- Tree::Resources => f.write_str(RESOURCES),
+ Tree::Resources => f.write_str(RESOURCE_TREE_CURRENT),
Tree::WatchedQueries => f.write_str(QUERIES_WATCHED),
Tree::PropValSub => f.write_str(PROPVALSUB),
Tree::ValPropSub => f.write_str(VALPROPSUB),
diff --git a/lib/src/email.rs b/lib/src/email.rs
new file mode 100644
index 000000000..a1e78fc0b
--- /dev/null
+++ b/lib/src/email.rs
@@ -0,0 +1,108 @@
+//! [EmailAddress] with validation, [MailMessage] with sending, and [get_smtp_client] for setting up mail.
+
+use crate::{errors::AtomicResult, urls, Query, Storelike};
+use mail_send::{mail_builder::MessageBuilder, Connected, Transport};
+use serde::{Deserialize, Serialize};
+use tracing::info;
+
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct EmailAddress {
+ pub address: String,
+}
+
+impl EmailAddress {
+ pub fn new(address: String) -> AtomicResult {
+ // TODO: use decent crate for validation, like Lettre
+ if !address.contains('@') {
+ return Err(format!("Invalid email address: {}", address).into());
+ }
+ Ok(Self { address })
+ }
+
+ /// Throws error if email address is already taken
+ pub fn check_used(self, store: &impl Storelike) -> AtomicResult {
+ let mut query = Query::new();
+ // TODO: This hits too many resources, as it will also include non-agent resources
+ query.property = Some(urls::EMAIL.into());
+ query.value = Some(crate::Value::String(self.address.clone()));
+ if store.query(&query)?.count > 0 {
+ return Err("Email address already used".into());
+ }
+ Ok(self)
+ }
+}
+
+impl std::fmt::Display for EmailAddress {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", self.address)
+ }
+}
+
+pub struct SmtpConfig {
+ pub host: String,
+ pub port: u16,
+}
+
+pub async fn get_smtp_client(
+ config: SmtpConfig,
+) -> AtomicResult> {
+ let full_address = format!("{}:{}", config.host, config.port);
+ info!("Connecting to mailserver {full_address}");
+ let connection = Transport::new(config.host.clone())
+ .port(config.port)
+ .connect()
+ .await
+ .map_err(|e| format!("Error connecting to SMTP mail server at {full_address}. Is it running? Error message: {e}"))?;
+ Ok(connection)
+}
+
+#[derive(Debug)]
+pub struct MailMessage {
+ pub to: EmailAddress,
+ pub subject: String,
+ pub body: String,
+ pub action: Option,
+}
+
+#[derive(Debug)]
+pub struct MailAction {
+ pub name: String,
+ pub url: String,
+}
+
+#[tracing::instrument(skip(connection))]
+pub async fn send_mail(
+ connection: &mut mail_send::Transport<'static, Connected>,
+ message: MailMessage,
+) -> AtomicResult<()> {
+ let html = if let Some(action) = message.action {
+ format!(
+ "{}{} ",
+ message.body, action.url, action.name
+ )
+ } else {
+ message.body.clone()
+ };
+
+ let builder = MessageBuilder::new()
+ .from(("Atomic Data", "noreply@atomicdata.dev"))
+ .to(vec![(message.to.to_string())])
+ .subject(message.subject)
+ .html_body(html)
+ .text_body(message.body);
+ info!("Sending mail");
+ connection.send(builder).await.map_err(|e| e.to_string())?;
+ Ok(())
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn create_and_serialize_email() {
+ EmailAddress::new("invalid email".into()).unwrap_err();
+ let valid = EmailAddress::new("valid@email.com".into()).unwrap();
+ assert_eq!(valid.to_string(), "valid@email.com");
+ }
+}
diff --git a/lib/src/endpoints.rs b/lib/src/endpoints.rs
index c3699fd60..0391b9eeb 100644
--- a/lib/src/endpoints.rs
+++ b/lib/src/endpoints.rs
@@ -32,6 +32,7 @@ pub struct HandlePostContext<'a> {
pub body: Vec,
}
/// An API endpoint at some path which accepts requests and returns some Resource.
+/// Add them by calling [Db::register_endpoint]
#[derive(Clone)]
pub struct Endpoint {
/// The part behind the server domain, e.g. '/versions' or '/collections'. Include the slash.
@@ -58,7 +59,11 @@ pub struct PostEndpoint {
impl Endpoint {
/// Converts Endpoint to resource. Does not save it.
pub fn to_resource(&self, store: &impl Storelike) -> AtomicResult {
- let subject = format!("{}{}", store.get_server_url(), self.path);
+ let subject = store
+ .get_server_url()
+ .clone()
+ .set_path(&self.path)
+ .to_string();
let mut resource = store.get_resource_new(&subject);
resource.set_string(urls::DESCRIPTION.into(), &self.description, store)?;
resource.set_string(urls::SHORTNAME.into(), &self.shortname, store)?;
@@ -74,7 +79,15 @@ impl Endpoint {
}
}
-pub fn default_endpoints() -> Vec {
+impl std::fmt::Debug for Endpoint {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("Endpoint")
+ .field("path", &self.path)
+ .finish()
+ }
+}
+
+pub fn build_default_endpoints() -> Vec {
vec![
plugins::versioning::version_endpoint(),
plugins::versioning::all_versions_endpoint(),
@@ -83,6 +96,8 @@ pub fn default_endpoints() -> Vec {
plugins::files::upload_endpoint(),
plugins::files::download_endpoint(),
plugins::export::export_endpoint(),
+ plugins::register::register_endpoint(),
+ plugins::register::confirm_email_endpoint(),
#[cfg(feature = "html")]
plugins::bookmark::bookmark_endpoint(),
plugins::importer::import_endpoint(),
diff --git a/lib/src/hierarchy.rs b/lib/src/hierarchy.rs
index 0eb1f92d6..42a3b733c 100644
--- a/lib/src/hierarchy.rs
+++ b/lib/src/hierarchy.rs
@@ -4,7 +4,7 @@
use core::fmt;
-use crate::{agents::ForAgent, errors::AtomicResult, storelike::Query, urls, Resource, Storelike};
+use crate::{agents::ForAgent, errors::AtomicResult, urls, Query, Resource, Storelike};
#[derive(Debug)]
pub enum Right {
diff --git a/lib/src/lib.rs b/lib/src/lib.rs
index 4238fcb8e..d6b3c38f3 100644
--- a/lib/src/lib.rs
+++ b/lib/src/lib.rs
@@ -1,25 +1,31 @@
/*!
`atomic_lib` helps you to get, store, serialize, parse and validate Atomic Data.
+It's primarily used for powering [Atomic-Server](https://github.com/atomicdata-dev/atomic-data-rust).
+Many of the features are optional, which helps us keep the default size small.
See the [Atomic Data Docs](https://docs.atomicdata.dev) for more information.
## Features
- Two stores for Atomic Data:
+ - **On disk** [Db], powered by Sled. Indexes filtered queries. (requires `db` feature)
- **In-memory** [Store] for getting / setting data. Useful for client applications.
- - **On disk** [Db], powered by Sled. Useful for applications that persist Atomic Data, such as [`atomic-server`](https://crates.io/crates/atomic-server).
-- [serialize] and [parse] tools for [JSON-AD](https://docs.atomicdata.dev/core/json-ad.html), plain JSON, RDF, Turtle, N-Triples and JSON-LD.
+- [parse] and import tools for [JSON-AD](https://docs.atomicdata.dev/core/json-ad.html)
+- [serialize] tools for JSON-AD, plain JSON, RDF, Turtle, N-Triples and JSON-LD.
- [Resource] with getters, setters and a `.save` function that creates Commits.
- [Value] converts Atomic Data to Rust native types
-- Validate [Atomic Schema](https://docs.atomicdata.dev/schema/intro.html)
-- [Commit]s (transactions / delta's / changes / updates / versioning / history).
-- [plugins] system (although not very mature)
+- [Commit]s (transactions / delta's / changes / updates / versioning / history). Supports many checks, such as Schema, Authorization and more.
- [collections] (pagination, sorting, filtering)
-- Querying (using triple pattern fragments) (see [storelike::Query])
-- [plugins::invite] for sharing
+- Queries (see [query::Query])
- [hierarchy] for authorization
- [crate::endpoints::Endpoint] for custom API endpoints
-- [config::Config] files.
+- [config::Config] files. (requires `config` feature)
+- [endpoints] which allow easily adding routes with custom features
+- [plugins] system basics. Not very mature, as we still need the code in this repo. (all plugins require `db` feature)
+- [plugins::invite] for sharing URLs that grant rights
+- [plugins::chatroom] for slack-like group chats.
+- [plugins::bookmark] for fetching HTML pages, converting them to markdown, and storing them as Atomic Data (requires `html` feature)
+- [plugins::versioning] for constructing previous versions of resources, powered by [Commit]s.
## Getting started
@@ -59,6 +65,7 @@ assert!(fetched_new_resource.get_shortname("description", &store).unwrap().to_st
*/
pub mod agents;
+pub mod atomic_url;
pub mod atoms;
pub mod authentication;
pub mod client;
@@ -70,6 +77,8 @@ pub mod datatype;
#[cfg(feature = "db")]
pub mod db;
#[cfg(feature = "db")]
+pub mod email;
+#[cfg(feature = "db")]
pub mod endpoints;
pub mod errors;
pub mod hierarchy;
@@ -78,6 +87,7 @@ pub mod parse;
#[cfg(feature = "db")]
pub mod plugins;
pub mod populate;
+pub mod query;
pub mod resources;
pub mod schema;
pub mod serialize;
@@ -85,17 +95,21 @@ pub mod store;
pub mod storelike;
#[cfg(test)]
mod test_utils;
+#[cfg(feature = "db")]
+pub mod token;
pub mod urls;
pub mod utils;
pub mod validate;
pub mod values;
+pub use atomic_url::AtomicUrl;
pub use atoms::Atom;
pub use commit::Commit;
#[cfg(feature = "db")]
pub use db::Db;
pub use errors::AtomicError;
pub use errors::AtomicErrorType;
+pub use query::Query;
pub use resources::Resource;
pub use store::Store;
pub use storelike::Storelike;
diff --git a/lib/src/parse.rs b/lib/src/parse.rs
index b746504e6..f27d03f15 100644
--- a/lib/src/parse.rs
+++ b/lib/src/parse.rs
@@ -221,7 +221,7 @@ pub fn parse_json_ad_commit_resource(
.get(urls::SUBJECT)
.ok_or("No subject field in Commit.")?
.to_string();
- let subject = format!("{}/commits/{}", store.get_server_url(), signature);
+ let subject = format!("{}commits/{}", store.get_server_url(), signature);
let mut resource = Resource::new(subject);
let propvals = match parse_json_ad_map_to_resource(json, store, &ParseOpts::default())? {
SubResource::Resource(r) => r.into_propvals(),
@@ -644,8 +644,8 @@ mod test {
.import(include_str!("../test_files/local_id.json"), &parse_opts)
.unwrap();
- let reference_subject = generate_id_from_local_id(&importer, "reference");
- let my_subject = generate_id_from_local_id(&importer, "my-local-id");
+ let reference_subject = generate_id_from_local_id(&importer, "parent");
+ let my_subject = generate_id_from_local_id(&importer, "parent/my-local-id");
let found = store.get_resource(&my_subject).unwrap();
let found_ref = store.get_resource(&reference_subject).unwrap();
diff --git a/lib/src/plugins/add_pubkey.rs b/lib/src/plugins/add_pubkey.rs
new file mode 100644
index 000000000..824184bf6
--- /dev/null
+++ b/lib/src/plugins/add_pubkey.rs
@@ -0,0 +1,134 @@
+/*!
+Sends users a link to add a new public key to their account.
+Useful when a users loses their private key.
+*/
+
+use serde::{Deserialize, Serialize};
+
+use crate::{
+ agents::Agent,
+ email::{EmailAddress, MailAction, MailMessage},
+ endpoints::{Endpoint, HandleGetContext},
+ errors::AtomicResult,
+ plugins::utils::return_success,
+ urls, Resource, Storelike,
+};
+
+pub fn request_email_add_pubkey() -> Endpoint {
+ Endpoint {
+ path: urls::PATH_ADD_PUBKEY.to_string(),
+ params: [urls::TOKEN.to_string(), urls::INVITE_PUBKEY.to_string()].into(),
+ description: "Requests an email to add a new PublicKey to an Agent.".to_string(),
+ shortname: "request-pubkey-reset".to_string(),
+ handle: Some(handle_request_email_pubkey),
+ handle_post: None,
+ }
+}
+
+pub fn confirm_add_pubkey() -> Endpoint {
+ Endpoint {
+ path: urls::PATH_CONFIRM_PUBKEY.to_string(),
+ params: [urls::TOKEN.to_string(), urls::INVITE_PUBKEY.to_string()].into(),
+ description: "Confirms a token to add a new PublicKey.".to_string(),
+ shortname: "request-pubkey-reset".to_string(),
+ handle: Some(handle_confirm_add_pubkey),
+ handle_post: None,
+ }
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+struct AddPubkeyToken {
+ agent: String,
+}
+
+pub fn handle_request_email_pubkey(context: HandleGetContext) -> AtomicResult {
+ let HandleGetContext {
+ subject,
+ store,
+ for_agent: _,
+ } = context;
+ let mut email_option: Option = None;
+ for (k, v) in subject.query_pairs() {
+ if let "email" = k.as_ref() {
+ email_option = Some(EmailAddress::new(v.to_string())?)
+ }
+ }
+ // by default just return the Endpoint
+ let Some(email) = email_option else {
+ return request_email_add_pubkey().to_resource(store);
+ };
+
+ // If we can't find the agent by email, we should still return a `success` response,
+ // in order to prevent users to know that the email exists.
+ let msg = "Email sent (if available)!";
+ let agent = match Agent::from_email(&email.to_string(), store) {
+ Ok(a) => a,
+ Err(_) => return return_success(msg),
+ };
+
+ // send the user an e-mail to confirm sign up
+ let store_clone = store.clone();
+ let confirmation_token_struct = AddPubkeyToken {
+ agent: agent.subject,
+ };
+ let token = crate::token::sign_claim(store, confirmation_token_struct)?;
+ let mut confirm_url = store
+ .get_server_url()
+ .clone()
+ .set_path(urls::PATH_CONFIRM_PUBKEY)
+ .url();
+ confirm_url.set_query(Some(&format!("token={}", token)));
+ let message = MailMessage {
+ to: email,
+ subject: "Add a new Secret to your account".to_string(),
+ body: "You've requested adding a new Secret. Click the link below to do so!".to_string(),
+ action: Some(MailAction {
+ name: "Add new Secret to account".to_string(),
+ url: confirm_url.into(),
+ }),
+ };
+ // async, because mails are slow
+ tokio::spawn(async move {
+ store_clone
+ .send_email(message)
+ .await
+ .unwrap_or_else(|e| tracing::error!("Error sending email: {}", e));
+ });
+
+ return_success(msg)
+}
+
+pub fn handle_confirm_add_pubkey(context: HandleGetContext) -> AtomicResult {
+ let HandleGetContext {
+ subject,
+ store,
+ for_agent: _,
+ } = context;
+
+ let mut token_opt: Option = None;
+ let mut pubkey_option = None;
+
+ for (k, v) in subject.query_pairs() {
+ match k.as_ref() {
+ "token" | urls::TOKEN => token_opt = Some(v.to_string()),
+ "public-key" | urls::INVITE_PUBKEY => pubkey_option = Some(v.to_string()),
+ _ => {}
+ }
+ }
+
+ let Some(token) = token_opt else {
+ return confirm_add_pubkey().to_resource(store);
+ };
+
+ let pubkey = pubkey_option.ok_or("No public-key provided")?;
+
+ // Parse and verify the JWT token
+ let confirmation = crate::token::verify_claim::(store, &token)?.custom;
+
+ // Add the key to the agent
+ let mut agent = store.get_resource(&confirmation.agent)?;
+ agent.push(urls::ACTIVE_KEYS, pubkey.into(), true)?;
+ agent.save(store)?;
+
+ return_success("")
+}
diff --git a/lib/src/plugins/chatroom.rs b/lib/src/plugins/chatroom.rs
index 45352abb7..f9aeb4944 100644
--- a/lib/src/plugins/chatroom.rs
+++ b/lib/src/plugins/chatroom.rs
@@ -8,9 +8,8 @@ use crate::{
agents::ForAgent,
commit::{CommitBuilder, CommitOpts},
errors::AtomicResult,
- storelike::Query,
urls::{self, PARENT},
- utils, Resource, Storelike, Value,
+ utils, Query, Resource, Storelike, Value,
};
// Find the messages for the ChatRoom
diff --git a/lib/src/plugins/mod.rs b/lib/src/plugins/mod.rs
index 9f383bf51..7275ea679 100644
--- a/lib/src/plugins/mod.rs
+++ b/lib/src/plugins/mod.rs
@@ -6,7 +6,7 @@ Plugins can have functions that are called at specific moments by Atomic-Server.
For example:
-- Before returning a Resource. These are either Endpoints or Class Extenders.
+- Before returning a Resource. These are either [Endpoint]s or Class Extenders.
- Before applying a Commit.
In the long term, these plugins will probably be powered by WASM and can be extended at runtime.
@@ -16,13 +16,13 @@ However, they are designed in such a way that they have a limited scope and a cl
## Extending resources
There are two ways of extending / modifying a Resource.
-Endpoints are great for APIs that have a fixed route, and Class Extenders are great for APIs that don't have a fixed route.
+[Endpoint]s are great for APIs that have a fixed route, and Class Extenders are great for APIs that don't have a fixed route.
Endpoints are easier to generate from Rust, and will be available the second a server is Running.
-### Endpoints
+### [Endpoint]s
Resources that typically parse query parameters and return a dynamic resource.
-When adding an endpoint, add it to the list of endpoints in [lib/src/endpoints.rs]
+When adding an endpoint, add it to the list of [default_endpoints] in this file.
Endpoints are all instances of the [crate] class.
They are presented in the UI as a form.
@@ -31,6 +31,7 @@ They are presented in the UI as a form.
Similar to Endpoints, Class Extenders can modify their contents before creating a response.
Contrary to Endpoints, these can be any type of Class.
They are used for performing custom queries, or calculating dynamic attributes.
+Add these by registering the handler at [crate::db::Db::get_resource_extended].
*/
// Class Extenders
@@ -39,6 +40,7 @@ pub mod importer;
pub mod invite;
// Endpoints
+pub mod add_pubkey;
#[cfg(feature = "html")]
pub mod bookmark;
pub mod export;
@@ -46,5 +48,9 @@ pub mod files;
pub mod path;
pub mod prunetests;
pub mod query;
+pub mod register;
pub mod search;
pub mod versioning;
+
+// Utilities / helpers
+mod utils;
diff --git a/lib/src/plugins/prunetests.rs b/lib/src/plugins/prunetests.rs
index c167d854c..7a4a4f906 100644
--- a/lib/src/plugins/prunetests.rs
+++ b/lib/src/plugins/prunetests.rs
@@ -1,8 +1,7 @@
use crate::{
endpoints::{Endpoint, HandleGetContext, HandlePostContext},
errors::AtomicResult,
- storelike::Query,
- urls, Resource, Storelike, Value,
+ urls, Query, Resource, Storelike, Value,
};
pub fn prune_tests_endpoint() -> Endpoint {
diff --git a/lib/src/plugins/register.rs b/lib/src/plugins/register.rs
new file mode 100644
index 000000000..ea0a8022a
--- /dev/null
+++ b/lib/src/plugins/register.rs
@@ -0,0 +1,207 @@
+//! Creates a new Drive and optionally also an Agent.
+
+use serde::{Deserialize, Serialize};
+
+use crate::{
+ agents::Agent,
+ email::{EmailAddress, MailAction, MailMessage},
+ endpoints::{Endpoint, HandleGetContext},
+ errors::AtomicResult,
+ urls::{self},
+ values::SubResource,
+ Resource, Storelike, Value,
+};
+
+use super::utils::return_success;
+
+pub fn register_endpoint() -> Endpoint {
+ Endpoint {
+ path: urls::PATH_REGISTER.to_string(),
+ params: [
+ urls::NAME.to_string(),
+ urls::EMAIL.to_string(),
+ ].into(),
+ description: "Allows new users to easily, in one request, make both an Agent and a Drive. This drive will be created at the subdomain of `name`.".to_string(),
+ shortname: "register".to_string(),
+ handle_post: None,
+ handle: Some(handle_register_name_and_email),
+ }
+}
+
+pub fn confirm_email_endpoint() -> Endpoint {
+ Endpoint {
+ path: urls::PATH_CONFIRM_EMAIL.to_string(),
+ params: [urls::TOKEN.to_string(), urls::INVITE_PUBKEY.to_string()].into(),
+ description: "Confirm email address and set a key for your Agent.".to_string(),
+ shortname: "confirm-email".to_string(),
+ handle: Some(construct_confirm_email_redirect),
+ handle_post: None,
+ }
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+struct MailConfirmation {
+ pub email: EmailAddress,
+ pub name: String,
+}
+
+#[derive(Debug, Clone)]
+struct UserName {
+ pub name: String,
+}
+
+impl UserName {
+ /// Throws error if email address is already taken
+ pub fn check_used(name: &str, store: &impl Storelike) -> AtomicResult {
+ let mut drive_url = store
+ .get_self_url()
+ .ok_or("No self url, cant check name")?
+ .clone();
+ drive_url.set_subdomain(Some(name))?;
+
+ match store.get_resource(&drive_url.to_string()) {
+ Ok(_) => Err("Name already used".into()),
+ Err(_) => Ok(Self { name: name.into() }),
+ }
+ }
+}
+
+impl std::fmt::Display for UserName {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", self.name)
+ }
+}
+
+#[tracing::instrument]
+pub fn handle_register_name_and_email(context: HandleGetContext) -> AtomicResult {
+ let HandleGetContext {
+ subject,
+ store: _,
+ for_agent: _,
+ } = context;
+ let mut name_option = None;
+ let mut email_option: Option = None;
+ let store = context.store;
+ for (k, v) in subject.query_pairs() {
+ match k.as_ref() {
+ "name" | urls::NAME => name_option = Some(UserName::check_used(&v, store)?),
+ "email" => email_option = Some(EmailAddress::new(v.to_string())?),
+ _ => {}
+ }
+ }
+ // by default just return the Endpoint
+ if name_option.is_none() && email_option.is_none() {
+ return register_endpoint().to_resource(store);
+ };
+
+ let name = name_option.ok_or("No name provided")?;
+ let _validate_name = Value::new(&name.to_string(), &crate::datatype::DataType::Slug)?;
+ let email = email_option.ok_or("No email provided")?.check_used(store)?;
+
+ // send the user an e-mail to confirm sign up
+ let store_clone = store.clone();
+ let confirmation_token_struct = MailConfirmation {
+ email: email.clone(),
+ name: name.to_string(),
+ };
+ let token = crate::token::sign_claim(store, confirmation_token_struct)?;
+ let mut confirm_url = store
+ .get_server_url()
+ .clone()
+ .set_path(urls::PATH_CONFIRM_EMAIL)
+ // .set_subdomain(Some(&name.to_string()))?
+ .url();
+ confirm_url.set_query(Some(&format!("token={}", token)));
+ let message = MailMessage {
+ to: email,
+ subject: "Confirm your e-mail address".to_string(),
+ body: format!("Welcome to Atomic Data, {}. Please confirm your e-mail address by clicking the link below", name),
+ action: Some(MailAction {
+ name: "Confirm e-mail address".to_string(),
+ url: confirm_url.into()
+ })
+ };
+ // async, because mails are slow
+ tokio::spawn(async move {
+ store_clone
+ .send_email(message)
+ .await
+ .unwrap_or_else(|e| tracing::error!("Error sending email: {}", e));
+ });
+
+ return_success("Account generated")
+}
+
+#[tracing::instrument()]
+pub fn construct_confirm_email_redirect(context: HandleGetContext) -> AtomicResult {
+ let url = context.subject;
+ let store = context.store;
+ let mut token_opt: Option = None;
+ let mut pubkey_option = None;
+
+ println!("url: {:?}", url);
+ for (k, v) in url.query_pairs() {
+ match k.as_ref() {
+ "token" | urls::TOKEN => token_opt = Some(v.to_string()),
+ "public-key" | urls::INVITE_PUBKEY => pubkey_option = Some(v.to_string()),
+ _ => {}
+ }
+ }
+ let token = if let Some(t) = token_opt {
+ t
+ } else {
+ return confirm_email_endpoint().to_resource(store);
+ };
+ let pubkey = pubkey_option.ok_or("No public-key provided")?;
+
+ // Parse and verify the JWT token
+ let confirmation = crate::token::verify_claim::(store, &token)?.custom;
+
+ // Create the Agent if it doesn't exist yet.
+ // Note: this happens before the drive is saved, which checks if the name is available.
+ // We get new agents that just do nothing, but perhaps that's not a problem.
+ let drive_creator_agent: String = {
+ let mut new_agent = Agent::new_from_public_key(store, &pubkey)?;
+ new_agent.name = Some(confirmation.name.clone());
+ let net_agent_subject = store
+ .get_server_url()
+ .clone()
+ .set_path(&format!("agents/{}", confirmation.name));
+ new_agent.subject = net_agent_subject.to_string();
+ new_agent.to_resource()?.save(store)?;
+ net_agent_subject.to_string()
+ };
+
+ // Create the new Drive
+ let drive = crate::populate::create_drive(
+ store,
+ Some(&confirmation.name),
+ &drive_creator_agent,
+ false,
+ )?;
+
+ // Add the drive to the Agent's list of drives
+ let mut agent = store.get_resource(&drive_creator_agent)?;
+ agent.push(
+ urls::DRIVES,
+ SubResource::Subject(drive.get_subject().into()),
+ true,
+ )?;
+ // TODO: Make sure this only works if the server sets the email address.
+ agent.set(
+ urls::EMAIL.into(),
+ Value::String(confirmation.email.to_string()),
+ store,
+ )?;
+ agent.save_locally(store)?;
+
+ // Construct the Redirect Resource, which might provide the Client with a Subject for his Agent.
+ let mut redirect = Resource::new_instance(urls::REDIRECT, store)?;
+ redirect.set_string(urls::DESTINATION.into(), drive.get_subject(), store)?;
+ redirect.set(
+ urls::REDIRECT_AGENT.into(),
+ crate::Value::AtomicUrl(drive_creator_agent),
+ store,
+ )?;
+ Ok(redirect)
+}
diff --git a/lib/src/plugins/utils.rs b/lib/src/plugins/utils.rs
new file mode 100644
index 000000000..86dcbd1fd
--- /dev/null
+++ b/lib/src/plugins/utils.rs
@@ -0,0 +1,11 @@
+//! Functions that can be valuable in multiple plugins
+
+use crate::{errors::AtomicResult, urls, Resource, Value};
+
+/// Returns a Resource with a description of "success"
+pub fn return_success(message: &str) -> AtomicResult {
+ let mut resource = Resource::new("unknown".into());
+ resource.set_unsafe(urls::DESCRIPTION.into(), Value::String(message.into()));
+ resource.set_unsafe(urls::NAME.into(), Value::String("Success".into()));
+ Ok(resource)
+}
diff --git a/lib/src/plugins/versioning.rs b/lib/src/plugins/versioning.rs
index 8ee57fd8b..78725e9b4 100644
--- a/lib/src/plugins/versioning.rs
+++ b/lib/src/plugins/versioning.rs
@@ -5,8 +5,7 @@ use crate::{
collections::CollectionBuilder,
endpoints::{Endpoint, HandleGetContext},
errors::AtomicResult,
- storelike::Query,
- urls, AtomicError, Commit, Resource, Storelike,
+ urls, AtomicError, Commit, Query, Resource, Storelike,
};
pub fn version_endpoint() -> Endpoint {
diff --git a/lib/src/populate.rs b/lib/src/populate.rs
index 829850830..8da031611 100644
--- a/lib/src/populate.rs
+++ b/lib/src/populate.rs
@@ -8,8 +8,7 @@ use crate::{
errors::AtomicResult,
parse::ParseOpts,
schema::{Class, Property},
- storelike::Query,
- urls, Storelike, Value,
+ urls, Query, Resource, Storelike, Value,
};
const DEFAULT_ONTOLOGY_PATH: &str = "defaultOntology";
@@ -153,26 +152,62 @@ pub fn populate_base_models(store: &impl Storelike) -> AtomicResult<()> {
Ok(())
}
-/// Creates a Drive resource at the base URL. Does not set rights. Use set_drive_rights for that.
-pub fn create_drive(store: &impl Storelike) -> AtomicResult<()> {
- let self_url = store
- .get_self_url()
- .ok_or("No self_url set, cannot populate store with Drive")?;
- let mut drive = store.get_resource_new(&self_url);
+/// Creates a Drive resource at the base URL if no name is passed.
+// #[tracing::instrument(skip(store), level = "info")]
+pub fn create_drive(
+ store: &impl Storelike,
+ drive_name: Option<&str>,
+ for_agent: &str,
+ public_read: bool,
+) -> AtomicResult {
+ let self_url = if let Some(url) = store.get_self_url() {
+ url.to_owned()
+ } else {
+ return Err("No self URL set. Cannot create drive.".into());
+ };
+ let drive_subject: String = if let Some(name) = drive_name {
+ // Let's make a subdomain
+ let mut url = self_url.url();
+ let host = url.host().expect("No host in server_url");
+ let subdomain_host = format!("{}.{}", name, host);
+ url.set_host(Some(&subdomain_host))?;
+ url.to_string()
+ } else {
+ self_url.to_string()
+ };
+
+ let mut drive = if let Some(drive_name_some) = drive_name {
+ if store.get_resource(&drive_subject).is_ok() {
+ return Err(format!("Name '{}' is already taken", drive_name_some).into());
+ }
+ Resource::new(drive_subject)
+ } else {
+ // Only for the base URL (of no drive name is passed), we should not check if the drive exists.
+ // This is because we use `create_drive` in the `--initialize` command.
+ store.get_resource_new(&drive_subject)
+ };
drive.set_class(urls::DRIVE);
- let server_url = url::Url::parse(store.get_server_url())?;
- drive.set_string(
- urls::NAME.into(),
- server_url.host_str().ok_or("Can't use current base URL")?,
- store,
- )?;
+ let host = self_url
+ .url()
+ .host()
+ .ok_or("no host in URL found")?
+ .to_string();
+ drive.set_string(urls::NAME.into(), drive_name.unwrap_or(&host), store)?;
+
+ // Set rights
+ drive.push(urls::WRITE, for_agent.into(), true)?;
+ drive.push(urls::READ, for_agent.into(), true)?;
+ if public_read {
+ drive.push(urls::READ, urls::PUBLIC_AGENT.into(), true)?;
+ }
+
drive.save_locally(store)?;
- Ok(())
+ Ok(drive)
}
pub fn create_default_ontology(store: &impl Storelike) -> AtomicResult<()> {
- let mut drive = store.get_resource(store.get_server_url())?;
+ let mut drive = store.get_resource(store.get_server_url().as_str())?;
let ontology_subject = format!("{}/{}", drive.get_subject(), DEFAULT_ONTOLOGY_PATH);
@@ -209,7 +244,7 @@ pub fn create_default_ontology(store: &impl Storelike) -> AtomicResult<()> {
/// Adds rights to the default agent to the Drive resource (at the base URL). Optionally give Public Read rights.
pub fn set_drive_rights(store: &impl Storelike, public_read: bool) -> AtomicResult<()> {
// Now let's add the agent as the Root user and provide write access
- let mut drive = store.get_resource(store.get_server_url())?;
+ let mut drive = store.get_resource(store.get_server_url().as_str())?;
let write_agent = store.get_default_agent()?.subject;
let read_agent = write_agent.clone();
@@ -236,7 +271,9 @@ To use the data in your web apps checkout our client libraries: [@tomic/lib](htt
Use [@tomic/cli](https://docs.atomicdata.dev/js-cli) to generate typed ontologies inside your code.
"#, store.get_server_url(), &format!("{}/{}", drive.get_subject(), DEFAULT_ONTOLOGY_PATH)), store)?;
}
+
drive.save_locally(store)?;
+
Ok(())
}
@@ -278,7 +315,7 @@ pub fn populate_collections(store: &impl Storelike) -> AtomicResult<()> {
for subject in result.subjects {
let mut collection =
- crate::collections::create_collection_resource_for_class(store, &subject)?;
+ crate::collections::create_collection_resource_for_class(store, &subject.to_string())?;
collection.save_locally(store)?;
}
@@ -289,13 +326,14 @@ pub fn populate_collections(store: &impl Storelike) -> AtomicResult<()> {
/// Adds default Endpoints (versioning) to the Db.
/// Makes sure they are fetchable
pub fn populate_endpoints(store: &crate::Db) -> AtomicResult<()> {
- let endpoints = crate::endpoints::default_endpoints();
- let endpoints_collection = format!("{}/endpoints", store.get_server_url());
- for endpoint in endpoints {
+ use crate::atomic_url::Routes;
+
+ let endpoints_collection = store.get_server_url().set_route(Routes::Endpoints);
+ for endpoint in crate::endpoints::build_default_endpoints() {
let mut resource = endpoint.to_resource(store)?;
resource.set(
urls::PARENT.into(),
- Value::AtomicUrl(endpoints_collection.clone()),
+ Value::AtomicUrl(endpoints_collection.to_string()),
store,
)?;
resource.save_locally(store)?;
@@ -303,34 +341,18 @@ pub fn populate_endpoints(store: &crate::Db) -> AtomicResult<()> {
Ok(())
}
-#[cfg(feature = "db")]
-/// Adds default Endpoints (versioning) to the Db.
-/// Makes sure they are fetchable
-pub fn populate_importer(store: &crate::Db) -> AtomicResult<()> {
- let base = store
- .get_self_url()
- .ok_or("No self URL in this Store - required for populating importer")?;
- let mut importer = crate::Resource::new(urls::construct_path_import(&base));
- importer.set_class(urls::IMPORTER);
- importer.set(urls::PARENT.into(), Value::AtomicUrl(base), store)?;
- importer.set(urls::NAME.into(), Value::String("Import".into()), store)?;
- importer.save_locally(store)?;
- Ok(())
-}
-
#[cfg(feature = "db")]
/// Adds items to the SideBar as subresources.
/// Useful for helping a new user get started.
pub fn populate_sidebar_items(store: &crate::Db) -> AtomicResult<()> {
let base = store.get_self_url().ok_or("No self_url")?;
- let mut drive = store.get_resource(&base)?;
- let arr = vec![
- format!("{}/setup", base),
- format!("{}/import", base),
- format!("{}/collections", base),
+ let mut drive = store.get_resource(base.as_str())?;
+ let sidebar_items = vec![
+ base.set_route(crate::atomic_url::Routes::Setup),
+ base.set_route(crate::atomic_url::Routes::Collections),
];
- for item in arr {
- drive.push(urls::SUBRESOURCES, item.into(), true)?;
+ for item in sidebar_items {
+ drive.push(urls::SUBRESOURCES, item.to_string().into(), true)?;
}
drive.save_locally(store)?;
Ok(())
@@ -342,13 +364,12 @@ pub fn populate_all(store: &crate::Db) -> AtomicResult<()> {
// populate_base_models should be run in init, instead of here, since it will result in infinite loops without
populate_default_store(store)
.map_err(|e| format!("Failed to populate default store. {}", e))?;
- create_drive(store).map_err(|e| format!("Failed to create drive. {}", e))?;
+ create_drive(store, None, &store.get_default_agent()?.subject, true)
+ .map_err(|e| format!("Failed to create drive. {}", e))?;
create_default_ontology(store)
.map_err(|e| format!("Failed to create default ontology. {}", e))?;
set_drive_rights(store, true)?;
populate_collections(store).map_err(|e| format!("Failed to populate collections. {}", e))?;
- populate_endpoints(store).map_err(|e| format!("Failed to populate endpoints. {}", e))?;
- populate_importer(store).map_err(|e| format!("Failed to populate importer. {}", e))?;
populate_sidebar_items(store)
.map_err(|e| format!("Failed to populate sidebar items. {}", e))?;
Ok(())
diff --git a/lib/src/query.rs b/lib/src/query.rs
new file mode 100644
index 000000000..4607f2b83
--- /dev/null
+++ b/lib/src/query.rs
@@ -0,0 +1,75 @@
+use crate::{agents::ForAgent, urls, Resource, Value};
+
+/// Use this to construct a list of Resources
+#[derive(Debug)]
+pub struct Query {
+ /// Filter by Property
+ pub property: Option,
+ /// Filter by Value
+ pub value: Option,
+ /// Maximum of items to return
+ pub limit: Option,
+ /// Value at which to begin lexicographically sorting things.
+ pub start_val: Option,
+ /// Value at which to stop lexicographically sorting things.
+ pub end_val: Option,
+ /// How many items to skip from the first one
+ pub offset: usize,
+ /// The Property URL that is used to sort the results
+ pub sort_by: Option,
+ /// Sort descending instead of ascending.
+ pub sort_desc: bool,
+ /// Whether to include non-server resources
+ pub include_external: bool,
+ /// Whether to include full Resources in the result, if not, will add empty vector here.
+ pub include_nested: bool,
+ /// For which Agent the query is executed. Pass `None` if you want to skip permission checks.
+ pub for_agent: ForAgent,
+}
+
+impl Query {
+ pub fn new() -> Self {
+ Query {
+ property: None,
+ value: None,
+ limit: None,
+ start_val: None,
+ end_val: None,
+ offset: 0,
+ sort_by: None,
+ sort_desc: false,
+ include_external: false,
+ include_nested: true,
+ for_agent: ForAgent::Sudo,
+ }
+ }
+
+ /// Search for a property-value combination
+ pub fn new_prop_val(prop: &str, val: &str) -> Self {
+ let mut q = Self::new();
+ q.property = Some(prop.to_string());
+ q.value = Some(Value::String(val.to_string()));
+ q
+ }
+
+ /// Search for instances of some Class
+ pub fn new_class(class: &str) -> Self {
+ let mut q = Self::new();
+ q.property = Some(urls::IS_A.into());
+ q.value = Some(Value::AtomicUrl(class.to_string()));
+ q
+ }
+}
+
+impl Default for Query {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+pub struct QueryResult {
+ pub subjects: Vec,
+ pub resources: Vec,
+ /// The amount of hits that were found, including the ones that were out of bounds or not authorized.
+ pub count: usize,
+}
diff --git a/lib/src/resources.rs b/lib/src/resources.rs
index ee1001fa1..b669689ea 100644
--- a/lib/src/resources.rs
+++ b/lib/src/resources.rs
@@ -2,10 +2,10 @@
//! Has methods for saving resources and getting properties inside them.
use crate::commit::{CommitOpts, CommitResponse};
-use crate::storelike::Query;
use crate::urls;
use crate::utils::random_string;
use crate::values::{SubResource, Value};
+use crate::Query;
use crate::{commit::CommitBuilder, errors::AtomicResult};
use crate::{
mapping::is_url,
@@ -347,13 +347,7 @@ impl Resource {
let commit_builder = self.get_commit_builder().clone();
let commit = commit_builder.sign(&agent, store, self)?;
// If the current client is a server, and the subject is hosted here, don't post
- let should_post = if let Some(self_url) = store.get_self_url() {
- !self.subject.starts_with(&self_url)
- } else {
- // Current client is not a server, has no own persisted store
- true
- };
- if should_post {
+ if store.is_external_subject(&commit.subject)? {
crate::client::post_commit(&commit, store)?;
}
let opts = CommitOpts {
@@ -392,6 +386,7 @@ impl Resource {
validate_for_agent: agent.subject.into(),
// https://github.com/atomicdata-dev/atomic-server/issues/412
validate_previous_commit: false,
+ // This is contentious: https://github.com/atomicdata-dev/atomic-data-rust/issues/556
update_index: true,
};
let commit_response = store.apply_commit(commit, &opts)?;
diff --git a/lib/src/store.rs b/lib/src/store.rs
index 8aeb82913..4d32b86ca 100644
--- a/lib/src/store.rs
+++ b/lib/src/store.rs
@@ -2,10 +2,10 @@
//! This provides many methods for finding, changing, serializing and parsing Atomic Data.
use crate::agents::Agent;
-use crate::storelike::QueryResult;
-use crate::Value;
-use crate::{atoms::Atom, storelike::Storelike};
+use crate::query::QueryResult;
+use crate::{atomic_url::AtomicUrl, storelike::Storelike};
use crate::{errors::AtomicResult, Resource};
+use crate::{Atom, Query, Value};
use std::{collections::HashMap, sync::Arc, sync::Mutex};
/// The in-memory store of data, containing the Resources, Properties and Classes
@@ -17,6 +17,10 @@ pub struct Store {
default_agent: Arc>>,
}
+lazy_static::lazy_static! {
+ static ref LOCAL_STORE_URL: AtomicUrl = AtomicUrl::try_from(crate::urls::LOCAL_STORE).unwrap();
+}
+
impl Store {
/// Creates an empty Store.
/// Run `.populate()` to get useful standard models loaded into your store.
@@ -34,7 +38,7 @@ impl Store {
/// Returns an empty array if nothing is found.
// Very costly, slow implementation.
// Does not assume any indexing.
- fn tpf(
+ pub fn tpf(
&self,
q_subject: Option<&str>,
q_property: Option<&str>,
@@ -158,14 +162,14 @@ impl Storelike for Store {
Box::new(self.hashmap.lock().unwrap().clone().into_values())
}
- fn get_server_url(&self) -> &str {
+ fn get_server_url(&self) -> &AtomicUrl {
// TODO Should be implemented later when companion functionality is here
// https://github.com/atomicdata-dev/atomic-server/issues/6
- "local:store"
+ &LOCAL_STORE_URL
}
- fn get_self_url(&self) -> Option {
- Some(self.get_server_url().into())
+ fn get_self_url(&self) -> Option<&AtomicUrl> {
+ Some(self.get_server_url())
}
fn get_default_agent(&self) -> AtomicResult {
@@ -179,11 +183,7 @@ impl Storelike for Store {
if let Some(resource) = self.hashmap.lock().unwrap().get(subject) {
return Ok(resource.clone());
}
- self.handle_not_found(
- subject,
- "Not found in HashMap.".into(),
- self.get_default_agent().ok().as_ref(),
- )
+ self.handle_not_found(subject, "Not found in HashMap.".into())
}
fn remove_resource(&self, subject: &str) -> AtomicResult<()> {
@@ -202,7 +202,7 @@ impl Storelike for Store {
self.default_agent.lock().unwrap().replace(agent);
}
- fn query(&self, q: &crate::storelike::Query) -> AtomicResult {
+ fn query(&self, q: &Query) -> AtomicResult {
let atoms = self.tpf(
None,
q.property.as_deref(),
diff --git a/lib/src/storelike.rs b/lib/src/storelike.rs
index 54705bfca..6061d03c7 100644
--- a/lib/src/storelike.rs
+++ b/lib/src/storelike.rs
@@ -1,12 +1,18 @@
-//! The Storelike Trait contains many useful methods for maniupulting / retrieving data.
+//! The Storelike Trait contains many useful methods for manipulating / retrieving data.
+//! It is the basis for both the in-memory [Store] and the on-disk [Db]
+
+use tracing::info;
use crate::{
agents::{Agent, ForAgent},
+ atomic_url::AtomicUrl,
commit::CommitResponse,
errors::AtomicError,
hierarchy,
+ query::QueryResult,
schema::{Class, Property},
- urls,
+ urls::LOCAL_STORE,
+ Query,
};
use crate::{errors::AtomicResult, parse::parse_json_ad_string};
use crate::{mapping::Mapping, values::Value, Atom, Resource};
@@ -95,15 +101,15 @@ pub trait Storelike: Sized {
}
/// Returns the base URL where the default store is.
- /// E.g. `https://example.com`
+ /// E.g. `https://example.com/`
/// This is where deltas should be sent to.
/// Also useful for Subject URL generation.
- fn get_server_url(&self) -> &str;
+ fn get_server_url(&self) -> &AtomicUrl;
/// Returns the root URL where this instance of the store is hosted.
/// Should return `None` if this is simply a client and not a server.
- /// E.g. `https://example.com`
- fn get_self_url(&self) -> Option {
+ /// E.g. `https://example.com.`
+ fn get_self_url(&self) -> Option<&AtomicUrl> {
None
}
@@ -192,11 +198,11 @@ pub trait Storelike: Sized {
Property::from_resource(prop)
}
- /// Get's the resource, parses the Query parameters and calculates dynamic properties.
+ /// Gets the resource, parses the Query parameters and calculates dynamic properties.
/// Defaults to get_resource if store doesn't support extended resources
/// If `for_agent` is None, no authorization checks will be done, and all resources will return.
- /// If you want public only resurces, pass `Some(crate::authentication::public_agent)` as the agent.
- /// - *skip_dynamic* Does not calculte dynamic properties. Adds an `incomplete=true` property if the resource should have been dynamic.
+ /// If you want public only resources, pass `Some(crate::authentication::public_agent)` as the agent.
+ /// - *skip_dynamic* Does not calculate dynamic properties. Adds an `incomplete=true` property if the resource should have been dynamic.
fn get_resource_extended(
&self,
subject: &str,
@@ -213,21 +219,16 @@ pub trait Storelike: Sized {
/// Implement this if you want to have custom handlers for Commits.
fn handle_commit(&self, _commit_response: &CommitResponse) {}
- fn handle_not_found(
- &self,
- subject: &str,
- _error: AtomicError,
- for_agent: Option<&Agent>,
- ) -> AtomicResult {
- if let Some(self_url) = self.get_self_url() {
- if subject.starts_with(&self_url) {
- return Err(AtomicError::not_found(format!(
- "Failed to retrieve locally: '{}'",
- subject
- )));
- }
+ fn handle_not_found(&self, subject: &str, _error: AtomicError) -> AtomicResult {
+ // This does not work for subdomains
+ if self.is_external_subject(subject)? {
+ self.fetch_resource(subject, None)
+ } else {
+ Err(AtomicError::not_found(format!(
+ "Subject is not stored on this server: '{}'",
+ subject
+ )))
}
- self.fetch_resource(subject, for_agent)
}
/// Imports a JSON-AD string, returns the amount of imported resources.
@@ -237,6 +238,48 @@ pub trait Storelike: Sized {
Ok(len)
}
+ /// Checks if the URL of some resource is owned by some external store.
+ /// If true, then the Subject points to a different server.
+ /// If you're using `Storelike` on something that does not persist (e.g. a client app),
+ /// the answer should always be `true`.
+ fn is_external_subject(&self, subject: &str) -> AtomicResult {
+ if let Some(self_url) = self.get_self_url() {
+ if self_url.as_str() == LOCAL_STORE {
+ return Ok(true);
+ }
+ if subject.starts_with(self_url.as_str()) {
+ return Ok(false);
+ } else {
+ // Is it a subdomain of the self_url?
+ let subject_url = url::Url::parse(subject)?;
+ let subject_host = subject_url.host().ok_or_else(|| {
+ AtomicError::not_found(format!("Subject URL has no host: {}", subject))
+ })?;
+ let self_url = self_url.url();
+ let self_host = self_url.host().ok_or_else(|| {
+ AtomicError::not_found(format!("Self URL has no host: {}", self_url))
+ })?;
+ // remove the subdomain from subject, if any.
+ // The server can have multiple subdomains
+ let subject_host_string = subject_host.to_string();
+ let subject_host_parts = subject_host_string.split('.').collect::>();
+
+ // Check if the last part of the host is equal
+ let Some(subject_host_stripped) = subject_host_parts.last() else {
+ return Ok(false);
+ };
+ info!(
+ "Comparing hosts: {} and {}",
+ subject_host_stripped, self_host
+ );
+ if subject_host_stripped == &self_host.to_string() {
+ return Ok(false);
+ }
+ }
+ }
+ Ok(true)
+ }
+
/// Removes a resource from the store. Errors if not present.
fn remove_resource(&self, subject: &str) -> AtomicResult<()>;
@@ -360,77 +403,3 @@ pub trait Storelike: Sized {
crate::validate::validate_store(self, false)
}
}
-
-/// Use this to construct a list of Resources
-#[derive(Debug)]
-pub struct Query {
- /// Filter by Property
- pub property: Option,
- /// Filter by Value
- pub value: Option,
- /// Maximum of items to return, if none returns all items.
- pub limit: Option,
- /// Value at which to begin lexicographically sorting things.
- pub start_val: Option,
- /// Value at which to stop lexicographically sorting things.
- pub end_val: Option,
- /// How many items to skip from the first one
- pub offset: usize,
- /// The Property URL that is used to sort the results
- pub sort_by: Option,
- /// Sort descending instead of ascending.
- pub sort_desc: bool,
- /// Whether to include non-server resources
- pub include_external: bool,
- /// Whether to include full Resources in the result, if not, will add empty vector here.
- pub include_nested: bool,
- /// For which Agent the query is executed. Pass `None` if you want to skip permission checks.
- pub for_agent: ForAgent,
-}
-
-impl Query {
- pub fn new() -> Self {
- Query {
- property: None,
- value: None,
- limit: None,
- start_val: None,
- end_val: None,
- offset: 0,
- sort_by: None,
- sort_desc: false,
- include_external: false,
- include_nested: true,
- for_agent: ForAgent::Sudo,
- }
- }
-
- /// Search for a property-value combination
- pub fn new_prop_val(prop: &str, val: &str) -> Self {
- let mut q = Self::new();
- q.property = Some(prop.to_string());
- q.value = Some(Value::String(val.to_string()));
- q
- }
-
- /// Search for instances of some Class
- pub fn new_class(class: &str) -> Self {
- let mut q = Self::new();
- q.property = Some(urls::IS_A.into());
- q.value = Some(Value::AtomicUrl(class.to_string()));
- q
- }
-}
-
-impl Default for Query {
- fn default() -> Self {
- Self::new()
- }
-}
-
-pub struct QueryResult {
- pub subjects: Vec,
- pub resources: Vec,
- /// The amount of hits that were found, including the ones that were out of bounds or not authorized.
- pub count: usize,
-}
diff --git a/lib/src/token.rs b/lib/src/token.rs
new file mode 100644
index 000000000..73f4b0e78
--- /dev/null
+++ b/lib/src/token.rs
@@ -0,0 +1,79 @@
+//! JWT tokens
+//! https://github.com/atomicdata-dev/atomic-data-rust/issues/544
+
+use jwt_simple::prelude::*;
+use serde::de::DeserializeOwned;
+use serde::Serialize;
+
+use crate::errors::AtomicResult;
+use crate::Storelike;
+
+/// Signs a claim as the Default Agent and creates a JWT token.
+pub fn sign_claim(
+ store: &impl Storelike,
+ custom_claim: CustomClaims,
+) -> AtomicResult {
+ let key = HS256Key::from_bytes(
+ store
+ .get_default_agent()?
+ .private_key
+ .ok_or("No private key in default agent, can't sign claims")?
+ .as_bytes(),
+ );
+ let time = Duration::from_hours(1u64);
+ let claim = Claims::with_custom_claims(custom_claim, time);
+ let token = key
+ .authenticate(claim)
+ .map_err(|e| format!("fail to create token: {}", e))?;
+ Ok(token)
+}
+
+/// Parses a JWT token, verifies its hash with the Current Agent and returns the Custom Claims.
+pub fn verify_claim(
+ store: &impl Storelike,
+ token: &str,
+) -> AtomicResult> {
+ let key = HS256Key::from_bytes(
+ store
+ .get_default_agent()?
+ .private_key
+ .ok_or("No private key in default agent, can't sign claims")?
+ .as_bytes(),
+ );
+ let verify_opts = VerificationOptions::default();
+ let claims = key
+ .verify_token(token, Some(verify_opts))
+ .map_err(|e| format!("fail to verify token: {}", e))?;
+ Ok(claims)
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use serde::{Deserialize, Serialize};
+
+ #[derive(Serialize, Deserialize, Debug)]
+ struct CustomClaims {
+ pub name: String,
+ pub email: String,
+ }
+
+ #[test]
+ fn test_sign_claim_store() {
+ let store = crate::test_utils::init_store();
+ let customclaim = CustomClaims {
+ name: "John Doe".to_string(),
+ email: "awdaw@adiow.com".to_string(),
+ };
+ let token = sign_claim(&store, customclaim).unwrap();
+ assert!(token.starts_with("ey"));
+ let claim = verify_claim::(&store, &token).unwrap();
+ assert!(claim.expires_at.is_some());
+ assert_eq!(claim.custom.email, "awdaw@adiow.com".to_string());
+
+ let malicous_agent = store.create_agent(None).unwrap();
+ store.set_default_agent(malicous_agent);
+ let wrong_claim = verify_claim::(&store, &token);
+ assert!(wrong_claim.is_err());
+ }
+}
diff --git a/lib/src/urls.rs b/lib/src/urls.rs
index 7ba27457f..f801af7e4 100644
--- a/lib/src/urls.rs
+++ b/lib/src/urls.rs
@@ -1,4 +1,5 @@
//! Contains some of the most important Atomic Data URLs.
+//! See [crate::atomic_url] for the URL datatype.
// Classes
pub const CLASS: &str = "https://atomicdata.dev/classes/Class";
@@ -54,8 +55,10 @@ pub const PREVIOUS_COMMIT: &str = "https://atomicdata.dev/properties/previousCom
pub const LAST_COMMIT: &str = "https://atomicdata.dev/properties/lastCommit";
// ... for Agents
pub const PUBLIC_KEY: &str = "https://atomicdata.dev/properties/publicKey";
+pub const ACTIVE_KEYS: &str = "https://atomicdata.dev/properties/activeKeys";
pub const NAME: &str = "https://atomicdata.dev/properties/name";
pub const DRIVES: &str = "https://atomicdata.dev/properties/drives";
+pub const EMAIL: &str = "https://atomicdata.dev/properties/email";
// ... for Collections
pub const COLLECTION_PROPERTY: &str = "https://atomicdata.dev/properties/collection/property";
pub const COLLECTION_VALUE: &str = "https://atomicdata.dev/properties/collection/value";
@@ -81,6 +84,7 @@ pub const SEARCH_LIMIT: &str = "https://atomicdata.dev/properties/search/limit";
pub const SEARCH_PROPERTY: &str = "https://atomicdata.dev/properties/search/property";
pub const URL: &str = "https://atomicdata.dev/property/url";
pub const PREVIEW: &str = "https://atomicdata.dev/property/preview";
+pub const TOKEN: &str = "https://atomicdata.dev/property/token";
// ... for Bookmarks
pub const IMAGE_URL: &str = "https://atomicdata.dev/properties/imageUrl";
// ... for Hierarchy / Drive
@@ -153,12 +157,22 @@ pub const PUBLIC_AGENT: &str = "https://atomicdata.dev/agents/publicAgent";
// We don't want a user to actually control this URL.
pub const SUDO_AGENT: &str = "sudo:agent";
-// Paths
-pub fn construct_path_import(base: &str) -> String {
- format!("{base}{PATH_IMPORT}")
-}
+/// The URL used for stores that are not accessible on the web.
+// I'd prefer this to a non-HTTP URI, but that causes parsing issues when we combine it with some paths (at least with Commits)
+pub const LOCAL_STORE: &str = "http://noresolve.localhost";
+// Paths
pub const PATH_IMPORT: &str = "/import";
pub const PATH_FETCH_BOOKMARK: &str = "/fetch-bookmark";
+pub const PATH_TPF: &str = "/tpf";
+pub const PATH_PATH: &str = "/path";
+pub const PATH_COMMITS: &str = "/commits";
+pub const PATH_ENDPOINTS: &str = "/endpoints";
+pub const PATH_REGISTER: &str = "/register";
+pub const PATH_CONFIRM_EMAIL: &str = "/confirm-email";
+pub const PATH_RESET_PUBKEY: &str = "/reset-public-key";
+pub const PATH_CONFIRM_PUBKEY: &str = "/confirm-public-key";
+pub const PATH_ADD_PUBKEY: &str = "/add-public-key";
+pub const PATH_CONFIRM_RESET: &str = "/confirm-reset-public-key";
pub const PATH_QUERY: &str = "/query";
pub const PATH_PRUNE_TESTS: &str = "/prunetests";
diff --git a/lib/src/values.rs b/lib/src/values.rs
index b6c75ace9..33603f1a2 100644
--- a/lib/src/values.rs
+++ b/lib/src/values.rs
@@ -2,7 +2,7 @@
use crate::{
datatype::match_datatype, datatype::DataType, errors::AtomicResult, resources::PropVals,
- utils::check_valid_url, Resource,
+ utils::check_valid_url, AtomicUrl, Resource,
};
use regex::Regex;
use serde::{Deserialize, Serialize};
@@ -353,6 +353,16 @@ impl From> for Value {
}
}
+impl From> for Value {
+ fn from(val: Vec) -> Self {
+ let mut vec = Vec::new();
+ for i in val {
+ vec.push(SubResource::Subject(i.to_string()));
+ }
+ Value::ResourceArray(vec)
+ }
+}
+
use std::fmt;
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
diff --git a/lib/test_files/local_id.json b/lib/test_files/local_id.json
index 8773e399b..d9610a5f5 100644
--- a/lib/test_files/local_id.json
+++ b/lib/test_files/local_id.json
@@ -1,17 +1,17 @@
[
{
- "https://atomicdata.dev/properties/localId": "reference",
+ "https://atomicdata.dev/properties/localId": "parent",
"https://atomicdata.dev/properties/write": [
- "my-local-id"
+ "parent/my-local-id"
],
"https://atomicdata.dev/properties/name": "My referenced resource"
},
{
- "https://atomicdata.dev/properties/localId": "my-local-id",
+ "https://atomicdata.dev/properties/localId": "parent/my-local-id",
"https://atomicdata.dev/properties/name": "My resource that refers",
- "https://atomicdata.dev/properties/parent": "reference",
+ "https://atomicdata.dev/properties/parent": "parent",
"https://atomicdata.dev/properties/write": [
- "reference"
+ "parent"
]
}
]
diff --git a/server/Cargo.toml b/server/Cargo.toml
index bebe64467..547dac130 100644
--- a/server/Cargo.toml
+++ b/server/Cargo.toml
@@ -19,7 +19,7 @@ name = "atomic-server"
path = "src/bin.rs"
[build-dependencies]
-dircpy = "0.3.15"
+dircpy = "0.3.19"
static-files = "0.2"
walkdir = "2"
@@ -44,8 +44,8 @@ sanitize-filename = "0.5"
serde_json = "1"
serde_with = "3.3.0"
simple-server-timing-header = "0.1.0"
-static-files = "0.2"
tantivy = "0.22"
+static-files = "0.2.3"
tracing = "0.1"
tracing-actix-web = "0.7"
tracing-chrome = "0.7"
@@ -55,6 +55,7 @@ urlencoding = "2"
image = "0.25.2"
webp = "0.3"
ravif = "0.11.8"
+url = "2.3.1"
[dependencies.instant-acme]
optional = true
diff --git a/server/build.rs b/server/build.rs
index a3d3234b8..76f7e27f0 100644
--- a/server/build.rs
+++ b/server/build.rs
@@ -21,40 +21,51 @@ fn main() -> std::io::Result<()> {
// Uncomment this line if you want faster builds during development
// return Ok(());
const BROWSER_ROOT: &str = "../browser/";
- let dirs: Dirs = {
- Dirs {
- js_dist_source: PathBuf::from("../browser/data-browser/dist"),
- js_dist_tmp: PathBuf::from("./assets_tmp"),
- src_browser: PathBuf::from("../browser/data-browser/src"),
- browser_root: PathBuf::from(BROWSER_ROOT),
- }
- };
println!("cargo:rerun-if-changed={}", BROWSER_ROOT);
- if should_build(&dirs) {
- build_js(&dirs);
- let _ = fs::remove_dir_all(&dirs.js_dist_tmp);
- dircpy::copy_dir(&dirs.js_dist_source, &dirs.js_dist_tmp)?;
- } else if dirs.js_dist_tmp.exists() {
- p!("Found {}, skipping copy", dirs.js_dist_tmp.display());
+ let is_check_like = false; // profile == "debug" && opt_level == "0";
+
+ if is_check_like {
+ println!("cargo:rerun-if-changed=build.rs");
+ // Skip the heavy logic
+ println!("Skipping build.rs logic for cargo check/clippy.");
} else {
- p!(
- "Could not find {} , copying from {}",
- dirs.js_dist_tmp.display(),
- dirs.js_dist_source.display()
- );
- dircpy::copy_dir(&dirs.js_dist_source, &dirs.js_dist_tmp)?;
- }
+ const BROWSER_ROOT: &str = "../browser/";
+ println!("cargo:rerun-if-changed={}", BROWSER_ROOT);
+ let dirs: Dirs = {
+ Dirs {
+ js_dist_source: PathBuf::from("../browser/data-browser/dist"),
+ js_dist_tmp: PathBuf::from("./assets_tmp"),
+ src_browser: PathBuf::from("../browser/data-browser/src"),
+ browser_root: PathBuf::from(BROWSER_ROOT),
+ }
+ };
+ //
+ if should_build(&dirs) {
+ build_js(&dirs);
+ let _ = fs::remove_dir_all(&dirs.js_dist_tmp);
+ dircpy::copy_dir(&dirs.js_dist_source, &dirs.js_dist_tmp)?;
+ } else if dirs.js_dist_tmp.exists() {
+ p!("Found {}, skipping copy", dirs.js_dist_tmp.display());
+ } else {
+ p!(
+ "Could not find {} , copying from {}",
+ dirs.js_dist_tmp.display(),
+ dirs.js_dist_source.display()
+ );
+ dircpy::copy_dir(&dirs.js_dist_source, &dirs.js_dist_tmp)?;
+ }
- // Makes the static files available for compilation
- static_files::resource_dir(&dirs.js_dist_tmp)
- .build()
- .unwrap_or_else(|_e| {
- panic!(
- "failed to open data browser assets from {}",
- dirs.js_dist_tmp.display()
- )
- });
+ // Makes the static files available for compilation
+ static_files::resource_dir(&dirs.js_dist_tmp)
+ .build()
+ .unwrap_or_else(|_e| {
+ panic!(
+ "failed to open data browser assets from {}",
+ dirs.js_dist_tmp.display()
+ )
+ });
+ }
Ok(())
}
diff --git a/server/src/appstate.rs b/server/src/appstate.rs
index fc6ca11ab..a6bc060e2 100644
--- a/server/src/appstate.rs
+++ b/server/src/appstate.rs
@@ -4,8 +4,10 @@ use crate::{
};
use atomic_lib::{
agents::{generate_public_key, Agent},
+ atomic_url::Routes,
commit::CommitResponse,
- Storelike,
+ email::SmtpConfig,
+ Db, Storelike,
};
/// The AppState contains all the relevant Context for the server.
@@ -25,11 +27,21 @@ pub struct AppState {
pub search_state: SearchState,
}
+/// Initializes the Store and sets the default agent.
+pub fn init_store(config: &Config) -> AtomicServerResult {
+ let store = atomic_lib::Db::init(&config.store_path, &config.server_url)?;
+
+ tracing::info!("Setting default agent");
+ set_default_agent(config, &store)?;
+
+ Ok(store)
+}
+
impl AppState {
/// Creates the AppState (the server's context available in Handlers).
/// Initializes or opens a store on disk.
/// Creates a new agent, if necessary.
- pub fn init(config: Config) -> AtomicServerResult {
+ pub async fn init(config: Config) -> AtomicServerResult {
tracing::info!("Initializing AppState");
// We warn over here because tracing needs to be initialized first.
@@ -40,19 +52,29 @@ impl AppState {
tracing::warn!("Development mode is enabled. This will use staging environments for services like LetsEncrypt.");
}
- let mut store = atomic_lib::Db::init(&config.store_path, config.server_url.clone())?;
+ let mut store = init_store(&config)?;
+
let no_server_resource = store.get_resource(&config.server_url).is_err();
if no_server_resource {
tracing::warn!("Server URL resource not found. This is likely because the server URL has changed. Initializing a new database...");
}
- let should_init = !&config.store_path.exists() || config.initialize || no_server_resource;
- if should_init {
- tracing::info!("Initialize: creating and populating new Database...");
+ let should_initialize =
+ !&config.store_path.exists() || config.initialize || no_server_resource;
+ if should_initialize {
+ tracing::warn!("Initialize: creating and populating new Database...");
atomic_lib::populate::populate_default_store(&store)
.map_err(|e| format!("Failed to populate default store. {}", e))?;
}
-
- set_default_agent(&config, &store)?;
+ if let Some(host) = &config.opts.smpt_host {
+ store
+ .set_smtp_config(SmtpConfig {
+ host: host.clone(),
+ port: config.opts.smpt_port,
+ })
+ .await?;
+ } else {
+ tracing::info!("No SMTP_HOST found, not starting email server.")
+ }
// Initialize search constructs
let search_state = SearchState::new(&config)
@@ -74,7 +96,7 @@ impl AppState {
// If the user changes their server_url, the drive will not exist.
// In this situation, we should re-build a new drive from scratch.
- if should_init {
+ if should_initialize {
atomic_lib::populate::populate_all(&store)?;
// Building the index here is needed to perform Queries on imported resources
let store_clone = store.clone();
@@ -105,6 +127,7 @@ impl AppState {
/// Cleanup code, writing buffers, committing changes, etc.
fn exit(&self) -> AtomicServerResult<()> {
self.search_state.writer.write()?.commit()?;
+ self.store.flush()?;
Ok(())
}
}
@@ -137,7 +160,7 @@ fn set_default_agent(config: &Config, store: &impl Storelike) -> AtomicServerRes
"server".into(),
store,
&agent_config.private_key,
- );
+ )?;
store.add_resource(&recreated_agent.to_resource()?)?;
agent_config
} else {
@@ -179,7 +202,7 @@ fn set_default_agent(config: &Config, store: &impl Storelike) -> AtomicServerRes
/// Creates the first Invitation that is opened by the user on the Home page.
fn set_up_initial_invite(store: &impl Storelike) -> AtomicServerResult<()> {
- let subject = format!("{}/setup", store.get_server_url());
+ let subject = store.get_server_url().set_route(Routes::Setup).to_string();
tracing::info!("Creating initial Invite at {}", subject);
let mut invite = store.get_resource_new(&subject);
invite.set_class(atomic_lib::urls::INVITE);
@@ -197,12 +220,12 @@ fn set_up_initial_invite(store: &impl Storelike) -> AtomicServerResult<()> {
)?;
invite.set(
atomic_lib::urls::TARGET.into(),
- atomic_lib::Value::AtomicUrl(store.get_server_url().into()),
+ atomic_lib::Value::AtomicUrl(store.get_server_url().to_string()),
store,
)?;
invite.set(
atomic_lib::urls::PARENT.into(),
- atomic_lib::Value::AtomicUrl(store.get_server_url().into()),
+ atomic_lib::Value::AtomicUrl(store.get_server_url().to_string()),
store,
)?;
invite.set(
diff --git a/server/src/bin.rs b/server/src/bin.rs
index e965c919b..dd27de76c 100644
--- a/server/src/bin.rs
+++ b/server/src/bin.rs
@@ -1,4 +1,4 @@
-use atomic_lib::{agents::ForAgent, urls, Storelike};
+use atomic_lib::{agents::ForAgent, atomic_url::Routes, Storelike};
use atomic_server_lib::config::Opts;
use std::{fs::File, io::Write};
@@ -48,7 +48,7 @@ async fn main_wrapped() -> errors::AtomicServerResult<()> {
pt
}
};
- let appstate = appstate::AppState::init(config.clone())?;
+ let appstate = appstate::AppState::init(config.clone()).await?;
let outstr = appstate.store.export(!e.only_internal)?;
std::fs::create_dir_all(path.parent().unwrap())
.map_err(|e| format!("Failed to create directory {:?}. {}", path, e))?;
@@ -64,11 +64,16 @@ async fn main_wrapped() -> errors::AtomicServerResult<()> {
std::fs::read_to_string(path)?
};
- let appstate = appstate::AppState::init(config.clone())?;
+ let appstate = appstate::AppState::init(config.clone()).await?;
let importer_subject = if let Some(i) = &import_opts.parent {
i.into()
} else {
- urls::construct_path_import(&appstate.store.get_self_url().expect("No self url"))
+ appstate
+ .store
+ .get_self_url()
+ .expect("No self URL")
+ .set_route(Routes::Import)
+ .to_string()
};
let parse_opts = atomic_lib::parse::ParseOpts {
importer: Some(importer_subject),
@@ -83,7 +88,6 @@ async fn main_wrapped() -> errors::AtomicServerResult<()> {
};
println!("Importing...");
appstate.store.import(&readstring, &parse_opts)?;
- appstate.search_state.add_all_resources(&appstate.store)?;
println!("Successfully imported {:?} to store.", import_opts.file);
println!("WARNING: Your search index is not yet updated with these imported items. Run `--rebuild-index` to fix that.");
Ok(())
diff --git a/server/src/commit_monitor.rs b/server/src/commit_monitor.rs
index 4e18a61ca..1b41c9d49 100644
--- a/server/src/commit_monitor.rs
+++ b/server/src/commit_monitor.rs
@@ -54,8 +54,9 @@ impl Handler for CommitMonitor {
fields(to = %msg.subject, agent = %msg.agent)
)]
fn handle(&mut self, msg: Subscribe, _ctx: &mut Context) {
- // check if the agent has the rights to subscribe to this resource
- if !msg.subject.starts_with(&self.store.get_self_url().unwrap()) {
+ // check if the agent has the rights to subscribe to this resourceif let Some(self_url) = self.get_self_url() {
+
+ if self.store.is_external_subject(&msg.subject).unwrap_or(true) {
tracing::warn!("can't subscribe to external resource");
return;
}
@@ -159,7 +160,7 @@ impl CommitMonitor {
impl Handler for CommitMonitor {
type Result = ();
- #[tracing::instrument(name = "handle_commit_message", skip_all, fields(subscriptions = &self.subscriptions.len(), s = %msg.commit_response.commit_resource.get_subject()))]
+ #[tracing::instrument(name = "handle_commit_message", level="debug", skip_all, fields(subscriptions = &self.subscriptions.len(), s = %msg.commit_response.commit_resource.get_subject()))]
fn handle(&mut self, msg: CommitMessage, _: &mut Context) {
// We have moved the logic to the `handle_internal` function for decent error handling
match self.handle_internal(msg) {
diff --git a/server/src/config.rs b/server/src/config.rs
index 6a0f60c96..e0dfb2729 100644
--- a/server/src/config.rs
+++ b/server/src/config.rs
@@ -75,6 +75,7 @@ pub struct Opts {
pub data_dir: Option,
/// CAUTION: Skip authentication checks, making all data publicly readable. Improves performance.
+
#[clap(long, env = "ATOMIC_PUBLIC_MODE")]
pub public_mode: bool,
@@ -94,6 +95,18 @@ pub struct Opts {
/// Introduces random delays in the server, to simulate a slow connection. Useful for testing.
#[clap(long, env = "ATOMIC_SLOW_MODE")]
pub slow_mode: bool,
+
+ /// Host address of an SMTP server. Add if you want so send e-mails (e.g. for user registration). Also set the port.
+ #[clap(long, env = "ATOMIC_SMTP_HOST")]
+ pub smpt_host: Option,
+
+ /// Port of your SMTP server.
+ #[clap(long, env = "ATOMIC_SMTP_PORT", default_value = "25")]
+ pub smpt_port: u16,
+
+ /// If you want to parse options from a specific .env file. By default reads the `./.env` file.
+ #[clap(long)]
+ pub env_file: Option,
}
#[derive(clap::ValueEnum, Clone, Debug)]
@@ -200,7 +213,14 @@ pub fn read_opts() -> Opts {
dotenv().ok();
// Parse CLI options, .env values, set defaults
- Opts::parse()
+ let mut opts = Opts::parse();
+ if let Some(env_path) = &opts.env_file {
+ dotenv::from_path(env_path)
+ .unwrap_or_else(|_| panic!("Env file not found: {} ", env_path.display()));
+ // Parse the opts again so the CLI opts override the .env file
+ opts = Opts::parse();
+ }
+ opts
}
/// Creates the server config, reads .env values and sets defaults
@@ -260,7 +280,11 @@ pub fn build_config(opts: Opts) -> AtomicServerResult {
// This logic could be a bit too complicated, but I'm not sure on how to make this simpler.
let server_url = if let Some(addr) = opts.server_url.clone() {
- addr
+ if addr.ends_with('/') {
+ return Err("The Server URL should not end with a trailing slash.".into());
+ } else {
+ addr
+ }
} else if opts.https && opts.port_https == 443 || !opts.https && opts.port == 80 {
format!("{}://{}", schema, opts.domain)
} else {
diff --git a/server/src/errors.rs b/server/src/errors.rs
index 6cf565216..b0e144c47 100644
--- a/server/src/errors.rs
+++ b/server/src/errors.rs
@@ -14,7 +14,7 @@ pub enum AppErrorType {
Other,
}
-// More strict error type, supports HTTP responses
+/// Error type that includes a Resource representation of the Error, which can be sent to the client.
pub struct AtomicServerError {
pub message: String,
pub error_type: AppErrorType,
@@ -49,8 +49,8 @@ impl ResponseError for AtomicServerError {
}
fn error_response(&self) -> HttpResponse {
// Creates a JSON-AD resource representing the Error.
- let r = match &self.error_resource {
- Some(r) => r.to_owned(),
+ let r: Resource = match &self.error_resource {
+ Some(r) => *r.clone(),
None => {
let mut r = Resource::new("subject".into());
r.set_class(urls::ERROR);
@@ -58,12 +58,12 @@ impl ResponseError for AtomicServerError {
urls::DESCRIPTION.into(),
Value::String(self.message.clone()),
);
- Box::new(r)
+ r
}
};
let body = r.to_json_ad().unwrap();
- tracing::info!("Error response: {}", self.message);
+ // tracing::info!("Error response: {}", self.message);
HttpResponse::build(self.status_code())
.content_type(JSON_AD_MIME)
.body(body)
@@ -179,3 +179,13 @@ impl From for AtomicServerError {
}
}
}
+
+impl From for AtomicServerError {
+ fn from(error: url::ParseError) -> Self {
+ AtomicServerError {
+ message: error.to_string(),
+ error_type: AppErrorType::Other,
+ error_resource: None,
+ }
+ }
+}
diff --git a/server/src/handlers/commit.rs b/server/src/handlers/commit.rs
index 43a7e2797..79f53bbd3 100644
--- a/server/src/handlers/commit.rs
+++ b/server/src/handlers/commit.rs
@@ -19,12 +19,8 @@ pub async fn post_commit(
let mut builder = HttpResponse::Ok();
let incoming_commit_resource = parse_json_ad_commit_resource(&body, store)?;
let incoming_commit = Commit::from_resource(incoming_commit_resource)?;
- if !incoming_commit.subject.contains(
- &store
- .get_self_url()
- .ok_or("Cannot apply commits to this store. No self_url is set.")?,
- ) {
- return Err("Subject of commit should be sent to other domain - this store can not own this resource.".into());
+ if store.is_external_subject(&incoming_commit.subject)? {
+ return Err("Subject of commit is external, and should be sent to its origin domain. This store can not own this resource. See https://github.com/atomicdata-dev/atomic-data-rust/issues/509".into());
}
let opts = CommitOpts {
validate_schema: true,
diff --git a/server/src/handlers/download.rs b/server/src/handlers/download.rs
index 71d2a2ca3..a91e4e1eb 100644
--- a/server/src/handlers/download.rs
+++ b/server/src/handlers/download.rs
@@ -5,6 +5,7 @@ use atomic_lib::{urls, Resource, Storelike};
use image::GenericImageView;
use image::{codecs::avif::AvifEncoder, ImageReader};
use serde::Deserialize;
+use std::path::Path;
use std::{collections::HashSet, io::Write, path::PathBuf};
#[serde_with::serde_as]
@@ -25,13 +26,16 @@ pub async fn handle_download(
req: actix_web::HttpRequest,
) -> AtomicServerResult {
let headers = req.headers();
- let server_url = &appstate.config.server_url;
let store = &appstate.store;
// We replace `/download` with `/` to get the subject of the Resource.
let subject = if let Some(pth) = path {
- let subject = format!("{}/{}", server_url, pth);
- subject
+ appstate
+ .store
+ .get_server_url()
+ .clone()
+ .set_path(pth.as_str())
+ .to_string()
} else {
// There is no end string, so It's the root of the URL, the base URL!
return Err("Put `/download` in front of an File URL to download it.".into());
@@ -161,8 +165,8 @@ fn process_image(
Err(format!("Unsupported format: {}", format).into())
}
-fn create_processed_folder_if_not_exists(base_path: &PathBuf) -> AtomicServerResult<()> {
- let mut processed_folder = base_path.clone();
+fn create_processed_folder_if_not_exists(base_path: &Path) -> AtomicServerResult<()> {
+ let mut processed_folder = base_path.to_path_buf();
processed_folder.push("processed");
std::fs::create_dir_all(processed_folder)?;
Ok(())
diff --git a/server/src/handlers/export.rs b/server/src/handlers/export.rs
index dc9449f79..5586ee67a 100644
--- a/server/src/handlers/export.rs
+++ b/server/src/handlers/export.rs
@@ -5,8 +5,8 @@ use actix_web::http::header::{ContentDisposition, DispositionParam, DispositionT
use actix_web::{web, HttpResponse};
use atomic_lib::agents::ForAgent;
use atomic_lib::errors::AtomicResult;
-use atomic_lib::storelike::Query;
use atomic_lib::values::SubResource;
+use atomic_lib::Query;
use atomic_lib::{urls, Db, Resource, Storelike, Value};
use chrono::DateTime;
use serde::Deserialize;
@@ -225,18 +225,16 @@ impl<'a> CSVExporter<'a> {
fn value_to_string(&self, value: &Value) -> String {
match value {
Value::Timestamp(ts) => {
- // Convert the timestamp to a NaiveDateTime (no timezone)
let seconds = ts / 1000;
let remaining_nanoseconds = (ts % 1000) * 1_000_000; // Convert remaining milliseconds to nanoseconds
- let Some(datetime) =
+ let Some(date_time) =
DateTime::from_timestamp(seconds, remaining_nanoseconds as u32)
else {
return ts.to_string();
};
- // Format the DateTime as a string in RFC3339 format (e.g., "2023-03-20T12:34:56Z")
- datetime.to_rfc3339()
+ date_time.to_rfc3339()
}
Value::ResourceArray(values) => {
let names: Vec = values
diff --git a/server/src/handlers/get_resource.rs b/server/src/handlers/get_resource.rs
index 1ef8f199c..184543f88 100644
--- a/server/src/handlers/get_resource.rs
+++ b/server/src/handlers/get_resource.rs
@@ -1,9 +1,8 @@
use crate::{
appstate::AppState,
- content_types::get_accept,
content_types::ContentType,
errors::AtomicServerResult,
- helpers::{get_client_agent, try_extension},
+ helpers::{get_client_agent, get_subject},
};
use actix_web::{web, HttpResponse};
use atomic_lib::Storelike;
@@ -16,39 +15,13 @@ pub async fn handle_get_resource(
path: Option>,
appstate: web::Data,
req: actix_web::HttpRequest,
+ conn: actix_web::dev::ConnectionInfo,
) -> AtomicServerResult {
let mut timer = Timer::new();
let headers = req.headers();
- let mut content_type = get_accept(headers);
- let server_url = &appstate.config.server_url;
// Get the subject from the path, or return the home URL
- let subject = if let Some(subj_end) = path {
- let mut subj_end_string = subj_end.as_str();
- // If the request is for the root, return the home URL
- if subj_end_string.is_empty() {
- server_url.to_string()
- } else {
- if content_type == ContentType::Html {
- if let Some((ext, path)) = try_extension(subj_end_string) {
- content_type = ext;
- subj_end_string = path;
- }
- }
- // Check extensions and set datatype. Harder than it looks to get right...
- // This might not be the best way of creating the subject. But I can't access the full URL from any actix stuff!
- let querystring = if req.query_string().is_empty() {
- "".to_string()
- } else {
- format!("?{}", req.query_string())
- };
- let subject = format!("{}/{}{}", server_url, subj_end_string, querystring);
- subject
- }
- } else {
- // There is no end string, so It's the root of the URL, the base URL!
- String::from(server_url)
- };
+ let (subject, content_type) = get_subject(&req, &conn, &appstate)?;
let store = &appstate.store;
timer.add("parse_headers");
diff --git a/server/src/handlers/post_resource.rs b/server/src/handlers/post_resource.rs
index 881467751..5c676e612 100644
--- a/server/src/handlers/post_resource.rs
+++ b/server/src/handlers/post_resource.rs
@@ -1,9 +1,8 @@
use crate::{
appstate::AppState,
- content_types::get_accept,
content_types::ContentType,
errors::AtomicServerResult,
- helpers::{get_client_agent, try_extension},
+ helpers::{get_client_agent, get_subject},
};
use actix_web::{web, HttpResponse};
use atomic_lib::Storelike;
@@ -16,39 +15,13 @@ pub async fn handle_post_resource(
appstate: web::Data,
req: actix_web::HttpRequest,
body: web::Bytes,
+ conn: actix_web::dev::ConnectionInfo,
) -> AtomicServerResult {
let mut timer = Timer::new();
let headers = req.headers();
- let mut content_type = get_accept(headers);
- let server_url = &appstate.config.server_url;
// Get the subject from the path, or return the home URL
- let subject = if let Some(subj_end) = path {
- let mut subj_end_string = subj_end.as_str();
- // If the request is for the root, return the home URL
- if subj_end_string.is_empty() {
- server_url.to_string()
- } else {
- if content_type == ContentType::Html {
- if let Some((ext, path)) = try_extension(subj_end_string) {
- content_type = ext;
- subj_end_string = path;
- }
- }
- // Check extensions and set datatype. Harder than it looks to get right...
- // This might not be the best way of creating the subject. But I can't access the full URL from any actix stuff!
- let querystring = if req.query_string().is_empty() {
- "".to_string()
- } else {
- format!("?{}", req.query_string())
- };
- let subject = format!("{}/{}{}", server_url, subj_end_string, querystring);
- subject
- }
- } else {
- // There is no end string, so It's the root of the URL, the base URL!
- String::from(server_url)
- };
+ let (subject, content_type) = get_subject(&req, &conn, &appstate)?;
let store = &appstate.store;
timer.add("parse_headers");
diff --git a/server/src/handlers/search.rs b/server/src/handlers/search.rs
index b314f7182..0c82addfd 100644
--- a/server/src/handlers/search.rs
+++ b/server/src/handlers/search.rs
@@ -5,6 +5,7 @@
use crate::{
appstate::AppState,
errors::{AtomicServerError, AtomicServerResult},
+ helpers::get_subject,
search::{resource_to_facet, Fields},
};
use actix_web::{web, HttpResponse};
@@ -15,7 +16,7 @@ use simple_server_timing_header::Timer;
use tantivy::{
collector::TopDocs,
query::{BooleanQuery, BoostQuery, Occur, Query, QueryParser, TermQuery},
- schema::IndexRecordOption,
+ schema::{IndexRecordOption, OwnedValue},
tokenizer::{TokenStream, Tokenizer},
Term,
};
@@ -54,6 +55,7 @@ pub async fn search_query(
appstate: web::Data,
params: web::Query,
req: actix_web::HttpRequest,
+ conn: actix_web::dev::ConnectionInfo,
) -> AtomicServerResult {
let mut timer = Timer::new();
let store = &appstate.store;
@@ -82,12 +84,7 @@ pub async fn search_query(
let subjects = docs_to_subjects(top_docs, &fields, &searcher)?;
// Create a valid atomic data resource.
- // You'd think there would be a simpler way of getting the requested URL...
- let subject = format!(
- "{}{}",
- store.get_self_url().ok_or("No base URL set")?,
- req.uri().path_and_query().ok_or("Add a query param")?
- );
+ let (subject, _) = get_subject(&req, &conn, &appstate)?;
let mut results_resource = atomic_lib::plugins::search::search_endpoint().to_resource(store)?;
results_resource.set_subject(subject.clone());
@@ -266,12 +263,12 @@ fn build_parent_query(subject: &str, fields: &Fields, store: &Db) -> AtomicServe
}
fn unpack_value(
- value: &tantivy::schema::OwnedValue,
+ value: &OwnedValue,
document: &tantivy::TantivyDocument,
name: String,
) -> Result {
match value {
- tantivy::schema::OwnedValue::Str(s) => Ok(s.to_string()),
+ OwnedValue::Str(s) => Ok(s.to_string()),
_else => Err(format!(
"Search schema error: {} is not a string! Doc: {:?}",
name, document
diff --git a/server/src/handlers/single_page_app.rs b/server/src/handlers/single_page_app.rs
index 6140a2c3b..766f1495c 100644
--- a/server/src/handlers/single_page_app.rs
+++ b/server/src/handlers/single_page_app.rs
@@ -1,6 +1,7 @@
use std::fmt::Display;
use std::fmt::Formatter;
+use crate::helpers::get_subject;
use crate::{appstate::AppState, errors::AtomicServerResult};
use actix_web::HttpResponse;
@@ -9,9 +10,11 @@ use actix_web::HttpResponse;
pub async fn single_page(
appstate: actix_web::web::Data,
path: actix_web::web::Path,
+ req: actix_web::HttpRequest,
+ conn: actix_web::dev::ConnectionInfo,
) -> AtomicServerResult {
let template = include_str!("../../assets_tmp/index.html");
- let subject = format!("{}/{}", appstate.store.get_server_url(), path);
+ let (subject, _content_type) = get_subject(&req, &conn, &appstate)?;
let meta_tags: MetaTags = if let Ok(resource) =
appstate
.store
@@ -35,6 +38,7 @@ pub async fn single_page(
"Cache-Control",
"no-store, no-cache, must-revalidate, private",
))
+ .append_header(("Vary", "Accept"))
.body(body);
Ok(resp)
@@ -99,7 +103,10 @@ impl Default for MetaTags {
impl Display for MetaTags {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
- let description = escape_html(&self.description);
+ let description = escape_html(&self.description)
+ .chars()
+ .take(250)
+ .collect::();
let image = &self.image;
let title = escape_html(&self.title);
diff --git a/server/src/handlers/upload.rs b/server/src/handlers/upload.rs
index 189332f90..7eb1adcd0 100644
--- a/server/src/handlers/upload.rs
+++ b/server/src/handlers/upload.rs
@@ -7,7 +7,11 @@ use futures::{StreamExt, TryStreamExt};
use image::GenericImageView;
use serde::Deserialize;
-use crate::{appstate::AppState, errors::AtomicServerResult, helpers::get_client_agent};
+use crate::{
+ appstate::AppState,
+ errors::AtomicServerResult,
+ helpers::{get_client_agent, get_subject},
+};
#[derive(Deserialize, Debug)]
pub struct UploadQuery {
@@ -25,19 +29,13 @@ pub async fn upload_handler(
appstate: web::Data,
query: web::Query,
req: actix_web::HttpRequest,
+ conn: actix_web::dev::ConnectionInfo,
) -> AtomicServerResult {
let store = &appstate.store;
let parent = store.get_resource(&query.parent)?;
- let subject = format!(
- "{}{}",
- store.get_server_url(),
- req.head()
- .uri
- .path_and_query()
- .ok_or("Path must be given")?
- );
- let agent = get_client_agent(req.headers(), &appstate, subject)?;
- check_write(store, &parent, &agent)?;
+ let (subject, _) = get_subject(&req, &conn, &appstate)?;
+ let for_agent = get_client_agent(req.headers(), &appstate, subject)?;
+ check_write(store, &parent, &for_agent)?;
let mut created_resources: Vec = Vec::new();
diff --git a/server/src/helpers.rs b/server/src/helpers.rs
index 4fc776de6..db255d67b 100644
--- a/server/src/helpers.rs
+++ b/server/src/helpers.rs
@@ -6,11 +6,13 @@ use actix_web::http::Uri;
use atomic_lib::agents::ForAgent;
use atomic_lib::authentication::AuthValues;
use atomic_lib::AtomicError;
+use atomic_lib::Storelike;
use percent_encoding::percent_decode_str;
use std::str::FromStr;
+use crate::content_types::{get_accept, ContentType};
use crate::errors::{AppErrorType, AtomicServerError};
-use crate::{appstate::AppState, content_types::ContentType, errors::AtomicServerResult};
+use crate::{appstate::AppState, errors::AtomicServerResult};
/// Returns the authentication headers from the request
#[tracing::instrument(skip_all)]
@@ -67,6 +69,7 @@ fn origin(url: &str) -> String {
)
}
+/// Checks if the origin in the Cookie matches the requested subject.
pub fn get_auth_from_cookie(
headers: &HeaderMap,
requested_subject: &str,
@@ -164,6 +167,7 @@ pub fn get_auth(
/// Checks for authentication headers and returns Some agent's subject if everything is well.
/// Skips these checks in public_mode and returns Ok(None).
+/// Returns the Agent's subject or the Public Agent.
#[tracing::instrument(skip(appstate))]
pub fn get_client_agent(
headers: &HeaderMap,
@@ -183,24 +187,6 @@ pub fn get_client_agent(
Ok(for_agent)
}
-/// Finds the extension
-pub fn try_extension(path: &str) -> Option<(ContentType, &str)> {
- let items: Vec<&str> = path.split('.').collect();
- if items.len() == 2 {
- let path = items[0];
- let content_type = match items[1] {
- "json" => ContentType::Json,
- "jsonld" => ContentType::JsonLd,
- "jsonad" => ContentType::JsonAd,
- "html" => ContentType::Html,
- "ttl" => ContentType::Turtle,
- _ => return None,
- };
- return Some((content_type, path));
- }
- None
-}
-
fn session_cookies_from_header(header: &HeaderValue) -> AtomicServerResult> {
let cookies: Vec<&str> = header
.to_str()
@@ -296,3 +282,77 @@ mod test {
assert_eq!(out.requested_subject, subject);
}
}
+
+/// Extracts the full URL from the request, connection and the store.
+// You'd think there would be a simpler way of getting the requested URL...
+// See https://github.com/actix/actix-web/issues/2895
+pub fn get_subject(
+ req: &actix_web::HttpRequest,
+ conn: &actix_web::dev::ConnectionInfo,
+ appstate: &AppState,
+) -> AtomicServerResult<(String, ContentType)> {
+ let content_type = get_accept(req.headers());
+
+ let domain = &appstate.config.opts.domain;
+ let host = conn.host();
+ let subdomain = if let Some(index) = host.find(domain) {
+ if index == 0 {
+ None
+ } else {
+ Some(host[0..index - 1].to_string())
+ }
+ } else {
+ panic!("Wrong domain! A requested URL did not contain the host for this domain. This should not be able to happen.");
+ };
+
+ let mut subject_url = appstate.store.get_server_url().clone();
+ if let Some(sd) = subdomain {
+ subject_url.set_subdomain(Some(&sd))?;
+ }
+ let server_without_last_slash = subject_url.to_string().trim_end_matches('/').to_string();
+ let subject = format!("{}{}", server_without_last_slash, &req.uri().to_string());
+ // if let Some((ct, path)) = try_extension(req.path()) {
+ // content_type = ct;
+ // return Ok((path.to_string(), content_type));
+ // }
+ Ok((subject, content_type))
+}
+
+/// Finds the extension of a supported serialization format.
+/// Not used right now, see: https://github.com/atomicdata-dev/atomic-data-rust/issues/601
+#[allow(dead_code)]
+fn try_extension(path: &str) -> Option<(ContentType, &str)> {
+ // Check if path ends with one of the folliwing extensions
+ let extensions = [
+ ".json",
+ ".jsonld",
+ ".jsonad",
+ ".html",
+ ".ttl",
+ ".nt",
+ ".nq",
+ ".ntriples",
+ ".nt",
+ ];
+ let mut found = None;
+ for ext in extensions.iter() {
+ if path.ends_with(ext) {
+ println!("Found extension: {}", ext);
+ let path = &path[0..path.len() - ext.len()];
+ let content_type = match *ext {
+ ".json" => Some(ContentType::Json),
+ ".jsonld" => Some(ContentType::JsonLd),
+ ".jsonad" => Some(ContentType::JsonAd),
+ ".html" => Some(ContentType::Html),
+ ".ttl" => Some(ContentType::Turtle),
+ ".nt" => Some(ContentType::NTriples),
+ ".ntriples" => Some(ContentType::NTriples),
+ _ => None,
+ };
+ if let Some(ct) = content_type {
+ found = Some((ct, path));
+ }
+ }
+ }
+ found
+}
diff --git a/server/src/https.rs b/server/src/https.rs
index 4003f20e4..34c5da947 100644
--- a/server/src/https.rs
+++ b/server/src/https.rs
@@ -287,7 +287,7 @@ pub async fn request_cert(config: &crate::config::Config) -> AtomicServerResult<
// Exponentially back off until the order becomes ready or invalid.
let mut tries = 1u8;
let mut delay = std::time::Duration::from_millis(250);
- let url = authorizations.get(0).expect("Authorizations is empty");
+ let url = authorizations.first().expect("Authorizations is empty");
let state = loop {
let state = order.state();
info!("Order state: {:#?}", state);
diff --git a/server/src/search.rs b/server/src/search.rs
index 0de28ca12..61ae75b65 100644
--- a/server/src/search.rs
+++ b/server/src/search.rs
@@ -250,7 +250,7 @@ mod tests {
use super::resource_to_facet;
#[test]
fn facet_contains_subfacet() {
- let store = atomic_lib::Db::init_temp("facet_contains").unwrap();
+ let store = atomic_lib::Db::init_temp().unwrap();
let mut prev_subject: Option = None;
let mut resources = Vec::new();
diff --git a/server/src/serve.rs b/server/src/serve.rs
index 9f7440375..7ce59e7bf 100644
--- a/server/src/serve.rs
+++ b/server/src/serve.rs
@@ -38,7 +38,7 @@ pub async fn serve(config: crate::config::Config) -> AtomicServerResult<()> {
let tracing_chrome_flush_guard = crate::trace::init_tracing(&config);
// Setup the database and more
- let appstate = crate::appstate::AppState::init(config.clone())?;
+ let appstate = crate::appstate::AppState::init(config.clone()).await?;
// Start async processes
if config.opts.rebuild_indexes {
@@ -112,6 +112,7 @@ pub async fn serve(config: crate::config::Config) -> AtomicServerResult<()> {
if let Some(guard) = tracing_chrome_flush_guard {
guard.flush()
}
+ drop(config);
tracing::info!("Server stopped");
Ok(())
diff --git a/server/src/tests.rs b/server/src/tests.rs
index ccd9d8424..eccd472f7 100644
--- a/server/src/tests.rs
+++ b/server/src/tests.rs
@@ -16,7 +16,9 @@ use atomic_lib::{urls, Storelike};
/// Returns the request with signed headers. Also adds a json-ad accept header - overwrite this if you need something else.
fn build_request_authenticated(path: &str, appstate: &AppState) -> TestRequest {
- let url = format!("{}{}", appstate.store.get_server_url(), path);
+ // remove last slash
+ let base = appstate.store.get_server_url().to_string();
+ let url = format!("{}{}", base.trim_end_matches('/'), path);
let headers = atomic_lib::client::get_authentication_headers(
&url,
&appstate.store.get_default_agent().unwrap(),
@@ -49,7 +51,9 @@ async fn server_tests() {
// This prevents folder access issues when running concurrent tests
config.search_index_path = format!("./.temp/{}/search_index", unique_string).into();
- let appstate = crate::appstate::AppState::init(config.clone()).expect("failed init appstate");
+ let appstate = crate::appstate::AppState::init(config.clone())
+ .await
+ .expect("failed to init appstate");
let data = Data::new(appstate.clone());
let app = test::init_service(
App::new()
@@ -75,8 +79,8 @@ async fn server_tests() {
assert!(body.as_str().contains("html"));
// Should 200 (public)
- let req =
- test::TestRequest::with_uri("/properties").insert_header(("Accept", "application/ad+json"));
+ let req = test::TestRequest::with_uri("/collections/properties")
+ .insert_header(("Accept", "application/ad+json"));
let resp = test::call_service(&app, req.to_request()).await;
assert_eq!(
resp.status().as_u16(),
@@ -91,8 +95,10 @@ async fn server_tests() {
let resp = test::call_service(&app, req).await;
assert!(resp.status().is_client_error());
- // Edit the main drive, make it hidden to the public agent
- let mut drive = store.get_resource(&appstate.config.server_url).unwrap();
+ // Edit the properties collection, make it hidden to the public agent
+ let mut drive = store
+ .get_resource(appstate.store.get_server_url().as_str())
+ .unwrap();
drive
.set(
urls::READ.into(),
@@ -103,8 +109,8 @@ async fn server_tests() {
drive.save(store).unwrap();
// Should 401 (Unauthorized)
- let req =
- test::TestRequest::with_uri("/properties").insert_header(("Accept", "application/ad+json"));
+ let req = test::TestRequest::with_uri("/collections/properties")
+ .insert_header(("Accept", "application/ad+json"));
let resp = test::call_service(&app, req.to_request()).await;
assert_eq!(
resp.status().as_u16(),
@@ -113,17 +119,18 @@ async fn server_tests() {
);
// Get JSON-AD
- let req = build_request_authenticated("/properties", &appstate);
+ let req = build_request_authenticated("/collections/properties", &appstate);
let resp = test::call_service(&app, req.to_request()).await;
- assert!(resp.status().is_success(), "setup not returning JSON-AD");
let body = get_body(resp);
+ println!("DEBUG: {:?}", body);
+ // assert!(resp.status().is_success(), "setup not returning JSON-AD");
assert!(
body.as_str().contains("{\n \"@id\""),
"response should be json-ad"
);
// Get JSON-LD
- let req = build_request_authenticated("/properties", &appstate)
+ let req = build_request_authenticated("/collections/properties", &appstate)
.insert_header(("Accept", "application/ld+json"));
let resp = test::call_service(&app, req.to_request()).await;
assert!(resp.status().is_success(), "setup not returning JSON-LD");
@@ -134,7 +141,7 @@ async fn server_tests() {
);
// Get turtle
- let req = build_request_authenticated("/properties", &appstate)
+ let req = build_request_authenticated("/collections/properties", &appstate)
.insert_header(("Accept", "text/turtle"));
let resp = test::call_service(&app, req.to_request()).await;
assert!(resp.status().is_success());
@@ -157,7 +164,7 @@ async fn server_tests() {
);
}
-/// Gets the body from the response as a String. Why doen't actix provide this?
+/// Gets the body from the response as a String. Why doesn't actix provide this?
fn get_body(resp: ServiceResponse) -> String {
let boxbody = resp.into_body();
let bytes = boxbody.try_into_bytes().unwrap();