diff --git a/Cargo.lock b/Cargo.lock index 9f4e9666..afe0525b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -367,6 +367,26 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" +[[package]] +name = "async-priority-channel" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c21678992e1b21bebfe2bc53ab5f5f68c106eddab31b24e0bb06e9b715a86640" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-recursion" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b015a331cc64ebd1774ba119538573603427eaace0a1950c423ab971f903796" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "async-trait" version = "0.1.63" @@ -378,6 +398,12 @@ dependencies = [ "syn", ] +[[package]] +name = "atomic-take" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8ab6b55fe97976e46f91ddbed8d147d966475dc29b2032757ba47e02376fbc3" + [[package]] name = "atty" version = "0.2.14" @@ -858,6 +884,59 @@ dependencies = [ "zeroize", ] +[[package]] +name = "axum" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5694b64066a2459918d8074c2ce0d5a88f409431994c2356617c8ae0c4721fc" +dependencies = [ + "async-trait", + "axum-core", + "base64 0.20.0", + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sha1", + "sync_wrapper", + "tokio", + "tokio-tungstenite", + "tower", + "tower-http", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cae3e661676ffbacb30f1a824089a8c9150e71017f7e1e38f2aa32009188d34" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + [[package]] name = "base64" version = "0.13.1" @@ -930,9 +1009,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b45ea9b00a7b3f2988e9a65ad3917e62123c38dba709b666506207be96d1790b" dependencies = [ "memchr", + "once_cell", + "regex-automata", "serde", ] +[[package]] +name = "btoi" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97c0869a9faa81f8bbf8102371105d6d0a7b79167a04c340b04ab16892246a11" +dependencies = [ + "num-traits", +] + [[package]] name = "bumpalo" version = "3.12.0" @@ -970,6 +1060,48 @@ dependencies = [ "bytes", ] +[[package]] +name = "camino" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c77df041dc383319cc661b428b6961a005db4d6808d5e12536931b1ca9556055" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08a1ec454bc3eead8719cb56e15dbbfecdbc14e4b3a3ae4936cc6e31f5fc0d07" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "cargo_toml" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bfbc36312494041e2cdd5f06697b7e89d4b76f42773a0b5556ac290ff22acc2" +dependencies = [ + "serde", + "toml", +] + [[package]] name = "cc" version = "1.0.79" @@ -1083,6 +1215,19 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "clearscreen" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41aa24cc5e1d6b3fc49ad4cd540b522fedcbe88bc6f259ff16e20e7010b6f8c7" +dependencies = [ + "nix", + "terminfo", + "thiserror", + "which", + "winapi", +] + [[package]] name = "codespan-reporting" version = "0.11.1" @@ -1093,6 +1238,18 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "command-group" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "026c3922235f9f7d78f21251a026f3acdeb7cce3deba107fe09a4bfa63d850a2" +dependencies = [ + "async-trait", + "nix", + "tokio", + "winapi", +] + [[package]] name = "console" version = "0.15.5" @@ -1189,12 +1346,17 @@ dependencies = [ "actix-web", "actix-web-httpauth", "anyhow", + "async-priority-channel", "aws-config", "aws-endpoint 0.14.0", "aws-sdk-s3", "aws-types 0.8.0", + "axum", "base64 0.21.0", + "cargo_metadata", + "cargo_toml", "chrono", + "clearscreen", "derive_more", "diesel", "diesel_derives", @@ -1210,8 +1372,10 @@ dependencies = [ "libsqlite3-sys", "md5", "mime_guess", + "open", "poem", "rand", + "reqwest", "rust-argon2", "serde", "serde_json", @@ -1221,6 +1385,7 @@ dependencies = [ "utoipa 3.0.0", "utoipa-swagger-ui", "uuid", + "watchexec", ] [[package]] @@ -1242,6 +1407,16 @@ dependencies = [ "walkdir", ] +[[package]] +name = "crossbeam-channel" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.14" @@ -1324,6 +1499,19 @@ dependencies = [ "syn", ] +[[package]] +name = "dashmap" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" +dependencies = [ + "cfg-if", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "derive_more" version = "0.99.17" @@ -1512,6 +1700,12 @@ dependencies = [ "libc", ] +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + [[package]] name = "fastrand" version = "1.8.0" @@ -1521,6 +1715,18 @@ dependencies = [ "instant", ] +[[package]] +name = "filetime" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e884668cd0c7480504233e951174ddc3b382f7c2666e3b7310b5c4e7b0c37f9" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "windows-sys", +] + [[package]] name = "flate2" version = "1.0.25" @@ -1561,6 +1767,15 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + [[package]] name = "futures" version = "0.3.25" @@ -1681,6 +1896,194 @@ dependencies = [ "polyval", ] +[[package]] +name = "git-actor" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "599cb75eb7c3bf03149b7ab70c66bc0b9a432a092b1154b49d21b49fc7e4bd93" +dependencies = [ + "bstr", + "btoi", + "git-date", + "itoa", + "nom 7.1.3", + "quick-error", +] + +[[package]] +name = "git-config" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1b95089db62159d7d24c13ddfb4bf949c508521d0bb25331744ef500295ceb8" +dependencies = [ + "bstr", + "git-config-value", + "git-features", + "git-glob", + "git-path", + "git-ref", + "git-sec", + "memchr", + "nom 7.1.3", + "once_cell", + "smallvec", + "thiserror", + "unicode-bom", +] + +[[package]] +name = "git-config-value" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "989a90c1c630513a153c685b4249b96fdf938afc75bf7ef2ae1ccbd3d799f5db" +dependencies = [ + "bitflags", + "bstr", + "git-path", + "libc", + "thiserror", +] + +[[package]] +name = "git-date" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a2874ce2f3a77cb144167901ea830969e5c991eac7bfee85e6e3f53ef9fcdf2" +dependencies = [ + "bstr", + "itoa", + "thiserror", + "time", +] + +[[package]] +name = "git-features" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0019327672cb759f851d1b18fdcc36bb797dc62b925cb93c8c881b54735eb2c2" +dependencies = [ + "git-hash", + "libc", + "sha1_smol", + "walkdir", +] + +[[package]] +name = "git-glob" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa73cf9c9c1a66e28de1cf250fc1ebe323e7c7c59768c1a2331e3b3308e783a3" +dependencies = [ + "bitflags", + "bstr", +] + +[[package]] +name = "git-hash" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1532d82bf830532f8d545c5b7b568e311e3593f16cf7ee9dd0ce03c74b12b99d" +dependencies = [ + "hex", + "thiserror", +] + +[[package]] +name = "git-lock" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7cf6a3c9d1a9932bb9bcb7e0044e2e429f9d94711969a7d2a09e34ae21f6437" +dependencies = [ + "fastrand", + "git-tempfile", + "quick-error", +] + +[[package]] +name = "git-object" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad6c2ddb376b99172dc8b651e11be4cb49cef423de4fad563ebbda4fee3fcf6" +dependencies = [ + "bstr", + "btoi", + "git-actor", + "git-features", + "git-hash", + "git-validate", + "hex", + "itoa", + "nom 7.1.3", + "smallvec", + "thiserror", +] + +[[package]] +name = "git-path" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e40e68481a06da243d3f4dfd86a4be39c24eefb535017a862e845140dcdb878a" +dependencies = [ + "bstr", + "thiserror", +] + +[[package]] +name = "git-ref" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b351af399166a5506369e36389d6b9aee744cfa672d0e2ea93d2b01223b1cfbe" +dependencies = [ + "git-actor", + "git-features", + "git-hash", + "git-lock", + "git-object", + "git-path", + "git-tempfile", + "git-validate", + "memmap2", + "nom 7.1.3", + "thiserror", +] + +[[package]] +name = "git-sec" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6696a816445a51f76995d579a3122f98247377cc45cd681764f740f3a2666004" +dependencies = [ + "bitflags", + "dirs", + "git-path", + "libc", + "windows", +] + +[[package]] +name = "git-tempfile" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d851911a2b043dc1ab6cd5432ce7a3ee3a2fd614ed87428cec1b15f5abb7e0c" +dependencies = [ + "dashmap", + "libc", + "once_cell", + "signal-hook", + "signal-hook-registry", + "tempfile", +] + +[[package]] +name = "git-validate" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0431cf9352c596dc7c8ec9066ee551ce54e63c86c3c767e5baf763f6019ff3c2" +dependencies = [ + "bstr", + "thiserror", +] + [[package]] name = "globset" version = "0.4.10" @@ -1851,6 +2254,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" +[[package]] +name = "http-range-header" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" + [[package]] name = "httparse" version = "1.8.0" @@ -1916,6 +2325,19 @@ dependencies = [ "webpki 0.21.4", ] +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "iana-time-zone" version = "0.1.53" @@ -1978,6 +2400,22 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "ignore-files" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f6fe1437ef5a520e79d63958e6bfb7cfd26e30d15e4e29d3d5561697099a70" +dependencies = [ + "futures", + "git-config", + "ignore", + "miette", + "project-origins", + "thiserror", + "tokio", + "tracing", +] + [[package]] name = "indexmap" version = "1.9.2" @@ -1995,6 +2433,26 @@ version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da2d6f23ffea9d7e76c53eee25dfb67bcd8fde7f1198b0855350698c9f07c780" +[[package]] +name = "inotify" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" +dependencies = [ + "bitflags", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + [[package]] name = "inout" version = "0.1.3" @@ -2023,6 +2481,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "ipnet" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" + [[package]] name = "is-terminal" version = "0.4.2" @@ -2073,6 +2537,26 @@ dependencies = [ "simple_asn1", ] +[[package]] +name = "kqueue" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c8fc60ba15bf51257aa9807a48a61013db043fcf3a78cb0d916e8e396dcad98" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8367585489f01bc55dd27404dcf56b95e6da061a256a666ab23be9ba96a2e587" +dependencies = [ + "bitflags", + "libc", +] + [[package]] name = "language-tags" version = "0.3.2" @@ -2101,7 +2585,7 @@ dependencies = [ "idna 0.2.3", "mime", "native-tls", - "nom", + "nom 7.1.3", "once_cell", "quoted_printable", "socket2", @@ -2186,19 +2670,57 @@ checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" name = "matches" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "matchit" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40" + +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memmap2" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b182332558b18d807c4ce1ca8ca983b34c3ee32765e47b3f0f69b90355cc1dc" +dependencies = [ + "libc", +] [[package]] -name = "md5" -version = "0.7.0" +name = "miette" +version = "5.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" +checksum = "4afd9b301defa984bbdbe112b4763e093ed191750a0d914a78c1106b2d0fe703" +dependencies = [ + "miette-derive", + "once_cell", + "thiserror", + "unicode-width", +] [[package]] -name = "memchr" -version = "2.5.0" +name = "miette-derive" +version = "5.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "97c2401ab7ac5282ca5c8b518a87635b1a93762b0b90b9990c509888eeccba29" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "migrations_internals" @@ -2282,6 +2804,28 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nix" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" +dependencies = [ + "bitflags", + "cfg-if", + "libc", + "static_assertions", +] + +[[package]] +name = "nom" +version = "5.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" +dependencies = [ + "memchr", + "version_check", +] + [[package]] name = "nom" version = "7.1.3" @@ -2301,6 +2845,30 @@ dependencies = [ "memchr", ] +[[package]] +name = "normalize-path" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf22e319b2e3cb517350572e3b70c6822e0a520abfb5c78f690e829a73e8d9f2" + +[[package]] +name = "notify" +version = "5.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58ea850aa68a06e48fdb069c0ec44d0d64c8dbffa49bf3b6f7f0a901fdea1ba9" +dependencies = [ + "bitflags", + "crossbeam-channel", + "filetime", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "mio", + "walkdir", + "windows-sys", +] + [[package]] name = "num-bigint" version = "0.4.3" @@ -2341,6 +2909,15 @@ dependencies = [ "libc", ] +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + [[package]] name = "once_cell" version = "1.17.0" @@ -2353,6 +2930,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "open" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2078c0039e6a54a0c42c28faa984e115fb4c2d5bf2208f77d1961002df8576f8" +dependencies = [ + "pathdiff", + "windows-sys", +] + [[package]] name = "openssl" version = "0.10.45" @@ -2442,6 +3029,12 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + [[package]] name = "pem" version = "1.1.1" @@ -2696,6 +3289,23 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "project-origins" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "629e0d57f265ca8238345cb616eea8847b8ecb86b5d97d155be2c8963a314379" +dependencies = [ + "futures", + "tokio", + "tokio-stream", +] + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + [[package]] name = "quote" version = "1.0.23" @@ -2783,6 +3393,12 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" + [[package]] name = "regex-syntax" version = "0.6.28" @@ -2798,6 +3414,43 @@ dependencies = [ "winapi", ] +[[package]] +name = "reqwest" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" +dependencies = [ + "base64 0.21.0", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + [[package]] name = "rfc7239" version = "0.1.0" @@ -2929,6 +3582,12 @@ dependencies = [ "security-framework", ] +[[package]] +name = "rustversion" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" + [[package]] name = "ryu" version = "1.0.12" @@ -3022,6 +3681,9 @@ name = "semver" version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" +dependencies = [ + "serde", +] [[package]] name = "serde" @@ -3054,6 +3716,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b04f22b563c91331a10074bda3dd5492e3cc39d56bd557e91c0af42b6c7341" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -3077,6 +3748,12 @@ dependencies = [ "digest", ] +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + [[package]] name = "sha2" version = "0.10.6" @@ -3103,6 +3780,16 @@ dependencies = [ "dirs", ] +[[package]] +name = "signal-hook" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d" +dependencies = [ + "libc", + "signal-hook-registry", +] + [[package]] name = "signal-hook-registry" version = "1.4.0" @@ -3170,6 +3857,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" version = "0.8.0" @@ -3223,6 +3916,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "tempfile" version = "3.3.0" @@ -3278,6 +3977,19 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "terminfo" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da31aef70da0f6352dbcb462683eb4dd2bfad01cf3fc96cf204547b9a839a585" +dependencies = [ + "dirs", + "fnv", + "nom 5.1.2", + "phf", + "phf_codegen", +] + [[package]] name = "textwrap" version = "0.11.0" @@ -3323,6 +4035,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" dependencies = [ "itoa", + "libc", + "num_threads", "serde", "time-core", "time-macros", @@ -3389,6 +4103,16 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.22.0" @@ -3411,6 +4135,18 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-tungstenite" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54319c93411147bced34cb5609a80e0a8e44c5999c93903a81cd866630ec0bfd" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + [[package]] name = "tokio-util" version = "0.6.10" @@ -3481,6 +4217,25 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower-http" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" +dependencies = [ + "bitflags", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-range-header", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.2" @@ -3551,6 +4306,25 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8acc9e35a3072d9ea375093f321b3738ea3ac4fdebc18d2789b01581e5c2e4ae" +[[package]] +name = "tungstenite" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ee6ab729cd4cf0fd55218530c4522ed30b7b6081752839b68fcec8d0960788" +dependencies = [ + "base64 0.13.1", + "byteorder", + "bytes", + "http", + "httparse", + "log", + "rand", + "sha1", + "thiserror", + "url", + "utf-8", +] + [[package]] name = "twoway" version = "0.2.2" @@ -3653,6 +4427,12 @@ version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" +[[package]] +name = "unicode-bom" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63ec69f541d875b783ca40184d655f2927c95f0bffd486faa83cd3ac3529ec32" + [[package]] name = "unicode-ident" version = "1.0.6" @@ -3744,6 +4524,12 @@ version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8db7427f936968176eaa7cdf81b7f98b980b18495ec28f1b5791ac3bfe3eea9" +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utoipa" version = "2.4.2" @@ -3889,6 +4675,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.83" @@ -3918,6 +4716,29 @@ version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +[[package]] +name = "watchexec" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70407b579a4df4ed964990432a03917ca81d378d9f570ada692d319a268db502" +dependencies = [ + "async-priority-channel", + "async-recursion", + "atomic-take", + "clearscreen", + "command-group", + "futures", + "ignore-files", + "miette", + "normalize-path", + "notify", + "once_cell", + "project-origins", + "thiserror", + "tokio", + "tracing", +] + [[package]] name = "web-sys" version = "0.3.60" @@ -3957,6 +4778,17 @@ dependencies = [ "webpki 0.22.0", ] +[[package]] +name = "which" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +dependencies = [ + "either", + "libc", + "once_cell", +] + [[package]] name = "winapi" version = "0.3.9" @@ -3988,6 +4820,21 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.43.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04662ed0e3e5630dfa9b26e4cb823b817f1a9addda855d973a9458c236556244" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + [[package]] name = "windows-sys" version = "0.42.0" @@ -4045,6 +4892,15 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + [[package]] name = "xmlparser" version = "0.13.3" diff --git a/create-rust-app/Cargo.toml b/create-rust-app/Cargo.toml index 3929b7ab..779fdd78 100644 --- a/create-rust-app/Cargo.toml +++ b/create-rust-app/Cargo.toml @@ -14,13 +14,13 @@ categories = ["command-line-utilities", "development-tools", "web-programming", ## ## COMMON / DEFAULT - required dependencies ## -dotenv = "0.15.0" +dotenv = "0.15.0" # + plugin_dev serde_json = "1.0.91" lettre = "0.10.1" tera = { version="1.17.0" } lazy_static = { version="1.4.0" } serde = { version = "1.0.152", features = ["derive"] } -diesel = { version="2.0.0-rc.1", default-features = false, features = ["uuid", "r2d2", "chrono", "returning_clauses_for_sqlite_3_35"] } # plugin_dev, plugin_auth +diesel = { version="2.0.0-rc.1", default-features = false, features = ["uuid", "r2d2", "chrono"] } # + plugin_dev, plugin_auth ## ## Database @@ -42,13 +42,20 @@ chrono = { optional=true, version = "0.4.23", default-features = false, features # plugin_dev diesel_migrations = { optional=true, version="2.0.0" } +cargo_metadata = { optional=true, version="0.15.2" } +watchexec = { optional=true, version="2.0.2" } +#### tracing = { optional=true, version="0.1" } +#### tracing-subscriber = { optional=true, version="0.3.16", features=["env-filter"] } +reqwest = { optional=true, version="0.11.13" } +clearscreen = { optional=true, version="2.0.0" } +open = { optional=true, version="3.2.0" } +cargo_toml = { optional=true, version = "0.14.0" } # plugin_storage aws-config = { optional=true, version="0.14.0" } aws-types = { optional=true, version="0.8.0" } aws-endpoint = { optional=true, version="0.14.0" } aws-sdk-s3 = { optional=true, version="0.8.0" } -futures-util = { optional=true, version="0.3.25" } http = { optional=true, version="0.2.6" } diesel_derives = { optional=true, version="2.0.1" } uuid = { optional=true, version="1.2.2", features=["v4", "serde"] } @@ -75,23 +82,31 @@ actix-web-httpauth = { optional=true, version="0.8.0" } derive_more = { optional=true, version="0.99.17" } futures = { optional=true, version="0.3.25" } env_logger = { optional=true, version= "0.10.0" } + +# axum dependencies (not yet released; only used for plugin_dev) + +axum = { optional=true, version="0.6.1" } + ## ## MISC - here, we list deps which are required by multiple features but are not required in all configurations ## mime_guess = { optional=true, version="2.0.4" } # backend_poem, plugin_storage -anyhow = { optional=true, version="1.0.68" } # backend_poem, plugin_auth -tokio = { optional=true, version = "1", features = ["full"] } # backend_poem, plugin-storage +anyhow = { optional=true, version="1.0.57" } # backend_poem, plugin_auth, plugin_dev +tokio = { optional=true, version = "1", features = ["full"] } # backend_poem, plugin_storage +async-priority-channel = "0.1.0" +futures-util = { optional=true, version = "0.3.25" } # plugin_dev, TODO:plugin_storage? [features] default = ["backend_actix-web", "database_postgres", "plugin_auth", "plugin_container", "plugin_dev", "plugin_graphql", "plugin_storage", "plugin_utoipa"] -plugin_dev = ["diesel_migrations"] +plugin_dev = ["backend_axum", "cargo_toml", "open", "reqwest", "anyhow", "clearscreen", "watchexec", "cargo_metadata", "diesel_migrations", "futures-util"] plugin_container = [] plugin_auth = ["anyhow", "rust-argon2", "rand", "jsonwebtoken", "chrono", "tsync"] -plugin_storage = [ "aws-config", "aws-types", "aws-endpoint", "aws-sdk-s3", "tokio", "futures-util", "http", "diesel_derives", "uuid", "md5", "mime_guess", "base64" ] +plugin_storage = [ "aws-config", "aws-types", "aws-endpoint", "aws-sdk-s3", "tokio", "http", "diesel_derives", "uuid", "md5", "mime_guess", "base64" ] # note: might need to add "futures-util"? plugin_graphql = [] plugin_utoipa = ["utoipa", "utoipa-swagger-ui", "backend_actix-web"] backend_poem = ["poem", "anyhow", "mime_guess", "tokio"] backend_actix-web = ["actix-web", "actix-http", "actix-files", "actix-multipart", "actix-web-httpauth","derive_more", "futures", "env_logger"] -database_sqlite = ["diesel/sqlite", "libsqlite3-sys/bundled"] +backend_axum = ["axum", "axum/ws"] +database_sqlite = ["diesel/sqlite", "diesel/returning_clauses_for_sqlite_3_35", "libsqlite3-sys/bundled"] database_postgres = ["diesel/postgres"] diff --git a/create-rust-app/src/auth/endpoints/mod.rs b/create-rust-app/src/auth/endpoints/mod.rs index 0be2ee3f..e02bae81 100644 --- a/create-rust-app/src/auth/endpoints/mod.rs +++ b/create-rust-app/src/auth/endpoints/mod.rs @@ -1,7 +1,9 @@ #[cfg(feature = "backend_actix-web")] mod service_actixweb; #[cfg(feature = "backend_actix-web")] -pub use service_actixweb::{endpoints, ApiDoc}; +pub use service_actixweb::endpoints; +#[cfg(all(feature = "backend_actix-web", feature = "plugin_utoipa"))] +pub use service_actixweb::ApiDoc; #[cfg(feature = "backend_poem")] mod service_poem; diff --git a/create-rust-app/src/database/mod.rs b/create-rust-app/src/database/mod.rs index e8aab4b4..ccfc2578 100644 --- a/create-rust-app/src/database/mod.rs +++ b/create-rust-app/src/database/mod.rs @@ -6,6 +6,12 @@ type DbCon = diesel::PgConnection; #[cfg(feature = "database_sqlite")] type DbCon = diesel::SqliteConnection; +#[cfg(feature = "database_postgres")] +pub type DieselBackend = diesel::pg::Pg; + +#[cfg(feature = "database_sqlite")] +pub type DieselBackend = diesel::sqlite::Sqlite; + pub type Pool = r2d2::Pool>; pub type Connection = PooledConnection>; diff --git a/create-rust-app/src/dev/backend_compiling_server.rs b/create-rust-app/src/dev/backend_compiling_server.rs new file mode 100644 index 00000000..230c41a6 --- /dev/null +++ b/create-rust-app/src/dev/backend_compiling_server.rs @@ -0,0 +1,271 @@ +use cargo_metadata::Message; +use std::collections::HashMap; +use std::path::PathBuf; +use std::process::Stdio; +use std::sync::Arc; +use tokio::process::Command; +use tokio::sync::Mutex; + +use tokio::sync::broadcast::{Receiver, Sender}; +use tokio::sync::mpsc; +use watchexec::action::Action; +use watchexec::config::{InitConfig, RuntimeConfig}; +use watchexec::event::{Event, Priority, Tag}; +use watchexec::handler::PrintDebug; +use watchexec::signal::source::MainSignal; +use watchexec::Watchexec; + +use super::{check_exit, DevServerEvent, DevState}; + +pub async fn start( + project_dir: &'static str, + dev_port: u16, + mut signal_rx: Receiver, // this is specifically for the SHUTDOWN signal + dev_server_events_s: Sender, // this one is for any dev server event signal + state: Arc>, + file_events_s: Sender, +) { + println!("Starting backend server @ http://localhost:3000/"); + let state2 = state.clone(); + let state3 = state.clone(); + + let (server_s, mut server_r) = mpsc::channel::<&str>(64); + let server_s2 = server_s.clone(); + + let ws2_s = dev_server_events_s.clone(); + // let ws3_s = dev_server_events_s.clone(); + + tokio::spawn(async move { + let mut m = state.lock().await; + m.backend_server_running = true; + drop(m); + + let new_child_process = || { + Command::new("cargo") + .arg("run") + .kill_on_drop(true) + .env("DEV_SERVER_PORT", dev_port.to_string()) + .current_dir(project_dir) + .spawn() + .unwrap() + }; + + let mut child_process = new_child_process(); + ws2_s.send(DevServerEvent::CHECK_MIGRATIONS).ok(); + + while let Some(event) = server_r.recv().await { + match event { + "restart" => { + println!("♻️ Restarting server..."); + // we don't send BackendStatus(false) but instead we notify the user that it's restarting which should be enough + // ws2_s.send(DevServerEvent::BackendStatus(false)).ok(); + ws2_s.send(DevServerEvent::BackendRestarting(true)).ok(); + if child_process.id().is_some() { + child_process.kill().await.unwrap(); + } + child_process = new_child_process(); + // note: the child process will hit /backend-up which + // sends backend status "true" to the websocket + // i.e. ws2_s.send(DevServerEvent::BackendStatus(true)).ok(); + // is not necessary + } + "stop" => { + println!("Shutting down backend server..."); + ws2_s.send(DevServerEvent::BackendStatus(false)).ok(); + if child_process.id().is_some() { + child_process.kill().await.unwrap(); + } + let mut m = state.lock().await; + m.backend_server_running = false; + check_exit(&m); + drop(m); + break; + } + _ => {} + } + } + }); + + let mut init = InitConfig::default(); + init.on_error(PrintDebug(std::io::stderr())); + + let mut runtime = RuntimeConfig::default(); + // runtime.command(watchexec::command::Command::Exec { + // prog: "cargo".to_string(), + // args: vec!["run".to_string()], + // }); + let mut file_events_r = file_events_s.subscribe(); + let files_to_ignore = Arc::new(std::sync::Mutex::new(vec![])); + let files_to_ignore2 = files_to_ignore.clone(); + tokio::spawn(async move { + while let Ok(file) = file_events_r.recv().await { + let mut arr = files_to_ignore2.lock().unwrap(); + arr.push(file); + } + }); + let backend_dir = PathBuf::from(format!("{project_dir}/backend")); + let migrations_dir = PathBuf::from(format!("{project_dir}/migrations")); + runtime.pathset([backend_dir, migrations_dir.clone()]); + runtime.on_action(move |action: Action| { + let files_to_ignore = files_to_ignore.clone(); + let server_s2 = server_s2.clone(); + let state3 = state3.clone(); + let ws_s = dev_server_events_s.clone(); + let migrations_dir = migrations_dir.clone(); + async move { + let exit_events = action + .events + .iter() + .filter(|e| e.metadata.contains_key("exit-watchexec")) + .collect::>(); + + if exit_events.is_empty() { + println!("continuous backend compilation stopped."); + let mut m = state3.lock().await; + m.watchexec_running = false; + check_exit(&m); + drop(m); + return Ok(()); + } + + let mut ignored_files = files_to_ignore.lock().unwrap(); + let files_to_ignore: Vec = ignored_files.clone(); + ignored_files.clear(); + drop(ignored_files); + // println!("=> ignoring {:#?}", files_to_ignore); + + let mut touched_migrations_dir = false; + let file_events = action + .events + .iter() + .filter(|e| { + e.tags.iter().any(|t| match t { + Tag::Path { path, file_type: _ } => { + // println!("PATH {:#?}", path); + + if path + .to_str() + .unwrap() + .starts_with(migrations_dir.as_os_str().to_str().unwrap()) + { + touched_migrations_dir = true; + } + + !files_to_ignore.iter().any(|file_to_ignore| { + path.to_str().unwrap().ends_with(file_to_ignore) + }) + } + _ => false, + }) + }) + .collect::>(); + + if file_events.is_empty() { + // compile + ws_s.send(DevServerEvent::BackendCompiling(true)).ok(); + if compile(project_dir, ws_s.clone()) { + // restart backend + server_s2.send("restart").await.unwrap(); + ws_s.send(DevServerEvent::CompileSuccess(true)).ok(); + } else { + ws_s.send(DevServerEvent::CompileSuccess(false)).ok(); + } + ws_s.send(DevServerEvent::BackendCompiling(false)).ok(); + } + + if touched_migrations_dir { + ws_s.send(DevServerEvent::CHECK_MIGRATIONS).ok(); + } + + Ok::<(), std::io::Error>(()) + } + }); + + let we = Watchexec::new(init, runtime.clone()).unwrap(); + let we2 = we.clone(); + + tokio::spawn(async move { + while let Ok(event) = signal_rx.recv().await { + if let DevServerEvent::SHUTDOWN = event { + let mut metadata = HashMap::new(); + metadata.insert("exit-watchexec".to_string(), vec!["true".to_string()]); + we2.send_event( + Event { + tags: vec![ + Tag::Signal(MainSignal::Interrupt), + Tag::Signal(MainSignal::Terminate), + ], + metadata, + }, + Priority::Urgent, + ) + .await + .unwrap(); // stops watch exec + server_s.send("stop").await.unwrap(); // stops backend server + } + } + }); + + let mut m = state2.lock().await; + m.watchexec_running = true; + drop(m); + + we.main().await.unwrap().unwrap(); + println!("backend compilation server stopped."); +} + +fn compile(project_dir: &'static str, ws_s: Sender) -> bool { + println!("🔨 Compiling backend..."); + let start_time = std::time::SystemTime::now(); + + let mut command = std::process::Command::new("cargo") + .args([ + "build", + "-q", + "--message-format=json-diagnostic-rendered-ansi", + ]) + .current_dir(project_dir) + .stdout(Stdio::piped()) + .spawn() + .unwrap(); + + let reader = std::io::BufReader::new(command.stdout.take().unwrap()); + let mut compiler_messages = vec![]; + ws_s.send(DevServerEvent::CompileMessages(compiler_messages.clone())) + .ok(); // clear previous messages + for message in cargo_metadata::Message::parse_stream(reader) { + match message.unwrap() { + Message::CompilerMessage(msg) => { + compiler_messages.push(msg); + ws_s.send(DevServerEvent::CompileMessages(compiler_messages.clone())) + .ok(); + } + Message::CompilerArtifact(_) => { + // println!("{:?}", artifact); + } + Message::BuildScriptExecuted(_) => { + // println!("{:?}", script); + } + Message::BuildFinished(finished) => { + let compile_time_s = std::time::SystemTime::now() + .duration_since(start_time) + .map(|d| d.as_secs_f32()) + .map(|d| format!("{d:.2}")) + .unwrap_or("?".to_string()); + + if finished.success { + println!("✅ Compiled ({compile_time_s} seconds)"); + } else { + println!("❌ Compilation failed: see errors in app ({compile_time_s} seconds)",); + } + } + _ => (), // Unknown message + } + } + + command + .wait_with_output() + .expect("Error retrieving `cargo build` exit status") + .status + .success() +} diff --git a/create-rust-app/src/dev/controller.rs b/create-rust-app/src/dev/controller.rs index 73bcd0b5..72a15aa1 100644 --- a/create-rust-app/src/dev/controller.rs +++ b/create-rust-app/src/dev/controller.rs @@ -1,9 +1,15 @@ -use crate::auth::{Auth, Role}; use crate::Database; -use diesel::{query_dsl::RunQueryDsl, sql_query, sql_types::Text}; +use diesel::{ + migration::{Migration, MigrationSource}, + query_dsl::RunQueryDsl, + sql_query, + sql_types::Text, +}; use diesel_migrations::{FileBasedMigrations, MigrationHarness}; use serde::{Deserialize, Serialize}; +use super::{CreateRustAppMigration, MigrationStatus}; + #[derive(Debug, Deserialize, QueryableByName)] pub struct MyQueryResult { #[diesel(sql_type=Text)] @@ -21,32 +27,13 @@ pub struct HealthCheckResponse { } /// /db/query -pub fn query_db(db: &Database, body: &MySqlQuery) -> Result { +pub fn query_db(db: &Database, body: &MySqlQuery) -> Result { let q = format!("SELECT json_agg(q) as json FROM ({}) q;", body.query); let mut db = db.pool.get().unwrap(); - let rows = sql_query(q.as_str()) - .get_result::(&mut db) - .map_err(|e| e.to_string()); - if rows.is_err() { - return Err(rows.err().unwrap()); - } - - let result = rows.unwrap().json; - - Ok(result) -} - -/// /auth/has-system-role -pub fn check_system_role(auth: &Auth) -> bool { - auth.has_permission("system".to_string()) -} - -/// /auth/add-system-role -pub fn add_system_role(db: &Database, auth: &Auth) -> bool { - let mut db = db.pool.clone().get().unwrap(); - - Role::assign(&mut db, auth.user_id, "system").unwrap() + Ok(sql_query(q.as_str()) + .get_result::(&mut db)? + .json) } /// /db/is-connected @@ -56,6 +43,61 @@ pub fn is_connected(db: &Database) -> bool { is_connected.is_err() } +pub fn get_migrations(db: &Database) -> Vec { + // Vec { + let mut db = db.pool.clone().get().unwrap(); + + let source = FileBasedMigrations::find_migrations_directory().unwrap(); + + #[cfg(feature = "database_sqlite")] + let file_migrations = + MigrationSource::::migrations(&source).unwrap(); + #[cfg(feature = "database_postgres")] + let file_migrations = + MigrationSource::::migrations(&source).unwrap(); + + let db_migrations = MigrationHarness::applied_migrations(&mut db).unwrap(); + let pending_migrations = MigrationHarness::pending_migrations(&mut db, source).unwrap(); + + let mut all_migrations = vec![]; + + file_migrations.iter().for_each(|fm| { + all_migrations.push(CreateRustAppMigration { + name: fm.name().to_string(), + version: fm.name().version().to_string(), + status: MigrationStatus::Unknown, + }) + }); + + // update the status for any pending file_migrations + pending_migrations.iter().for_each(|pm| { + if let Some(existing) = all_migrations.iter_mut().find(|m| { + m.version + .eq_ignore_ascii_case(&pm.name().version().to_string()) + }) { + existing.status = MigrationStatus::Pending; + } + }); + + db_migrations.iter().for_each(|dm| { + match all_migrations + .iter_mut() + .find(|m| m.version.eq_ignore_ascii_case(&dm.to_string())) + { + Some(existing) => { + existing.status = MigrationStatus::Applied; + } + None => all_migrations.push(CreateRustAppMigration { + name: format!("{dm}_?"), + version: dm.to_string(), + status: MigrationStatus::AppliedButMissingLocally, + }), + } + }); + + all_migrations +} + /// /db/needs-migration /// checks if a migration is needed pub fn needs_migration(db: &Database) -> bool { @@ -67,7 +109,7 @@ pub fn needs_migration(db: &Database) -> bool { /// /db/migrate /// performs any pending migrations -pub fn migrate_db(db: &Database) -> bool { +pub fn migrate_db(db: &Database) -> (bool, /* error message: */ Option) { let mut db = db.pool.clone().get().unwrap(); let source = FileBasedMigrations::find_migrations_directory().unwrap(); @@ -75,17 +117,17 @@ pub fn migrate_db(db: &Database) -> bool { MigrationHarness::has_pending_migration(&mut db, source.clone()).unwrap(); if !has_pending_migrations { - return true; + return (true, None); } let op = MigrationHarness::run_pending_migrations(&mut db, source); - - if op.is_err() { - println!("{:#?}", op.err()); - return false; + match op { + Ok(_) => (true, None), + Err(err) => { + println!("{err:#?}"); + (false, Some(err.to_string())) + } } - - true } /// /health diff --git a/create-rust-app/src/dev/dev_server.rs b/create-rust-app/src/dev/dev_server.rs new file mode 100644 index 00000000..523eb98e --- /dev/null +++ b/create-rust-app/src/dev/dev_server.rs @@ -0,0 +1,321 @@ +use std::sync::{Arc, Mutex}; + +use axum::{ + extract::{ws::WebSocket, State, WebSocketUpgrade}, + http::status::StatusCode, + response::{Html, IntoResponse}, + routing::get, + Router, +}; +use cargo_metadata::CompilerMessage; +use futures_util::{SinkExt, StreamExt}; +use tokio::sync::broadcast::{Receiver, Sender}; + +use crate::{dev::controller, Database}; + +use super::{CreateRustAppMigration, DevServerEvent}; + +#[derive(Debug)] +struct CurrentDevState { + backend_status: bool, + backend_compiled: bool, + backend_compiling: bool, + backend_restarting: bool, + compiler_messages: Vec, + vite_status: bool, + features: Vec, + migrations_pending: (bool, Vec), +} + +struct AppState { + #[allow(dead_code)] + project_dir: &'static str, + rx: tokio::sync::Mutex>, // this is the original subscribed receiver which hasn't missed a single event :) + tx: Sender, + file_tx: Sender, + db: Database, + dev: Mutex, +} + +pub async fn start( + project_dir: &'static str, + dev_port: u16, + dev_server_events_r: Receiver, + dev_server_events_s: Sender, + file_events_s: Sender, + features: Vec, +) { + if dotenv::dotenv().is_err() { + panic!("ERROR: Could not load environment variables from dotenv file"); + } + + let app_state = Arc::new(AppState { + project_dir, + rx: tokio::sync::Mutex::new(dev_server_events_r), + tx: dev_server_events_s, + file_tx: file_events_s, + db: Database::new(), + dev: Mutex::new(CurrentDevState { + backend_compiled: true, + backend_compiling: false, + backend_restarting: false, + backend_status: true, + compiler_messages: vec![], + vite_status: true, + features, + migrations_pending: (false, vec![]), + }), + }); + + let app = Router::new() + .route("/", get(|| async { + // Let user know the server is running + Html(r###" + + Create Rust App: Development Server + + +

Create Rust App: Development Server

+ + + "###) + })) + .route("/vitejs-down", get(vitejs_down_handler)) + .route("/vitejs-up", get(vitejs_up_handler)) + .route("/backend-up", get(backend_up_handler)) + .route("/ws", get(ws_handler)) + .with_state(app_state); + + println!("Starting dev server @ http://localhost:{dev_port}/"); + + axum::Server::bind(&format!("0.0.0.0:{dev_port}").parse().unwrap()) + .serve(app.into_make_service()) + .await + .unwrap(); +} + +async fn backend_up_handler(State(state): State>) -> impl IntoResponse { + let sender = state.tx.clone(); + sender.send(DevServerEvent::BackendRestarting(false)).ok(); + sender.send(DevServerEvent::BackendStatus(true)).ok(); + + StatusCode::OK.into_response() +} + +async fn vitejs_up_handler(State(state): State>) -> impl IntoResponse { + let sender = state.tx.clone(); + sender.send(DevServerEvent::ViteJSStatus(true)).ok(); + + StatusCode::OK.into_response() +} + +async fn vitejs_down_handler(State(state): State>) -> impl IntoResponse { + let sender = state.tx.clone(); + sender.send(DevServerEvent::ViteJSStatus(true)).ok(); + + StatusCode::OK.into_response() +} + +async fn ws_handler(ws: WebSocketUpgrade, State(state): State>) -> impl IntoResponse { + ws.on_upgrade(|socket| handle_socket(socket, state)) +} + +async fn handle_socket(stream: WebSocket, state: Arc) { + use axum::extract::ws::Message; + let (mut sender, mut receiver) = stream.split(); + + /* + SECTION: sending initial state + */ + let compiler_messages = state.dev.lock().unwrap().compiler_messages.clone(); + let backend_status = state.dev.lock().unwrap().backend_status; + let backend_compiling = state.dev.lock().unwrap().backend_compiling; + let backend_restarting = state.dev.lock().unwrap().backend_restarting; + let backend_compiled = state.dev.lock().unwrap().backend_compiled; + let vite_status = state.dev.lock().unwrap().vite_status; + let features = state.dev.lock().unwrap().features.clone(); + let migrations_pending = state.dev.lock().unwrap().migrations_pending.clone(); + sender + .send(Message::Text(DevServerEvent::FeaturesList(features).json())) + .await + .unwrap(); + sender + .send(Message::Text( + DevServerEvent::CompileSuccess(backend_compiled).json(), + )) + .await + .unwrap(); + sender + .send(Message::Text( + DevServerEvent::CompileMessages(compiler_messages).json(), + )) + .await + .unwrap(); + sender + .send(Message::Text( + DevServerEvent::BackendStatus(backend_status).json(), + )) + .await + .unwrap(); + sender + .send(Message::Text( + DevServerEvent::BackendRestarting(backend_restarting).json(), + )) + .await + .unwrap(); + sender + .send(Message::Text( + DevServerEvent::BackendCompiling(backend_compiling).json(), + )) + .await + .unwrap(); + sender + .send(Message::Text( + DevServerEvent::ViteJSStatus(vite_status).json(), + )) + .await + .unwrap(); + sender + .send(Message::Text( + DevServerEvent::PendingMigrations(migrations_pending.0, migrations_pending.1).json(), + )) + .await + .unwrap(); + + /* + SECTION: receive dev server events + */ + let db = state.db.clone(); + let state2 = state.clone(); + let dse_s = state2.tx.clone(); + let mut send_task = tokio::spawn(async move { + let mut rx = state2.rx.lock().await; + + while let Ok(e) = rx.recv().await { + let mut send_response = true; + + match e.clone() { + DevServerEvent::CHECK_MIGRATIONS => { + send_response = false; + let migrations = controller::get_migrations(&db); + if controller::needs_migration(&db) { + dse_s + .send(DevServerEvent::PendingMigrations(true, migrations)) + .ok(); + } else { + dse_s + .send(DevServerEvent::PendingMigrations(false, migrations)) + .ok(); + } + } + DevServerEvent::MigrationResponse(success, _) => { + // this is a response; we don't need to handle anything on the dev-server side + let migrations = controller::get_migrations(&db); + dse_s + .send(DevServerEvent::PendingMigrations(!success, migrations)) + .ok(); + } + DevServerEvent::PendingMigrations(a, b) => { + let mut s = state2.dev.lock().unwrap(); + s.migrations_pending = (a, b); + } + DevServerEvent::FeaturesList(list) => { + let mut s = state2.dev.lock().unwrap(); + s.features = list; + } + DevServerEvent::BackendCompiling(b) => { + let mut s = state2.dev.lock().unwrap(); + s.backend_compiling = b; + } + DevServerEvent::BackendStatus(b) => { + let mut s = state2.dev.lock().unwrap(); + s.backend_status = b; + } + DevServerEvent::BackendRestarting(b) => { + let mut s = state2.dev.lock().unwrap(); + s.backend_restarting = b; + } + DevServerEvent::CompileSuccess(b) => { + let mut s = state2.dev.lock().unwrap(); + s.backend_compiled = b; + } + DevServerEvent::CompileMessages(messages) => { + let mut s = state2.dev.lock().unwrap(); + s.compiler_messages = messages.clone(); + } + DevServerEvent::SHUTDOWN => { + let mut s = state2.dev.lock().unwrap(); + s.backend_status = false; + } + DevServerEvent::ViteJSStatus(b) => { + let mut s = state2.dev.lock().unwrap(); + s.vite_status = b; + } + }; + + if send_response { + sender.send(Message::Text(e.json())).await.unwrap(); + } + } + }); + + /* + SECTION: receive websocket events + */ + let state3 = state.clone(); + let file_tx = state.file_tx.clone(); + let mut recv_task = tokio::spawn(async move { + let state3 = state3.clone(); + + while let Some(msg) = receiver.next().await { + if let Ok(msg) = msg { + match msg { + Message::Text(t) => { + let state3 = state3.clone(); + let file_tx = file_tx.clone(); + tokio::spawn(async move { + let state3 = state3.clone(); + if t.starts_with("open:") { + // HACK: tell the backend server we're about the modify the file (this is a side effect of the `open` crate) + // that way it won't try to recompile as a result of this filesystem modification event + + let (_, file_name) = t.split_at(5); + file_tx.send(file_name.to_string()).ok(); + + // WARNING: this hack causes a race condition between the file above being registered for ignoring + // and the `open` command below which will modify the file + // + // suggestion 1: change the method by which we open files so they don't get "modified" when opening them + // the new method should be one which can open a specific line and column, unlike the current solution + // + // suggestion 2: listen for a 'file-registered' event from the backend compiling server + // so we know that it won't re-compile based on the modify event that this + // file opening causes + open::that(file_name).unwrap_or_else(|_| { + println!("📝 Could not open file `{file_name}`"); + }); + } else if t.eq_ignore_ascii_case("migrate") { + let (success, error_message) = controller::migrate_db(&state3.db); + + state3 + .tx + .send(DevServerEvent::MigrationResponse(success, error_message)) + .ok(); + } + }); + } + Message::Binary(_) => {} + Message::Ping(_) => {} + Message::Pong(_) => {} + Message::Close(_) => {} + } + } + } + }); + + // If any one of the tasks exit, abort the other. + tokio::select! { + _ = (&mut send_task) => {recv_task.abort()}, + _ = (&mut recv_task) => {send_task.abort()}, + }; +} diff --git a/create-rust-app/src/dev/endpoints/service_actixweb.rs b/create-rust-app/src/dev/endpoints/service_actixweb.rs index 9e90d0cd..88b30d1e 100644 --- a/create-rust-app/src/dev/endpoints/service_actixweb.rs +++ b/create-rust-app/src/dev/endpoints/service_actixweb.rs @@ -1,11 +1,6 @@ -use crate::{ - auth::Auth, - dev::controller, - dev::controller::{HealthCheckResponse, MySqlQuery}, - Database, -}; +use crate::{dev::controller, dev::controller::MySqlQuery, Database}; use actix_web::{ - get, post, + post, web::{Data, Json}, HttpResponse, Scope, }; @@ -19,46 +14,6 @@ async fn query_db(db: Data, body: Json) -> HttpResponse { } } -#[get("/auth/has-system-role")] -async fn check_system_role(auth: Auth) -> HttpResponse { - HttpResponse::Ok().json(controller::check_system_role(&auth)) -} - -#[get("/auth/add-system-role")] -async fn add_system_role(db: Data, auth: Auth) -> HttpResponse { - HttpResponse::Ok().json(controller::add_system_role(&db, &auth)) -} - -#[get("/db/is-connected")] -async fn is_connected(db: Data) -> HttpResponse { - HttpResponse::Ok().json(controller::is_connected(&db)) -} - -#[get("/db/needs-migration")] -async fn needs_migration(db: Data) -> HttpResponse { - HttpResponse::Ok().json(controller::needs_migration(&db)) -} - -#[get("/db/migrate")] -async fn migrate_db(db: Data) -> HttpResponse { - HttpResponse::Ok().json(controller::migrate_db(&db)) -} - -#[get("/health")] -async fn health() -> HttpResponse { - controller::health(); - HttpResponse::Ok().json(HealthCheckResponse { - message: "healthy".to_string(), - }) -} - pub fn endpoints(scope: Scope) -> Scope { - scope - .service(query_db) - .service(check_system_role) - .service(add_system_role) - .service(is_connected) - .service(needs_migration) - .service(migrate_db) - .service(health) + scope.service(query_db) } diff --git a/create-rust-app/src/dev/endpoints/service_poem.rs b/create-rust-app/src/dev/endpoints/service_poem.rs index c7516cbd..c3a937a2 100644 --- a/create-rust-app/src/dev/endpoints/service_poem.rs +++ b/create-rust-app/src/dev/endpoints/service_poem.rs @@ -1,50 +1,14 @@ use poem::{ - get, handler, + handler, http::StatusCode, post, web::{Data, Json}, Error, IntoResponse, Result, Route, }; -use diesel::{sql_query, sql_types::Text, QueryableByName, RunQueryDsl}; -use serde::{Deserialize, Serialize}; +use crate::dev::{controller, controller::MySqlQuery}; -use crate::dev::{ - controller, - controller::{HealthCheckResponse, MyQueryResult, MySqlQuery}, -}; - -use crate::auth::Role; -use crate::{ - auth::{Auth, Permission}, - Database, -}; - -#[handler] -async fn health() -> Result { - controller::health(); - Ok(Json(HealthCheckResponse { - message: "healthy".to_string(), - }) - .with_status(StatusCode::OK) - .into_response()) -} - -#[handler] -async fn migrate(db: Data<&Database>) -> Result { - if controller::migrate_db(db.0) { - Ok(Json(true).with_status(StatusCode::OK).into_response()) - } else { - Ok(Json(false) - .with_status(StatusCode::INTERNAL_SERVER_ERROR) - .into_response()) - } -} - -#[handler] -async fn needs_migration(db: Data<&Database>) -> Result { - Ok(Json(controller::needs_migration(db.0))) -} +use crate::Database; #[handler] async fn query(db: Data<&Database>, body: Json) -> Result { @@ -54,28 +18,6 @@ async fn query(db: Data<&Database>, body: Json) -> Result) -> Result { - Ok(Json(controller::is_connected(db.0))) -} - -#[handler] -async fn has_system_role(auth: Auth) -> Result { - Ok(Json(controller::check_system_role(&auth))) -} - -#[handler] -async fn add_system_role(auth: Auth, db: Data<&Database>) -> Result { - Ok(Json(controller::add_system_role(db.0, &auth))) -} - pub fn api() -> Route { - Route::new() - .at("/health", get(health)) - .at("/db/migrate", get(migrate)) - .at("/db/needs-migration", get(needs_migration)) - .at("/db/query", post(query)) - .at("/db/is-connected", get(is_connected)) - .at("/auth/add-system-role", get(add_system_role)) - .at("/auth/has-system-role", get(has_system_role)) + Route::new().at("/db/query", post(query)) } diff --git a/create-rust-app/src/dev/frontend_dev_server.rs b/create-rust-app/src/dev/frontend_dev_server.rs new file mode 100644 index 00000000..549fceb8 --- /dev/null +++ b/create-rust-app/src/dev/frontend_dev_server.rs @@ -0,0 +1,51 @@ +use std::iter::FromIterator; +use std::path::PathBuf; +use std::sync::Arc; +use tokio::process::Command; +use tokio::sync::Mutex; + +use tokio::sync::broadcast::{Receiver, Sender}; + +use super::NPM; + +use super::{check_exit, DevServerEvent, DevState}; + +pub async fn start( + project_dir: &'static str, + dev_port: u16, + mut signal_rx: Receiver, + _dev_server_events_s: Sender, + state: Arc>, +) { + println!("Starting frontend server @ http://localhost:21012/"); + let mut m = state.lock().await; + m.frontend_server_running = true; + drop(m); + let mut child_process = Command::new(NPM) + .args(["run", "start:dev"]) + .env("DEV_SERVER_PORT", dev_port.to_string()) + .current_dir(PathBuf::from_iter([project_dir, "frontend"])) + .spawn() + .unwrap(); + + // tokio::fs::create_dir_all(PathBuf::from(".cargo/tmp")).await.unwrap(); + // tokio::fs::write( + // PathBuf::from(".cargo/tmp/frontend_pid.txt"), + // child_process.id().unwrap_or_default().to_string(), + // ).await.unwrap(); + + while let Ok(event) = signal_rx.recv().await { + if let DevServerEvent::SHUTDOWN = event { + println!("Shutting down frontend server..."); + if child_process.id().is_some() { + child_process.kill().await.unwrap(); + } + let mut m = state.lock().await; + m.frontend_server_running = false; + check_exit(&m); + drop(m); + break; + } + } + println!("frontend server stopped."); +} diff --git a/create-rust-app/src/dev/mod.rs b/create-rust-app/src/dev/mod.rs index 46d533ec..1abc55ff 100644 --- a/create-rust-app/src/dev/mod.rs +++ b/create-rust-app/src/dev/mod.rs @@ -1,3 +1,324 @@ +mod backend_compiling_server; +mod dev_server; +mod frontend_dev_server; + pub mod controller; +use cargo_metadata::CompilerMessage; +use cargo_toml::Manifest; +use serde::Serialize; +use serde_json::json; mod endpoints; +use crate::util::net::find_free_port; +use async_priority_channel as priority; pub use endpoints::*; +use std::iter::FromIterator; +use std::path::PathBuf; +use std::process::exit; +use std::sync::Arc; +use tokio::sync::Mutex; +use watchexec::event::{Event, Priority, Tag}; +use watchexec::signal::source::worker; +use watchexec::signal::source::MainSignal; + +#[cfg(windows)] +const NPM: &str = "npm.cmd"; + +#[cfg(not(windows))] +const NPM: &str = "npm"; + +pub async fn vitejs_ping_down() { + let port = std::env::var("DEV_SERVER_PORT"); + if port.is_err() { + return; + } + let port = port.unwrap(); + + let url = format!("http://localhost:{port}/vitejs-down"); + + // send event to dev server that the backend is up! + match reqwest::get(url).await { + Ok(_) => {} + Err(_) => { + println!("WARNING: Could not inform dev server that vitejs is down."); + } + }; +} + +pub async fn vitejs_ping_up() { + let port = std::env::var("DEV_SERVER_PORT"); + if port.is_err() { + return; + } + let port = port.unwrap(); + + let url = format!("http://localhost:{port}/vitejs-up"); + + // send event to dev server that the backend is up! + match reqwest::get(url).await { + Ok(_) => {} + Err(_) => { + println!("WARNING: Could not inform dev server that vitejs is up."); + } + }; +} + +pub async fn setup_development() { + let port = std::env::var("DEV_SERVER_PORT"); + if port.is_err() { + return; + } + let port = port.unwrap(); + + let url = format!("http://localhost:{port}/backend-up"); + + // send event to dev server that the backend is up! + match reqwest::get(url).await { + Ok(_) => {} + Err(_) => { + println!("WARNING: Could not inform dev server of presence."); + } + }; +} + +#[allow(non_camel_case_types)] +#[derive(Debug, Clone)] +pub enum DevServerEvent { + /* + These events are internal: they shouldn't be used to communicate with the web browser client + */ + /// sent when Ctrl+C or similar signals are sent to the dev server parent process + SHUTDOWN, // related to external event: "BackendStatus" + /// sent once when the backend compiles successfully and more when files in the migrations/ directory change + CHECK_MIGRATIONS, // related to external event: "PendingMigrations" + + /* + These events are external: they are sent to the web browser client + */ + PendingMigrations(bool, Vec), + MigrationResponse(bool, Option), + FeaturesList(Vec), + ViteJSStatus(bool), + BackendCompiling(bool), + BackendRestarting(bool), + BackendStatus(bool), + CompileSuccess(bool), + CompileMessages(Vec), +} + +#[derive(Serialize, Debug, Clone)] +pub enum MigrationStatus { + Applied, + Pending, + AppliedButMissingLocally, + Unknown, +} + +#[derive(Serialize, Debug, Clone)] +pub struct CreateRustAppMigration { + name: String, + version: String, + status: MigrationStatus, +} + +impl DevServerEvent { + pub fn json(self) -> String { + match self { + DevServerEvent::CHECK_MIGRATIONS => json!({ + "type": "_", + }) + .to_string(), + DevServerEvent::PendingMigrations(migrations_pending, migrations) => json!({ + "type": "migrationsPending", + "status": migrations_pending, + "migrations": migrations + }) + .to_string(), + DevServerEvent::MigrationResponse(success, error_message) => json!({ + "type": "migrateResponse", + "status": success, + "error": error_message, + }) + .to_string(), + DevServerEvent::FeaturesList(list) => json!({ + "type": "featuresList", + "features": list + }) + .to_string(), + DevServerEvent::ViteJSStatus(b) => json!({ + "type": "viteStatus", + "status": b + }) + .to_string(), + DevServerEvent::CompileSuccess(b) => json!({ + "type": "compileStatus", + "compiled": b + }) + .to_string(), + DevServerEvent::BackendCompiling(b) => json!({ + "type": "backendCompiling", + "compiling": b + }) + .to_string(), + DevServerEvent::BackendStatus(b) => json!({ + "type": "backendStatus", + "status": b + }) + .to_string(), + DevServerEvent::BackendRestarting(b) => json!({ + "type": "backendRestarting", + "status": b + }) + .to_string(), + DevServerEvent::SHUTDOWN => json!({ + "type": "backendStatus", + "status": "false" + }) + .to_string(), + DevServerEvent::CompileMessages(msgs) => { + let messages = serde_json::to_value(&msgs).unwrap(); + json!({ + "type": "compilerMessages", + "messages": messages + }) + .to_string() + } + } + } +} + +#[derive(Debug)] +pub struct DevState { + pub frontend_server_running: bool, + pub backend_server_running: bool, + pub watchexec_running: bool, +} + +fn get_features(project_dir: &'static str) -> Vec { + let cargo_toml = Manifest::from_path(PathBuf::from_iter([project_dir, "Cargo.toml"])) + .unwrap_or_else(|_| panic!("Could not find \"{}\"", project_dir)); + // .expect(&format!("Could not find \"{project_dir}\"")); + let deps = cargo_toml.dependencies; + let dep = deps.get("create-rust-app").unwrap_or_else(|| { + panic!( + "Expected \"{}\" to list 'create-rust-app' as a dependency.", + project_dir + ) + }); + let dep = dep.clone(); + + dep.req_features().to_vec() +} + +pub fn run_server(project_dir: &'static str) { + clearscreen::clear().expect("failed to clear screen"); + + let features = get_features(project_dir); + + println!(".................................."); + println!(".. Starting development server ..."); + println!(".................................."); + let rt = tokio::runtime::Runtime::new().unwrap(); + + let dev_port = std::env::var("DEV_SERVER_PORT") + .map(|p| { + p.parse::() + .expect("Could not parse DEV_SERVER_PORT to u16") + }) + .unwrap_or_else(|_| { + find_free_port(60012..65535) + .expect("FATAL: Could not find a free port for the development server.") + }); + + rt.block_on(async move { + let state = Arc::new(Mutex::new(DevState { + backend_server_running: false, + frontend_server_running: false, + watchexec_running: false, + })); + let state2 = state.clone(); + + // used for shutdown only (TODO: merge this with next broadcast channel) + let (signal_tx, signal_rx) = tokio::sync::broadcast::channel::(64); + let signal_rx2 = signal_tx.subscribe(); + + // used for websocket events + let (dev_server_events_s, dev_server_events_r) = + tokio::sync::broadcast::channel::(64); + let dev_server_events_s2 = dev_server_events_s.clone(); + let dev_server_events_s3 = dev_server_events_s.clone(); + + // HACK: used to ignore some file modification events as a result of interaction with the dev server + let (file_events_s, _) = tokio::sync::broadcast::channel::(64); + let file_events_s2 = file_events_s.clone(); + + tokio::spawn(async move { + dev_server::start( + project_dir, + dev_port, + dev_server_events_r, + dev_server_events_s3, + file_events_s2, + features, + ) + .await + }); + tokio::spawn(async move { + backend_compiling_server::start( + project_dir, + dev_port, + signal_rx2, + dev_server_events_s2, + state2, + file_events_s, + ) + .await + }); + tokio::spawn(async move { + frontend_dev_server::start( + project_dir, + dev_port, + signal_rx, + dev_server_events_s.clone(), + state, + ) + .await + }); + + listen_for_signals(signal_tx).await + }); +} + +fn check_exit(state: &DevState) { + // println!("Checking exit status {:#?}", state); + if !state.backend_server_running && !state.frontend_server_running && !state.watchexec_running { + exit(0); + } +} + +async fn listen_for_signals(signal_tx: tokio::sync::broadcast::Sender) { + let (ev_s, ev_r) = priority::bounded::(1024); + let (er_s, mut er_r) = tokio::sync::mpsc::channel(64); + + // panic on errors + tokio::spawn(async move { + while let Some(error) = er_r.recv().await { + panic!( + "Error handling process signal:\n==============================\n{:#?}", + error + ); + } + }); + + // broadcast signals + tokio::spawn(async move { + while let Ok((event, _)) = ev_r.recv().await { + if event.tags.contains(&Tag::Signal(MainSignal::Terminate)) + || event.tags.contains(&Tag::Signal(MainSignal::Interrupt)) + { + signal_tx.send(DevServerEvent::SHUTDOWN).unwrap(); + } + } + }); + + worker(er_s, ev_s).await.unwrap(); +} diff --git a/create-rust-app/src/lib.rs b/create-rust-app/src/lib.rs index f5dd7887..6765b8a6 100644 --- a/create-rust-app/src/lib.rs +++ b/create-rust-app/src/lib.rs @@ -3,6 +3,11 @@ compile_error!( "feature \"backend_actix-web\" and feature \"backend_poem\" cannot be enabled at the same time" ); +#[cfg(all(feature = "database_sqlite", feature = "database_postgres"))] +compile_error!( + "feature \"database_sqlite\" and feature \"database_postgres\" cannot be enabled at the same time" +); + // #[cfg(not(any(feature = "backend_poem", feature = "backend_actix-web")))] // compile_error!("Please enable one of the backend features (options: 'backend_actix-web', 'backend-poem')"); @@ -17,12 +22,15 @@ pub mod auth; #[cfg(all(feature = "plugin_dev", debug_assertions))] pub mod dev; +#[cfg(all(feature = "plugin_dev", debug_assertions))] +pub use dev::setup_development; mod database; pub use database::{Connection, Database, Pool}; #[cfg(feature = "backend_poem")] mod logger; +#[allow(deprecated)] // deprecated; we're going to roll out better logging soon. Use your own tracing setup for now! #[cfg(feature = "backend_poem")] pub use logger::Logger as PoemLogger; @@ -83,8 +91,6 @@ pub fn setup() -> AppData { panic!("No DATABASE_URL environment variable set!"); } - Mailer::check_environment_variables(); - AppData { mailer: Mailer::new(), database: Database::new(), diff --git a/create-rust-app/src/logger.rs b/create-rust-app/src/logger.rs index 3a96fd00..f28d0c09 100644 --- a/create-rust-app/src/logger.rs +++ b/create-rust-app/src/logger.rs @@ -1,21 +1,23 @@ use poem::{async_trait, Endpoint, IntoResponse, Middleware, Request, Response, Result}; +#[deprecated] /// Logger middleware that provides similar functionality as [`actix_web::middleware::Logger`] /// for the poem backend pub struct Logger; +#[allow(deprecated)] impl Middleware for Logger { type Output = LogImpl; - /// TODO: documentation fn transform(&self, ep: E) -> Self::Output { LogImpl(ep) } } -/// TODO: documentation +#[deprecated] pub struct LogImpl(E); +#[allow(deprecated)] #[async_trait] impl Endpoint for LogImpl { type Output = Response; diff --git a/create-rust-app/src/mailer.rs b/create-rust-app/src/mailer.rs index e4369758..e2b84d0a 100644 --- a/create-rust-app/src/mailer.rs +++ b/create-rust-app/src/mailer.rs @@ -71,28 +71,31 @@ impl Mailer { /// prints messages denoting which, if any, of the required /// environment variables were not set pub fn check_environment_variables() { - if std::env::var("SMTP_FROM_ADDRESS").is_err() { - println!("Note: Mailing disabled; 'SMTP_FROM_ADDRESS' not set."); - } - - if std::env::var("SMTP_SERVER").is_err() { - println!("Note: Mailing disabled; 'SMTP_SERVER' not set."); - } + let vars = vec![ + "SMTP_FROM_ADDRESS", + "SMTP_SERVER", + "SMTP_USERNAME", + "SMTP_PASSWORD", + "SEND_MAIL", + ]; - if std::env::var("SMTP_USERNAME").is_err() { - println!("Note: Mailing disabled; 'SMTP_USERNAME' not set."); - } + let unset_vars = vars + .into_iter() + .filter(|v| std::env::var(v).is_err()) + .collect::>(); - if std::env::var("SMTP_PASSWORD").is_err() { - println!("Note: Mailing disabled; 'SMTP_PASSWORD' not set."); + if !unset_vars.is_empty() { + println!( + "Warning: Mailing disabled; the following variables must be set: {}", + unset_vars.join(", ") + ); } - if std::env::var("SEND_MAIL").is_err() - || !std::env::var("SEND_MAIL") - .unwrap() - .eq_ignore_ascii_case("true") + let send_mail_value = std::env::var("SEND_MAIL").unwrap_or_default(); + if !send_mail_value.eq_ignore_ascii_case("true") + && !send_mail_value.eq_ignore_ascii_case("false") { - println!("Note: Mailing disabled; 'SEND_MAIL' not 'true'."); + println!("Warning: SEND_MAIL must be `true` or `false`"); } } diff --git a/create-rust-app/src/storage/attachment.rs b/create-rust-app/src/storage/attachment.rs index a69ceca9..124c6e55 100644 --- a/create-rust-app/src/storage/attachment.rs +++ b/create-rust-app/src/storage/attachment.rs @@ -3,7 +3,6 @@ use diesel::QueryResult; //use md5; //use mime_guess; use serde::{Deserialize, Serialize}; -//use std::sync::Arc; use uuid::Uuid; use crate::diesel::*; @@ -132,7 +131,7 @@ impl Attachment { /// in poem, we need to pass in the pool itself because the Connection is not Send+Sync which poem handlers require #[cfg(feature = "backend_poem")] pub async fn attach( - pool: Arc, + pool: std::sync::Arc, storage: &Storage, name: String, record_type: String, @@ -160,7 +159,7 @@ impl Attachment { if existing.is_ok() { // one already exists, we need to delete it if overwrite_existing { - Attachment::detach(pool.clone(), &storage, existing.unwrap().id).await.map_err(|err| { + Attachment::detach(pool.clone(), &storage, existing.unwrap().id).await.map_err(|_| { format!("Could not detach the existing attachment for '{name}' attachment on '{record_type}'", name=name.clone(), record_type=record_type.clone()) })?; } else { @@ -250,7 +249,11 @@ impl Attachment { /// in poem, we need to pass in the pool itself because the Connection is not Send+Sync which poem handlers require #[cfg(feature = "backend_poem")] - pub async fn detach(pool: Arc, storage: &Storage, item_id: ID) -> Result<(), String> { + pub async fn detach( + pool: std::sync::Arc, + storage: &Storage, + item_id: ID, + ) -> Result<(), String> { let mut db = pool.get().unwrap(); let attached = diff --git a/create-rust-app/src/storage/mod.rs b/create-rust-app/src/storage/mod.rs index fef0fb25..61d2dae8 100644 --- a/create-rust-app/src/storage/mod.rs +++ b/create-rust-app/src/storage/mod.rs @@ -232,24 +232,24 @@ impl Storage { } fn check_environment_variables() { - if std::env::var("S3_HOST").is_err() { - println!("Note: Storage disabled; 'S3_HOST' is not set.") - } - - if std::env::var("S3_REGION").is_err() { - println!("Note: Storage disabled; 'S3_REGION' is not set.") - } - - if std::env::var("S3_BUCKET").is_err() { - println!("Note: Storage disabled; 'S3_BUCKET' is not set.") - } - - if std::env::var("S3_ACCESS_KEY_ID").is_err() { - println!("Note: Storage disabled; 'S3_ACCESS_KEY_ID' is not set.") - } - - if std::env::var("S3_SECRET_ACCESS_KEY").is_err() { - println!("Note: Storage disabled; 'S3_SECRET_ACCESS_KEY' is not set.") + let vars = vec![ + "S3_HOST", + "S3_REGION", + "S3_BUCKET", + "S3_ACCESS_KEY_ID", + "S3_SECRET_ACCESS_KEY", + ]; + + let unset_vars = vars + .into_iter() + .filter(|v| std::env::var(v).is_err()) + .collect::>(); + + if !unset_vars.is_empty() { + println!( + "Warning: Storage disabled; the following variables must be set: {}", + unset_vars.join(", ") + ); } } diff --git a/create-rust-app/src/util/actix_web_utils.rs b/create-rust-app/src/util/actix_web_utils.rs index 9292453e..2fcff93b 100644 --- a/create-rust-app/src/util/actix_web_utils.rs +++ b/create-rust-app/src/util/actix_web_utils.rs @@ -1,3 +1,5 @@ +use std::sync::Mutex; + use super::template_utils::SinglePageApplication; use crate::util::template_utils::{to_template_name, DEFAULT_TEMPLATE, TEMPLATES}; use actix_files::NamedFile; @@ -30,6 +32,10 @@ async fn render_spa_handler( template_response(content) } +// used to count number of refresh requests sent when viteJS dev server is down +#[cfg(debug_assertions)] +static REQUEST_REFRESH_COUNT: Mutex = Mutex::new(0); + /// takes a request to, say, www.you_webapp.com/foo/bar and looks in the ./backend/views folder /// for a html file/template at the matching path (in this case, ./foo/bar.html), /// defaults to index.html @@ -46,7 +52,37 @@ pub async fn render_views(req: HttpRequest) -> HttpResponse { #[cfg(debug_assertions)] if path.eq("/__vite_ping") { println!("The vite dev server seems to be down..."); - return HttpResponse::NotFound().finish(); + } + { + // Catch viteJS ping requests and try to handle them gracefully + // Request the browser to refresh the page (maybe the server is up but the browser just can't reconnect) + + if path.eq("/__vite_ping") { + #[cfg(feature = "plugin_dev")] + { + crate::dev::vitejs_ping_down().await; + } + let mut count = REQUEST_REFRESH_COUNT.lock().unwrap(); + if *count < 3 { + *count += 1; + println!("The vite dev server seems to be down... refreshing page ({count})."); + return HttpResponse::build(StatusCode::TEMPORARY_REDIRECT) + .append_header(("Location", ".")) + .finish(); + } else { + println!("The vite dev server is down."); + return HttpResponse::NotFound().finish(); + } + } + // If this is a non-viteJS ping request, let's reset the refresh attempt count + else { + #[cfg(feature = "plugin_dev")] + { + crate::dev::vitejs_ping_up().await; + } + let mut count = REQUEST_REFRESH_COUNT.lock().unwrap(); + *count = 0; + } } let mut template_path = to_template_name(req.path()); diff --git a/create-rust-app/src/util/net.rs b/create-rust-app/src/util/net.rs index f8706592..327f0a2d 100644 --- a/create-rust-app/src/util/net.rs +++ b/create-rust-app/src/util/net.rs @@ -1,4 +1,7 @@ -use std::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6, TcpListener, ToSocketAddrs}; +use std::{ + net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6, TcpListener, ToSocketAddrs}, + ops::Range, +}; /// binds a [`TcpListener`] to the given [`addr`](`ToSocketAddrs`) fn test_bind(addr: A) -> bool { @@ -14,3 +17,8 @@ pub fn is_port_free(port: u16) -> bool { test_bind(ipv6) && test_bind(ipv4) } + +/// max range is 65535 +pub fn find_free_port(mut range: Range) -> Option { + range.find(|port| is_port_free(*port)) +} diff --git a/create-rust-app/src/util/poem_utils.rs b/create-rust-app/src/util/poem_utils.rs index 805178ae..f63e8b9d 100644 --- a/create-rust-app/src/util/poem_utils.rs +++ b/create-rust-app/src/util/poem_utils.rs @@ -1,3 +1,5 @@ +use std::sync::Mutex; + use poem::http::{StatusCode, Uri}; use poem::middleware::{AddData, AddDataEndpoint}; use poem::web::Data; @@ -32,6 +34,10 @@ async fn render_spa_handler(spa_info: Data<&SinglePageApplication>) -> impl Into template_response(content) } +// used to count number of refresh requests sent when viteJS dev server is down +#[cfg(debug_assertions)] +static REQUEST_REFRESH_COUNT: Mutex = Mutex::new(0); + /// takes a request to, say, www.you_webapp.com/foo/bar and looks in the ./backend/views folder /// for a html file/template at the matching path (in this case, ./foo/bar.html), /// defaults to index.html @@ -45,9 +51,34 @@ pub async fn render_views(uri: &Uri) -> impl IntoResponse { let path = uri.path(); #[cfg(debug_assertions)] - if path.eq("/__vite_ping") { - println!("The vite dev server seems to be down..."); - return StatusCode::NOT_FOUND.into_response(); + { + // Catch viteJS ping requests and try to handle them gracefully + // Request the browser to refresh the page (maybe the server is up but the browser just can't reconnect) + + if path.eq("/__vite_ping") { + #[cfg(feature = "plugin_dev")] + { + crate::dev::vitejs_ping_down().await; + } + let mut count = REQUEST_REFRESH_COUNT.lock().unwrap(); + if *count < 3 { + *count = 1 + *count; + println!("The vite dev server seems to be down... refreshing page ({count})."); + return poem::web::Redirect::temporary(".").into_response(); + } else { + println!("The vite dev server is down."); + return StatusCode::NOT_FOUND.into_response(); + } + } + // If this is a non-viteJS ping request, let's reset the refresh attempt count + else { + #[cfg(feature = "plugin_dev")] + { + crate::dev::vitejs_ping_up().await; + } + let mut count = REQUEST_REFRESH_COUNT.lock().unwrap(); + *count = 0; + } } let mut template_path = to_template_name(path); diff --git a/create-rust-app_cli/src/content/project.rs b/create-rust-app_cli/src/content/project.rs index 13c56f39..72c84258 100644 --- a/create-rust-app_cli/src/content/project.rs +++ b/create-rust-app_cli/src/content/project.rs @@ -91,6 +91,14 @@ path = ".cargo/bin/tsync.rs" name = "dsync" path = ".cargo/bin/dsync.rs" +[[bin]] +name = "backend" +path = ".cargo/bin/backend.rs" + +[[bin]] +name = "frontend" +path = ".cargo/bin/frontend.rs" + [[bin]] name = "{project_name}" path = "backend/main.rs" diff --git a/create-rust-app_cli/src/main.rs b/create-rust-app_cli/src/main.rs index 5c1bd3af..39806ece 100644 --- a/create-rust-app_cli/src/main.rs +++ b/create-rust-app_cli/src/main.rs @@ -310,19 +310,19 @@ fn create_project( let items = vec![ "Authentication Plugin: local email-based authentication", "Container Plugin: dockerize your app", - "Development Plugin: adds dev warnings and an admin portal", + // "Development Plugin: adds dev warnings and an admin portal", "Storage Plugin: adds S3 file storage capabilities", "GraphQL Plugin: bootstraps a GraphQL setup including a playground", "Utoipa Plugin: Autogenerated OpenAPI documentation served in a SwaggerUI playground", ]; let chosen: Vec = MultiSelect::with_theme(&ColorfulTheme::default()) .items(&items) - .defaults(&[true, true, true, true, true, false]) + .defaults(&[true, true, true, true, false]) .interact()?; let add_plugin_auth = chosen.iter().any(|x| *x == 0); let add_plugin_container = chosen.iter().any(|x| *x == 1); - let add_plugin_dev = chosen.iter().any(|x| *x == 2); + let add_plugin_dev = true; // chosen.iter().any(|x| *x == 2); let add_plugin_storage = chosen.iter().any(|x| *x == 3); let add_plugin_graphql = chosen.iter().any(|x| *x == 4); let add_plugin_utoipa = chosen.iter().any(|x| *x == 5); diff --git a/create-rust-app_cli/src/plugins/dev.rs b/create-rust-app_cli/src/plugins/dev.rs index aceb1d6a..0d2bda9d 100644 --- a/create-rust-app_cli/src/plugins/dev.rs +++ b/create-rust-app_cli/src/plugins/dev.rs @@ -5,6 +5,7 @@ use crate::utils::fs; use crate::utils::logger::add_file_msg; use crate::BackendFramework; use anyhow::Result; +use indoc::indoc; use rust_embed::RustEmbed; pub struct Dev {} @@ -19,11 +20,6 @@ impl Plugin for Dev { } fn install(&self, install_config: InstallConfig) -> Result<()> { - if !install_config.plugin_auth { - crate::logger::error("The development plugin requires the Auth plugin (temporarily). For details, see: https://github.com/Wulf/create-rust-app/issues/52"); - std::process::exit(1); - } - for filename in Asset::iter() { if filename.starts_with("README.md") || filename.contains(".cargo/admin") && !filename.contains(".cargo/admin/dist") @@ -53,7 +49,18 @@ impl Plugin for Dev { "react-query": "^3.21.0""#, )?; - fs::append("frontend/src/dev.tsx", "\nimport './setupDevelopment'")?; + fs::append( + "frontend/src/dev.tsx", + indoc! {r##" + // Sets up the development environment. + // + // Note: When running `cargo frontend` and `cargo backend` individually, "DEV_SERVER_PORT" is not set. + // Use `cargo fullstack` for the full development experience. + if (import.meta.env.DEV_SERVER_PORT !== 'false') { + import('./setupDevelopment') + } + "##}, + )?; match install_config.backend_framework { BackendFramework::ActixWeb => { diff --git a/create-rust-app_cli/template-plugin-dev/frontend/src/setupDevelopment.tsx b/create-rust-app_cli/template-plugin-dev/frontend/src/setupDevelopment.tsx index 3b096b6f..6253b9ed 100644 --- a/create-rust-app_cli/template-plugin-dev/frontend/src/setupDevelopment.tsx +++ b/create-rust-app_cli/template-plugin-dev/frontend/src/setupDevelopment.tsx @@ -4,217 +4,310 @@ /* require() this file in development mode to enable development hints */ -import React, { useEffect, useRef, useState } from 'react' -import ReactDOM from 'react-dom' -import { - QueryClient, - QueryClientProvider, - useMutation, - useQuery, -} from 'react-query' - -const warning = ( - - - -) -const close = ( - - - -) +import { useCallback, useEffect, useRef, useState } from 'react' +import ReactDOM from 'react-dom/client' +import ReconnectingWebsocket from 'reconnecting-websocket' +import Ansi from 'ansi-to-react' -interface Action { - label: string - fn: () => void +interface Migration { + name: string + status: 'Applied' | 'AppliedButMissingLocally' | 'Pending' | 'Unknown' + version: string } -const DevBoxItem = (props: { - actions?: Action[] - children: React.ReactNode -}) => { - return ( -
-
{props.children}
-
- {props.actions?.map((action) => ( - - ))} -
-
- ) -} +const useWebsocketConnection = () => { + const [features, setFeaturesList] = useState>(new Set()) + const [backendCompileState, setBackendCompileState] = useState(true) + const [backendCompilingState, setBackendCompilingState] = useState(false) + const [backendRestartingState, setBackendRestartingState] = useState(false) + const [backendCompilerMessages, setBackendCompilerMessages] = useState([]) + const [backendOnlineState, setBackendOnlineState] = useState(true) + const [wsConnected, setWsConnected] = useState(false) + const [viteStatus, setViteStatus] = useState(true) + const [migrationsPending, setMigrationPending] = useState(false) + const [migrating, setMigrating] = useState(false) + const [migrationSuccess, setMigrationSuccess] = useState(true) + const [migrationError, setMigrationError] = useState() + const [migrations, setMigrations] = useState([]) -const DevBox = () => { - const [display, setDisplay] = useState(true) + const wsRef = useRef() - const healthQuery = useQuery( - 'health', - () => fetch('/api/development/health').then((r) => r.json()), - { - onSuccess: () => queryClient.invalidateQueries('migrations'), + useEffect(() => { + const connect = () => { + if (wsRef.current) { + wsRef.current.close() + } + + const ws = new ReconnectingWebsocket(`ws://localhost:${import.meta.env.DEV_SERVER_PORT}/ws`); + + wsRef.current = ws; + + ws.onopen = () => { setWsConnected(true) } + ws.onclose = () => { setWsConnected(false) } + ws.onerror = (err) => { + console.error('create-rust-app:plugin_dev: Websocket error', err) + ws.close() + } + ws.onmessage = (payload) => { + const data = JSON.parse(payload.data) + + // console.log("DEV MESSAGE", data) + + if (data.type === 'featuresList') { + setFeaturesList(new Set(data.features)) + } else if (data.type === 'backendCompiling') { + setBackendCompilingState(data.compiling) + } else if (data.type === 'compileStatus') { + setBackendCompileState(data.compiled) + } else if (data.type === 'backendStatus') { + setBackendOnlineState(data.status) + } else if (data.type === 'compilerMessages') { + setBackendCompilerMessages(data.messages) + } else if (data.type === 'viteStatus') { + setViteStatus(data.status) + } else if (data.type === 'backendRestarting') { + setBackendRestartingState(data.status) + } else if (data.type === 'migrationsPending') { + setMigrationPending(data.status) + setMigrations(data.migrations) + } else if (data.type === 'migrateResponse') { + setMigrationSuccess(data.status) + setMigrationError(data.error) + setMigrating(false) + } + } } - ) - const dbMigrationQuery = useQuery('migrations', () => - fetch('/api/development/db/needs-migration').then((r) => r.json()) - ) - const dbMigrateMutation = useMutation( - () => fetch('/api/development/db/migrate').then((r) => r.json()), - { - onSuccess: () => queryClient.invalidateQueries('migrations'), + + connect(); + + return () => { + if (wsRef.current) { + wsRef.current.close() + } } - ) - // const hasSystemRoleQuery = useQuery('role-check', () => fetch('/api/development/auth/has-system-role', { headers: { Authorization: `${auth.accessToken}` } }).then(r => r.json())) - // const addSystemRoleMutation = useMutation(() => fetch('/api/development/auth/add-system-role', { headers: { Authorization: `${auth.accessToken}` } }).then(r => r.json()), { - // onSuccess: () => queryClient.invalidateQueries('role-check') - // }) - - const isFetching = - healthQuery.isFetching || - dbMigrationQuery.isFetching /*|| hasSystemRoleQuery.isFetching*/ - const shouldDisplay = useRef(true) - - shouldDisplay.current = !!( - ( - healthQuery.isError || - dbMigrationQuery.isError || - dbMigrationQuery.data - ) /*|| hasSystemRoleQuery.data === false*/ - ) + }, []) + + interface RequestBase { + type: string + } - // useEffect(() => { - // hasSystemRoleQuery.refetch() - // }, [auth.isAuthenticated, auth.accessToken]) + interface OpenRequest extends RequestBase { type: 'open'; file: string } + interface MigrateRequest extends RequestBase { type: 'migrate' } + + type WebsocketRequest = + | MigrateRequest + | OpenRequest + + const send = useCallback((message: WebsocketRequest) => { + let msg = '' + + if (message.type === 'open') { + msg = `open:${message.file}` + } else if (message.type === 'migrate') { + msg = `migrate` + setMigrating(true) + } + + if (msg.length === 0) return + + wsRef.current?.send(msg) + }, [wsRef.current]); + + return { + features, + backendCompileState, + backendCompilingState, + backendRestartingState, + backendCompilerMessages, + backendOnlineState, + wsConnected, + viteStatus, + migrationsPending, + migrationSuccess, + migrationError, + migrating, + migrations, + send + } +} + + +const DevBox = () => { + const state = useWebsocketConnection() + const [isWeirdMigrationState, setWeirdMigrationState] = useState(false) + const send = state.send + + const shouldDisplay = state.backendCompileState !== true + || state.backendRestartingState === true + || state.backendOnlineState !== true + || state.wsConnected === false + || state.backendCompilingState === true + || state.viteStatus === false + || state.migrationsPending === true + || state.migrationSuccess === false + || state.migrating === true + || isWeirdMigrationState useEffect(() => { - setDisplay(shouldDisplay.current) - }, [ - shouldDisplay, - healthQuery.isError, - healthQuery.isFetching, - healthQuery.data, - dbMigrationQuery.isError, - dbMigrationQuery.data, - // hasSystemRoleQuery.data - ]) + if (state.migrations?.find(m => m.status === 'AppliedButMissingLocally')) { + setWeirdMigrationState(true) + } + }, [state.migrations]) + + const compilerErrors = state.backendCompilerMessages + ?.filter(m => m?.message?.level === "error") return (
- -
- -
DEVBOX
{' '} - {isFetching &&
} -
- {!isFetching && ( - {warning} - )} -
- {(healthQuery.isError || healthQuery.isFetching) && ( - queryClient.invalidateQueries('health'), - }, - ]} - > - WARN!  - {healthQuery.isError && 'The backend is not reachable.'} - {healthQuery.isFetching && 'Connecting to backend...'} - - )} - {!healthQuery.isError && ( - <> - {!dbMigrationQuery.isError && dbMigrationQuery.data && ( - { - if (dbMigrateMutation.isLoading) return - dbMigrateMutation.mutate() - }, - }, - ]} - > - WARN! Some - migrations are pending! - - )} - {/* {hasSystemRoleQuery.data === false && { - if (addSystemRoleMutation.isLoading) return - addSystemRoleMutation.mutate() - } - }]}> - INFO! The logged in user doesn't have the developer role. You may not have access to administrative developer tasks. - } */} - - )} + {/* + DEV SERVER CONNECTION + */} + {state.wsConnected === false &&
Connecting to development server...
} + {state.viteStatus === false && } + + {/* + MIGRATIONS + */} + {isWeirdMigrationState &&
Migrations are in a strange state... setWeirdMigrationState(false)}>ok, thanks!
} + {state.migrationSuccess === false &&
⚠️ Migration failed ({state.migrationError}).
} + {state.migrationsPending === true && + } + + {(state.migrationsPending === true || isWeirdMigrationState) &&
{state.migrations?.sort((a, b) => b.version.localeCompare(a.version)).map(m => ( +
+ + {m.name} + + + + {m.status} + +
+ ))} +
} + + {/* + COMPILATION + */} + {state.backendCompileState === false &&
The last compilation failed.
} + {state.backendOnlineState === false &&
The backend is offline!
} + {state.backendCompilingState &&
🔨 Compiling backend...
} + {state.backendRestartingState === true &&
♻️ Restarting backend...
} + {compilerErrors.length > 0 && +
+          {compilerErrors.map(m => (
+             {
+                send({ type: 'open', file: path })
+              }}
+              m={m}
+            />
+          ))}
+        
+ }
) } +interface CompilerMessage { + package_id: { + repr: string + }, + target: { + name: string, + kind: Array<"bin" | "example" | "test" | "bench" | "lib" | "custom-build">, + crate_types: Array, + required_features: Array, + src_path: string, + edition: string, + doctest: boolean, + test: boolean, + doc: boolean, + }, + message: { + message: string, + code?: { + code?: string + explanation?: string + }, + /// "error: internal compiler error", "error", "warning", "note", "help" + level: "error: internal compiler error" | "error" | "warning" | "note" | "help" | "failure-note", + /// A list of source code spans this diagnostic is associated with. + spans: Array<{ + file_name: string, + byte_start: number, + byte_end: number, + line_start: number, + line_end: number, + column_start: number, + column_end: number, + is_primary: boolean, + text: Array<{ + text: string, + highlight_start: number, + highlight_end: number, + }>, + label?: string, + suggested_replacement?: string, + suggestion_applicability?: string, + expansion?: any, + }>, + children: Array, + rendered?: string, // The message as rustc would render it + }, +} + +interface CompilerMessageProps { + m: CompilerMessage + onOpenFile: (f: string) => void +} +const CompilerMessage = ({ m, onOpenFile }: CompilerMessageProps) => { + const primarySpan = m.message.spans.filter(s => s.is_primary)[0]; + + if (!primarySpan) return <> //
no primary span,
{JSON.stringify(m)}
+ + return
+    
+ {m.message.level}: {m.message.message} +
+
+   -->  + { + e.preventDefault() + onOpenFile(`${primarySpan.file_name}`) + }} + > + {primarySpan.file_name} {/* the following svg was taken from https://iconmonstr.com/cursor-2-svg/ and does not belong to the create-rust-app project */} + +
+
+ {m.message.rendered?.split('\n').slice(2).join('\n')} +
+
+} + const DEVBOX_ID = 'create-rust-app-devbox' const existingDevBoxes = document.getElementsByClassName(DEVBOX_ID) if (existingDevBoxes.length > 0) { @@ -233,96 +326,52 @@ devBoxStyle.innerHTML = ` position: fixed; height: 0px; width: 0px; - right: 12px; - bottom: 12px; - font-size: 16px; + bottom: 0; + font-size: 12px; } .${DEVBOX_ID} > div { position: absolute; bottom: 0; - right: 0; + width: 100vw; transition-property: all; transition-duration: .5s; font-family: monospace; transition-timing-function: cubic-bezier(1, 1, 1, 1); color: black; background: rgb(255,69,69); - background: linear-gradient(345deg, rgba(255,69,69,1) 0%, rgba(207,144,58,1) 100%); - min-width: 400px; - box-shadow: 0 4px 4px #00000044; + background: linear-gradient(0deg, rgba(0,0,0,1) 0%, rgba(255,255,255,1) 100%); + box-shadow: 0 0px 8px #00000088; } -.${DEVBOX_ID}-loader, -.${DEVBOX_ID}-loader:before, -.${DEVBOX_ID}-loader:after { - background: #e86840; - -webkit-animation: ${DEVBOX_ID}-loader-animation 1s infinite ease-in-out; - animation: ${DEVBOX_ID}-loader-animation 1s infinite ease-in-out; - width: 1em; - height: 1em; +.${DEVBOX_ID} a { + color: skyblue; } -.${DEVBOX_ID}-loader { - color: #e86840; - text-indent: -9999em; - margin: 21px 30px; - position: relative; - font-size: 11px; - -webkit-transform: translateZ(0); - -ms-transform: translateZ(0); - transform: translateZ(0); - -webkit-animation-delay: -0.16s; - animation-delay: -0.16s; -} -.${DEVBOX_ID}-loader:before, -.${DEVBOX_ID}-loader:after { - position: absolute; - top: 0; - content: ''; + +.file:hover { + text-decoration: underline; + cursor: pointer; + background-color: gray; } -.${DEVBOX_ID}-loader:before { - left: -1.5em; - -webkit-animation-delay: -0.32s; - animation-delay: -0.32s; + +.guide { + color: rgb(106,112,246); } -.${DEVBOX_ID}-loader:after { - left: 1.5em; + +.warning { + color: yellow; } -@-webkit-keyframes ${DEVBOX_ID}-loader-animation { - 0%, 80%, 100% { - box-shadow: 0 0; - height: 4em; - } - 40% { - box-shadow: 0 -2em; - height: 5em; - } + +.error { + color: rgb(237,118,108); } -@keyframes ${DEVBOX_ID}-loader-animation { - 0%, 80%, 100% { - box-shadow: 0 0; - height: 4em; - } - 40% { - box-shadow: 0 -2em; - height: 5em; - } + +pre { + white-space: pre-line; + font-family: monospace; } ` devBox.appendChild(devBoxStyle) devBox.appendChild(document.createElement('div')) document.body.appendChild(devBox) -const queryClient = new QueryClient({ - defaultOptions: { queries: { retry: false } }, -}) - -ReactDOM.render( - <> - - - - , - devBox.children[1] -) - -export {} +ReactDOM.createRoot(devBox.children[1]).render() diff --git a/create-rust-app_cli/template-plugin-graphql/backend/graphql/mod.rs+actix_web b/create-rust-app_cli/template-plugin-graphql/backend/graphql/mod.rs+actix_web index f1c09356..d77e7f02 100644 --- a/create-rust-app_cli/template-plugin-graphql/backend/graphql/mod.rs+actix_web +++ b/create-rust-app_cli/template-plugin-graphql/backend/graphql/mod.rs+actix_web @@ -9,7 +9,7 @@ pub use subscription::SubscriptionRoot; use actix_web::{HttpRequest, HttpResponse, web}; use async_graphql::{Data, Schema}; -use async_graphql::http::{GraphQLPlaygroundConfig, playground_source}; +// use async_graphql::http::{GraphQLPlaygroundConfig, playground_source}; use async_graphql_actix_web::{GraphQLRequest, GraphQLResponse, GraphQLSubscription}; use create_rust_app::auth::{Auth, Permission}; use jsonwebtoken::{DecodingKey, decode, Validation}; diff --git a/create-rust-app_cli/template-plugin-graphql/backend/graphql/mod.rs+poem b/create-rust-app_cli/template-plugin-graphql/backend/graphql/mod.rs+poem index 71aa3d49..844db6df 100644 --- a/create-rust-app_cli/template-plugin-graphql/backend/graphql/mod.rs+poem +++ b/create-rust-app_cli/template-plugin-graphql/backend/graphql/mod.rs+poem @@ -8,12 +8,12 @@ pub use mutation::MutationRoot; pub use subscription::SubscriptionRoot; use async_graphql::{Data, Schema}; -use async_graphql::http::{ALL_WEBSOCKET_PROTOCOLS, GraphQLPlaygroundConfig, playground_source}; +use async_graphql::http::{ALL_WEBSOCKET_PROTOCOLS}; use create_rust_app::auth::{Auth, Permission}; use jsonwebtoken::{DecodingKey, decode, Validation}; use std::iter::FromIterator; -use async_graphql_poem::{GraphQLProtocol, GraphQLRequest, GraphQLResponse, GraphQLSubscription, GraphQLWebSocket}; -use poem::{handler, Result, IntoResponse}; +use async_graphql_poem::{GraphQLProtocol, GraphQLRequest, GraphQLResponse, GraphQLWebSocket}; +use poem::{handler, IntoResponse}; use poem::web::Html; use poem::web::websocket::WebSocket; diff --git a/create-rust-app_cli/template-plugin-storage/backend/services/file.rs+poem b/create-rust-app_cli/template-plugin-storage/backend/services/file.rs+poem index 489b01d2..237985ca 100644 --- a/create-rust-app_cli/template-plugin-storage/backend/services/file.rs+poem +++ b/create-rust-app_cli/template-plugin-storage/backend/services/file.rs+poem @@ -1,11 +1,9 @@ use std::sync::Arc; use create_rust_app::{Attachment, AttachmentBlob, AttachmentData, Database, Storage}; -use poem::{get, handler, http::StatusCode, IntoResponse, Response, Result, Route, web::{Data, Json, Multipart, Path, Query}}; +use poem::{get, handler, http::StatusCode, IntoResponse, Response, Result, Route, web::{Data, Json, Multipart, Path}}; use serde::Serialize; -// use futures_util::StreamExt as _; - #[derive(Serialize)] #[tsync::tsync] struct FileInfo { diff --git a/create-rust-app_cli/template/.cargo/bin/backend.rs b/create-rust-app_cli/template/.cargo/bin/backend.rs new file mode 100644 index 00000000..b6cca2ad --- /dev/null +++ b/create-rust-app_cli/template/.cargo/bin/backend.rs @@ -0,0 +1,13 @@ +use std::{process::Command, path::PathBuf}; + +pub fn main() { + let dir = env!("CARGO_MANIFEST_DIR"); + + Command::new("cargo") + .args(["watch", "-x", "run", "-w", "backend"]) + .current_dir(PathBuf::from(dir)) + .spawn() + .unwrap() + .wait_with_output() + .unwrap(); +} \ No newline at end of file diff --git a/create-rust-app_cli/template/.cargo/bin/frontend.rs b/create-rust-app_cli/template/.cargo/bin/frontend.rs new file mode 100644 index 00000000..736ac970 --- /dev/null +++ b/create-rust-app_cli/template/.cargo/bin/frontend.rs @@ -0,0 +1,27 @@ +use std::{process::Command, path::PathBuf}; + +#[cfg(windows)] +pub const NPM: &'static str = "npm.cmd"; + +#[cfg(not(windows))] +pub const NPM: &'static str = "npm"; + +pub fn main() { + if !create_rust_app::net::is_port_free(21012) { + println!("========================================================"); + println!(" ViteJS (the frontend compiler/bundler) needs to run on"); + println!(" port 21012 but it seems to be in use."); + println!("========================================================"); + panic!("Port 21012 is taken but is required for development!") + } + + let dir = env!("CARGO_MANIFEST_DIR"); + + Command::new(NPM) + .args(["run", "start:dev"]) + .current_dir(PathBuf::from_iter([dir, "frontend"])) + .spawn() + .unwrap() + .wait_with_output() + .unwrap(); +} \ No newline at end of file diff --git a/create-rust-app_cli/template/.cargo/bin/fullstack.rs b/create-rust-app_cli/template/.cargo/bin/fullstack.rs index fea08d04..564f9e6d 100644 --- a/create-rust-app_cli/template/.cargo/bin/fullstack.rs +++ b/create-rust-app_cli/template/.cargo/bin/fullstack.rs @@ -1,5 +1,3 @@ -use std::{process::Command, path::PathBuf}; - mod dsync; mod tsync; @@ -12,16 +10,10 @@ pub fn main() { panic!("Port 21012 is taken but is required for development!") } - let dir = env!("CARGO_MANIFEST_DIR"); + let project_dir = env!("CARGO_MANIFEST_DIR"); dsync::main(); tsync::main(); - Command::new("yarn") - .arg("fullstack") - .current_dir(PathBuf::from_iter([dir, "frontend"])) - .spawn() - .unwrap() - .wait_with_output() - .unwrap(); + create_rust_app::dev::run_server(project_dir); } diff --git a/create-rust-app_cli/template/.cargo/config b/create-rust-app_cli/template/.cargo/config index d0f466dc..46891c8b 100644 --- a/create-rust-app_cli/template/.cargo/config +++ b/create-rust-app_cli/template/.cargo/config @@ -2,6 +2,8 @@ fullstack="run --bin fullstack" tsync="run --bin tsync" dsync="run --bin dsync" +backend="run --bin backend" +frontend="run --bin frontend" [build] target-dir=".cargo/.build" diff --git a/create-rust-app_cli/template/.gitignore b/create-rust-app_cli/template/.gitignore index c99f3df7..2651040b 100644 --- a/create-rust-app_cli/template/.gitignore +++ b/create-rust-app_cli/template/.gitignore @@ -1,5 +1,6 @@ /frontend/dist /.cargo/.build +/.cargo/tmp .env* !.env.example build-log*.txt diff --git a/create-rust-app_cli/template/README.md b/create-rust-app_cli/template/README.md index d8e81aa0..3b64cbc9 100644 --- a/create-rust-app_cli/template/README.md +++ b/create-rust-app_cli/template/README.md @@ -44,7 +44,7 @@ Outputs to `frontend/src/types/rust.d.ts`. ```sh # frontend -cd frontend && yarn && yarn start +cd frontend && npm && npm start ``` ```sh @@ -60,3 +60,6 @@ cargo watch -x run -i frontend/ - `diesel database setup` - `diesel database reset` + +# Tips +* Use the [mold](https://github.com/rui314/mold) linker for slightly faster compilation. diff --git a/create-rust-app_cli/template/backend/mail/example.rs b/create-rust-app_cli/template/backend/mail/example.rs index 76b51005..e2436150 100644 --- a/create-rust-app_cli/template/backend/mail/example.rs +++ b/create-rust-app_cli/template/backend/mail/example.rs @@ -1,5 +1,6 @@ use create_rust_app::Mailer; +#[allow(dead_code)] pub fn send(mailer: &Mailer, to_email: &str) { let subject = "Example Email"; let text = format!( diff --git a/create-rust-app_cli/template/backend/main.rs+actix_web b/create-rust-app_cli/template/backend/main.rs+actix_web index 91d37256..939c2569 100644 --- a/create-rust-app_cli/template/backend/main.rs+actix_web +++ b/create-rust-app_cli/template/backend/main.rs+actix_web @@ -1,4 +1,3 @@ -#[macro_use] extern crate diesel; use actix_files::{Files}; @@ -13,6 +12,7 @@ mod mail; #[actix_web::main] async fn main() -> std::io::Result<()> { + #[cfg(debug_assertions)] create_rust_app::setup_development().await; let app_data = create_rust_app::setup(); HttpServer::new(move || { diff --git a/create-rust-app_cli/template/backend/main.rs+poem b/create-rust-app_cli/template/backend/main.rs+poem index 27bb51d1..a82b7110 100644 --- a/create-rust-app_cli/template/backend/main.rs+poem +++ b/create-rust-app_cli/template/backend/main.rs+poem @@ -1,10 +1,9 @@ -#[macro_use] extern crate diesel; use poem::endpoint::{StaticFilesEndpoint}; use poem::{ listener::TcpListener, - middleware::{AddData, CookieJarManager}, + middleware::{AddData, CookieJarManager, Tracing}, EndpointExt, Route, Server, }; @@ -15,6 +14,7 @@ mod services; #[tokio::main] async fn main() -> Result<(), std::io::Error> { + #[cfg(debug_assertions)] create_rust_app::setup_development().await; tracing_subscriber::fmt::init(); let data = create_rust_app::setup(); @@ -36,8 +36,8 @@ async fn main() -> Result<(), std::io::Error> { .run( app.with(AddData::new(data.mailer)) .with(AddData::new(data.database)) - .with(create_rust_app::PoemLogger) .with(CookieJarManager::new()) + .with(Tracing) .catch_error(create_rust_app::not_found), ) .await diff --git a/create-rust-app_cli/template/build.rs b/create-rust-app_cli/template/build.rs index 65734c9e..71e9f961 100644 --- a/create-rust-app_cli/template/build.rs +++ b/create-rust-app_cli/template/build.rs @@ -11,6 +11,11 @@ use std::{io::Write, process::Command}; * so that we can include println!(...) statements in build.rs */ +/* + * Note 2: this file was written for *nix systems -- it likely won't + * work on windows! + */ + #[allow(dead_code)] fn shell(command: &str) { // println!("build.rs => {}", command); @@ -37,9 +42,9 @@ fn shell(command: &str) { fn main() { // Only install frontend dependencies when building release #[cfg(not(debug_assertions))] - shell("cd frontend && yarn install --frozen-lockfile"); + shell("cd frontend && npm install --frozen-lockfile"); // Only build frontend when building a release #[cfg(not(debug_assertions))] - shell("cd frontend && yarn build"); + shell("cd frontend && npm build"); } diff --git a/create-rust-app_cli/template/frontend/package-lock.json b/create-rust-app_cli/template/frontend/package-lock.json new file mode 100644 index 00000000..cd6346f0 --- /dev/null +++ b/create-rust-app_cli/template/frontend/package-lock.json @@ -0,0 +1,1938 @@ +{ + "name": "create-rust-app", + "version": "0.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "requires": { + "@babel/highlight": "^7.18.6" + } + }, + "@babel/compat-data": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.5.tgz", + "integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==", + "dev": true + }, + "@babel/core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.5.tgz", + "integrity": "sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==", + "dev": true, + "requires": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.5", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-module-transforms": "^7.20.2", + "@babel/helpers": "^7.20.5", + "@babel/parser": "^7.20.5", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.5", + "@babel/types": "^7.20.5", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + } + }, + "@babel/generator": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.5.tgz", + "integrity": "sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==", + "dev": true, + "requires": { + "@babel/types": "^7.20.5", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz", + "integrity": "sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.20.0", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "semver": "^6.3.0" + } + }, + "@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "dev": true + }, + "@babel/helper-function-name": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "dev": true, + "requires": { + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-transforms": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.2.tgz", + "integrity": "sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.2" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + }, + "@babel/helper-simple-access": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "dev": true, + "requires": { + "@babel/types": "^7.20.2" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-string-parser": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "dev": true + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "dev": true + }, + "@babel/helpers": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.6.tgz", + "integrity": "sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w==", + "dev": true, + "requires": { + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.5", + "@babel/types": "^7.20.5" + } + }, + "@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz", + "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==", + "dev": true + }, + "@babel/plugin-syntax-jsx": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", + "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-react-jsx": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.19.0.tgz", + "integrity": "sha512-UVEvX3tXie3Szm3emi1+G63jyw1w5IcMY0FSKM+CRnKRI5Mr1YbCNgsSTwoTwKphQEG9P+QqmuRFneJPZuHNhg==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/plugin-syntax-jsx": "^7.18.6", + "@babel/types": "^7.19.0" + } + }, + "@babel/plugin-transform-react-jsx-development": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz", + "integrity": "sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==", + "dev": true, + "requires": { + "@babel/plugin-transform-react-jsx": "^7.18.6" + } + }, + "@babel/plugin-transform-react-jsx-self": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.18.6.tgz", + "integrity": "sha512-A0LQGx4+4Jv7u/tWzoJF7alZwnBDQd6cGLh9P+Ttk4dpiL+J5p7NSNv/9tlEFFJDq3kjxOavWmbm6t0Gk+A3Ig==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-react-jsx-source": { + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.19.6.tgz", + "integrity": "sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.19.0" + } + }, + "@babel/template": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" + } + }, + "@babel/traverse": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.5.tgz", + "integrity": "sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.5", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.20.5", + "@babel/types": "^7.20.5", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", + "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + }, + "@esbuild/linux-loong64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz", + "integrity": "sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==", + "dev": true, + "optional": true + }, + "@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "@playwright/test": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.29.0.tgz", + "integrity": "sha512-gp5PVBenxTJsm2bATWDNc2CCnrL5OaA/MXQdJwwkGQtqTjmY+ZOqAdLqo49O9MLTDh2vYh+tHWDnmFsILnWaeA==", + "dev": true, + "requires": { + "@types/node": "*", + "playwright-core": "1.29.0" + }, + "dependencies": { + "playwright-core": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.29.0.tgz", + "integrity": "sha512-pboOm1m0RD6z1GtwAbEH60PYRfF87vKdzOSRw2RyO0Y0a7utrMyWN2Au1ojGvQr4umuBMODkKTv607YIRypDSQ==", + "dev": true + } + } + }, + "@remix-run/router": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.1.0.tgz", + "integrity": "sha512-rGl+jH/7x1KBCQScz9p54p0dtPLNeKGb3e0wD2H5/oZj41bwQUnXdzbj2TbUAFhvD7cp9EyEQA4dEgpUFa1O7Q==" + }, + "@rollup/pluginutils": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", + "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==", + "dev": true, + "requires": { + "estree-walker": "^2.0.1", + "picomatch": "^2.2.2" + } + }, + "@types/minimist": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", + "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", + "dev": true + }, + "@types/node": { + "version": "15.14.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-15.14.9.tgz", + "integrity": "sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A==", + "dev": true + }, + "@types/normalize-package-data": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", + "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", + "dev": true + }, + "@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", + "dev": true + }, + "@types/react": { + "version": "18.0.26", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.26.tgz", + "integrity": "sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==", + "dev": true, + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "@types/react-dom": { + "version": "18.0.9", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.9.tgz", + "integrity": "sha512-qnVvHxASt/H7i+XG1U1xMiY5t+IHcPGUK7TDMDzom08xa7e86eCeKOiLZezwCKVxJn6NEiiy2ekgX8aQssjIKg==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, + "@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", + "dev": true + }, + "@vitejs/plugin-react": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-1.3.2.tgz", + "integrity": "sha512-aurBNmMo0kz1O4qRoY+FM4epSA39y3ShWGuqfLRA/3z0oEJAdtoSfgA3aO98/PCCHAqMaduLxIxErWrVKIFzXA==", + "dev": true, + "requires": { + "@babel/core": "^7.17.10", + "@babel/plugin-transform-react-jsx": "^7.17.3", + "@babel/plugin-transform-react-jsx-development": "^7.16.7", + "@babel/plugin-transform-react-jsx-self": "^7.16.7", + "@babel/plugin-transform-react-jsx-source": "^7.16.7", + "@rollup/pluginutils": "^4.2.1", + "react-refresh": "^0.13.0", + "resolve": "^1.22.0" + } + }, + "anser": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/anser/-/anser-1.4.10.tgz", + "integrity": "sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "ansi-to-react": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/ansi-to-react/-/ansi-to-react-6.1.6.tgz", + "integrity": "sha512-+HWn72GKydtupxX9TORBedqOMsJRiKTqaLUKW8txSBZw9iBpzPKLI8KOu4WzwD4R7hSv1zEspobY6LwlWvwZ6Q==", + "dev": true, + "requires": { + "anser": "^1.4.1", + "escape-carriage": "^1.3.0" + } + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "browserslist": { + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" + } + }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, + "camelcase-keys": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-7.0.2.tgz", + "integrity": "sha512-Rjs1H+A9R+Ig+4E/9oyB66UC5Mj9Xq3N//vcLf2WzgdTi/3gUu3Z9KoqmlrEG4VuuLK8wJHofxzdQXz/knhiYg==", + "dev": true, + "requires": { + "camelcase": "^6.3.0", + "map-obj": "^4.1.0", + "quick-lru": "^5.1.1", + "type-fest": "^1.2.1" + } + }, + "caniuse-lite": { + "version": "1.0.30001439", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001439.tgz", + "integrity": "sha512-1MgUzEkoMO6gKfXflStpYgZDlFM7M/ck/bgfVCACO5vnAf0fXoNVHdWtqGU+MYca+4bL9Z5bpOVmR33cWW9G2A==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "concurrently": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-7.6.0.tgz", + "integrity": "sha512-BKtRgvcJGeZ4XttiDiNcFiRlxoAeZOseqUvyYRUp/Vtd+9p1ULmeoSqGsDA+2ivdeDFpqrJvGvmI+StKfKl5hw==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "date-fns": "^2.29.1", + "lodash": "^4.17.21", + "rxjs": "^7.0.0", + "shell-quote": "^1.7.3", + "spawn-command": "^0.0.2-1", + "supports-color": "^8.1.0", + "tree-kill": "^1.2.2", + "yargs": "^17.3.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "csstype": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", + "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==", + "dev": true + }, + "date-fns": { + "version": "2.29.3", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz", + "integrity": "sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==", + "dev": true + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "decamelize": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-5.0.1.tgz", + "integrity": "sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA==", + "dev": true + }, + "decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "dev": true, + "requires": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "dev": true + } + } + }, + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true + }, + "dotenv": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", + "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==" + }, + "electron-to-chromium": { + "version": "1.4.284", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "env-editor": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/env-editor/-/env-editor-1.1.0.tgz", + "integrity": "sha512-7AXskzN6T7Q9TFcKAGJprUbpQa4i1VsAetO9rdBqbGMGlragTziBgWt4pVYJMBWHQlLoX0buy6WFikzPH4Qjpw==", + "dev": true + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "esbuild": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.54.tgz", + "integrity": "sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==", + "dev": true, + "requires": { + "@esbuild/linux-loong64": "0.14.54", + "esbuild-android-64": "0.14.54", + "esbuild-android-arm64": "0.14.54", + "esbuild-darwin-64": "0.14.54", + "esbuild-darwin-arm64": "0.14.54", + "esbuild-freebsd-64": "0.14.54", + "esbuild-freebsd-arm64": "0.14.54", + "esbuild-linux-32": "0.14.54", + "esbuild-linux-64": "0.14.54", + "esbuild-linux-arm": "0.14.54", + "esbuild-linux-arm64": "0.14.54", + "esbuild-linux-mips64le": "0.14.54", + "esbuild-linux-ppc64le": "0.14.54", + "esbuild-linux-riscv64": "0.14.54", + "esbuild-linux-s390x": "0.14.54", + "esbuild-netbsd-64": "0.14.54", + "esbuild-openbsd-64": "0.14.54", + "esbuild-sunos-64": "0.14.54", + "esbuild-windows-32": "0.14.54", + "esbuild-windows-64": "0.14.54", + "esbuild-windows-arm64": "0.14.54" + } + }, + "esbuild-android-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz", + "integrity": "sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==", + "dev": true, + "optional": true + }, + "esbuild-android-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz", + "integrity": "sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==", + "dev": true, + "optional": true + }, + "esbuild-darwin-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz", + "integrity": "sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==", + "dev": true, + "optional": true + }, + "esbuild-darwin-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz", + "integrity": "sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==", + "dev": true, + "optional": true + }, + "esbuild-freebsd-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz", + "integrity": "sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==", + "dev": true, + "optional": true + }, + "esbuild-freebsd-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz", + "integrity": "sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==", + "dev": true, + "optional": true + }, + "esbuild-linux-32": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz", + "integrity": "sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==", + "dev": true, + "optional": true + }, + "esbuild-linux-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz", + "integrity": "sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==", + "dev": true, + "optional": true + }, + "esbuild-linux-arm": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz", + "integrity": "sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==", + "dev": true, + "optional": true + }, + "esbuild-linux-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz", + "integrity": "sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==", + "dev": true, + "optional": true + }, + "esbuild-linux-mips64le": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz", + "integrity": "sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==", + "dev": true, + "optional": true + }, + "esbuild-linux-ppc64le": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz", + "integrity": "sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==", + "dev": true, + "optional": true + }, + "esbuild-linux-riscv64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz", + "integrity": "sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==", + "dev": true, + "optional": true + }, + "esbuild-linux-s390x": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz", + "integrity": "sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==", + "dev": true, + "optional": true + }, + "esbuild-netbsd-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz", + "integrity": "sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==", + "dev": true, + "optional": true + }, + "esbuild-openbsd-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz", + "integrity": "sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==", + "dev": true, + "optional": true + }, + "esbuild-sunos-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz", + "integrity": "sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==", + "dev": true, + "optional": true + }, + "esbuild-windows-32": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz", + "integrity": "sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==", + "dev": true, + "optional": true + }, + "esbuild-windows-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz", + "integrity": "sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==", + "dev": true, + "optional": true + }, + "esbuild-windows-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz", + "integrity": "sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==", + "dev": true, + "optional": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-carriage": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/escape-carriage/-/escape-carriage-1.3.0.tgz", + "integrity": "sha512-ATWi5MD8QlAGQOeMgI8zTp671BG8aKvAC0M7yenlxU4CRLGO/sKthxVUyjiOFKjHdIo+6dZZUNFgHFeVEaKfGQ==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-stdin": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz", + "integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==", + "dev": true + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, + "glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true + }, + "indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "json5": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.2.tgz", + "integrity": "sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ==", + "dev": true + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "line-column-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/line-column-path/-/line-column-path-3.0.0.tgz", + "integrity": "sha512-Atocnm7Wr9nuvAn97yEPQa3pcQI5eLQGBz+m6iTb+CVw+IOzYB9MrYK7jI7BfC9ISnT4Fu0eiwhAScV//rp4Hw==", + "dev": true, + "requires": { + "type-fest": "^2.0.0" + }, + "dependencies": { + "type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true + } + } + }, + "lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true + }, + "meow": { + "version": "10.1.5", + "resolved": "https://registry.npmjs.org/meow/-/meow-10.1.5.tgz", + "integrity": "sha512-/d+PQ4GKmGvM9Bee/DPa8z3mXs/pkvJE2KEThngVNOqtmljC6K7NMPxtc2JeZYTmpWb9k/TmxjeL18ez3h7vCw==", + "dev": true, + "requires": { + "@types/minimist": "^1.2.2", + "camelcase-keys": "^7.0.0", + "decamelize": "^5.0.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.2", + "read-pkg-up": "^8.0.0", + "redent": "^4.0.0", + "trim-newlines": "^4.0.2", + "type-fest": "^1.2.2", + "yargs-parser": "^20.2.9" + }, + "dependencies": { + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true + } + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true + }, + "minimatch": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.1.tgz", + "integrity": "sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "dev": true + }, + "node-releases": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.8.tgz", + "integrity": "sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==", + "dev": true + }, + "normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "requires": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "dev": true, + "requires": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + } + }, + "open-editor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/open-editor/-/open-editor-4.0.0.tgz", + "integrity": "sha512-5mKZ98iFdkivozt5XTCOspoKbL3wtYu6oOoVxfWQ0qUX9NYsK8pdkHE7VUHXr+CwyC3nf6mV0S5FPsMS65innw==", + "dev": true, + "requires": { + "env-editor": "^1.0.0", + "execa": "^5.1.1", + "line-column-path": "^3.0.0", + "open": "^8.4.0" + } + }, + "open-editor-cli": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/open-editor-cli/-/open-editor-cli-2.0.0.tgz", + "integrity": "sha512-YI0Nmljw1/JJaCnUl5vjIB9RIhMA7Yg3vkFxWckIVvs89ixSflQRINOMAH1yPdm51gMn7c8DUkruufPm+SGfhg==", + "dev": true, + "requires": { + "get-stdin": "^9.0.0", + "meow": "^10.1.1", + "open-editor": "^4.0.0", + "temp-write": "^5.0.0" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "postcss": { + "version": "8.4.20", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.20.tgz", + "integrity": "sha512-6Q04AXR1212bXr5fh03u8aAwbLxAQNGQ/Q1LNa0VfOI06ZAlhPHtQvE4OIdpj4kLThXilalPnmDSOD65DcHt+g==", + "dev": true, + "requires": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true + }, + "react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "requires": { + "loose-envify": "^1.1.0" + } + }, + "react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "requires": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + } + }, + "react-refresh": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.13.0.tgz", + "integrity": "sha512-XP8A9BT0CpRBD+NYLLeIhld/RqG9+gktUjW1FkE+Vm7OCinbG1SshcK5tb9ls4kzvjZr9mOQc7HYgBngEyPAXg==", + "dev": true + }, + "react-router": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.5.0.tgz", + "integrity": "sha512-fqqUSU0NC0tSX0sZbyuxzuAzvGqbjiZItBQnyicWlOUmzhAU8YuLgRbaCL2hf3sJdtRy4LP/WBrWtARkMvdGPQ==", + "requires": { + "@remix-run/router": "1.1.0" + } + }, + "react-router-dom": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.5.0.tgz", + "integrity": "sha512-/XzRc5fq80gW1ctiIGilyKFZC/j4kfe75uivMsTChFbkvrK4ZrF3P3cGIc1f/SSkQ4JiJozPrf+AwUHHWVehVg==", + "requires": { + "@remix-run/router": "1.1.0", + "react-router": "6.5.0" + } + }, + "read-pkg": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-6.0.0.tgz", + "integrity": "sha512-X1Fu3dPuk/8ZLsMhEj5f4wFAF0DWoK7qhGJvgaijocXxBmSToKfbFtqbxMO7bVjNA1dmE5huAzjXj/ey86iw9Q==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^3.0.2", + "parse-json": "^5.2.0", + "type-fest": "^1.0.1" + } + }, + "read-pkg-up": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-8.0.0.tgz", + "integrity": "sha512-snVCqPczksT0HS2EC+SxUndvSzn6LRCwpfSvLrIfR5BKDQQZMaI6jPRC9dYvYFDRAuFEAnkwww8kBBNE/3VvzQ==", + "dev": true, + "requires": { + "find-up": "^5.0.0", + "read-pkg": "^6.0.0", + "type-fest": "^1.0.1" + } + }, + "reconnecting-websocket": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/reconnecting-websocket/-/reconnecting-websocket-4.4.0.tgz", + "integrity": "sha512-D2E33ceRPga0NvTDhJmphEgJ7FUYF0v4lr1ki0csq06OdlxKfugGzN0dSkxM/NfqCxYELK4KcaTOUOjTV6Dcng==", + "dev": true + }, + "redent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-4.0.0.tgz", + "integrity": "sha512-tYkDkVVtYkSVhuQ4zBgfvciymHaeuel+zFKXShfDnFP5SyVEP7qo70Rf1jTOTCx3vGNAbnEi/xFkcfQVMIBWag==", + "dev": true, + "requires": { + "indent-string": "^5.0.0", + "strip-indent": "^4.0.0" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true + }, + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "rollup": { + "version": "2.77.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.77.3.tgz", + "integrity": "sha512-/qxNTG7FbmefJWoeeYJFbHehJ2HNWnjkAFRKzWN/45eNBBF/r8lo992CwcJXEzyVxs5FmfId+vTSTQDb+bxA+g==", + "dev": true, + "requires": { + "fsevents": "~2.3.2" + } + }, + "rxjs": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", + "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", + "dev": true, + "requires": { + "tslib": "^2.1.0" + } + }, + "scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "requires": { + "loose-envify": "^1.1.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "shell-quote": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.4.tgz", + "integrity": "sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw==", + "dev": true + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true + }, + "spawn-command": { + "version": "0.0.2-1", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", + "integrity": "sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==", + "dev": true + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", + "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "strip-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.0.0.tgz", + "integrity": "sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==", + "dev": true, + "requires": { + "min-indent": "^1.0.1" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, + "temp-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "dev": true + }, + "temp-write": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/temp-write/-/temp-write-5.0.0.tgz", + "integrity": "sha512-cJhnzBW7DjNox7VcZDXeNlQSkIh3mX/h+M0n0Fh+zgT7YAHwI9c+OngKx4MCiQCVx9iXxV104xYlJgDBCCtawA==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.6", + "is-stream": "^2.0.0", + "temp-dir": "^2.0.0", + "uuid": "^8.3.2" + } + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true + }, + "tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true + }, + "trim-newlines": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-4.0.2.tgz", + "integrity": "sha512-GJtWyq9InR/2HRiLZgpIKv+ufIKrVrvjQWEj7PxAXNc5dwbNJkqhAUoAGgzRmULAnoOM5EIpveYd3J2VeSAIew==", + "dev": true + }, + "tslib": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", + "dev": true + }, + "type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true + }, + "typescript": { + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==" + }, + "update-browserslist-db": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "dev": true, + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "vite": { + "version": "2.9.15", + "resolved": "https://registry.npmjs.org/vite/-/vite-2.9.15.tgz", + "integrity": "sha512-fzMt2jK4vQ3yK56te3Kqpkaeq9DkcZfBbzHwYpobasvgYmP2SoAr6Aic05CsB4CzCZbsDv4sujX3pkEGhLabVQ==", + "dev": true, + "requires": { + "esbuild": "^0.14.27", + "fsevents": "~2.3.2", + "postcss": "^8.4.13", + "resolve": "^1.22.0", + "rollup": ">=2.59.0 <2.78.0" + } + }, + "web-vitals": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-2.1.4.tgz", + "integrity": "sha512-sVWcwhU5mX6crfI5Vd2dC4qchyTqxV8URinzt25XqVh+bHEPGH4C3NPrNionCP7Obx59wrYEbNlw4Z8sjALzZg==" + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yargs": { + "version": "17.6.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", + "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", + "dev": true, + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + } + } + } + \ No newline at end of file diff --git a/create-rust-app_cli/template/frontend/package.json b/create-rust-app_cli/template/frontend/package.json index 2b70c079..15829761 100644 --- a/create-rust-app_cli/template/frontend/package.json +++ b/create-rust-app_cli/template/frontend/package.json @@ -11,10 +11,10 @@ "web-vitals": "^3.1.1" }, "scripts": { - "start": "yarn vite", + "start": "vite", "build": "vite build", "preview": "vite preview", - "fullstack": "yarn install && yarn concurrently --prefix-colors cyan,magenta --kill-others -n backend,frontend \"cargo --color=always watch -x run -w backend\" \"yarn start\"", + "start:dev": "npm install && vite", "test": "npx playwright test ./tests" }, "eslintConfig": { @@ -41,8 +41,16 @@ "@types/react": "^18.0.27", "@types/react-dom": "^18.0.10", "@vitejs/plugin-react": "^3.0.1", + "ansi-to-react": "^6.1.6", "concurrently": "^7.6.0", "glob": "^8.1.0", + "reconnecting-websocket": "4.4.0", "vite": "^4.0.4" + }, + "overrides": { + "ansi-to-react": { + "react": "^18", + "react-dom": "^18" + } } } diff --git a/create-rust-app_cli/template/frontend/src/App.tsx b/create-rust-app_cli/template/frontend/src/App.tsx index e7cb6970..5eca8875 100644 --- a/create-rust-app_cli/template/frontend/src/App.tsx +++ b/create-rust-app_cli/template/frontend/src/App.tsx @@ -9,7 +9,7 @@ const App = () => { /* CRA: app hooks */ // @ts-ignore - return ( + return (
@@ -22,11 +22,11 @@ const App = () => {
- - } /> - } /> - {/* CRA: routes */} - + + } /> + } /> + {/* CRA: routes */} +
) diff --git a/create-rust-app_cli/template/frontend/src/containers/Home.tsx b/create-rust-app_cli/template/frontend/src/containers/Home.tsx index d693ae74..9957126c 100644 --- a/create-rust-app_cli/template/frontend/src/containers/Home.tsx +++ b/create-rust-app_cli/template/frontend/src/containers/Home.tsx @@ -12,7 +12,7 @@ export const Home = () => { react-logo

- Edit app/src/App.tsx and save to reload. + Edit frontend/src/App.tsx and save to reload.

diff --git a/create-rust-app_cli/template/frontend/src/containers/Todo.tsx b/create-rust-app_cli/template/frontend/src/containers/Todo.tsx index a32a992e..aed0eb40 100644 --- a/create-rust-app_cli/template/frontend/src/containers/Todo.tsx +++ b/create-rust-app_cli/template/frontend/src/containers/Todo.tsx @@ -95,9 +95,9 @@ export const Todos = () => {

Todos

{(!todos || todos.total_items === 0) && "No todos, create one!"} - {todos?.items.map((todo, index) => + {todos?.items.map((todo) => todo.id === selectedTodo?.id ? ( -
+
{ - const rollupInput = isDevelopment ? { +const buildRollupInput = (isDevelopment): { [entryAlias: string]: string } => { + const rollupInput: { [entryAlias: string]: string } = isDevelopment ? { 'dev.tsx': resolve(__dirname, './src/dev.tsx') } : {} // TODO: use import.meta.glob() + npm uninstall glob - glob.sync(resolve(__dirname, './bundles/**/*.tsx')).map(inputEntry => { + glob.sync(resolve(__dirname, './bundles/**/*.tsx')).map((inputEntry: string) => { let outputEntry = inputEntry // output entry is an absolute path, let's remove the absolute part: outputEntry = outputEntry.replace(`${__dirname}/`, '') @@ -26,15 +26,25 @@ const buildRollupInput = (isDevelopment) => { // https://vitejs.dev/config/ export default defineConfig(async ({ command, mode }) => ({ base: command === 'serve' ? 'http://localhost:21012' : '/', + clearScreen: false, build: { manifest: true, rollupOptions: { input: buildRollupInput(command === 'serve') }, }, - plugins: [react()], + define: { + // When this variable is set, setupDevelopment.tsx will also be loaded! + // See `dev.tsx` which is included in development. + 'import.meta.env.DEV_SERVER_PORT': JSON.stringify(process.env.DEV_SERVER_PORT ?? false), + }, + plugins: [ + react(), + ], + server: { port: 21012, + host: '127.0.0.1', proxy: { // with options '/api': { diff --git a/create-rust-app_cli/template/frontend/yarn.lock b/create-rust-app_cli/template/frontend/yarn.lock deleted file mode 100644 index 5cc7cc19..00000000 --- a/create-rust-app_cli/template/frontend/yarn.lock +++ /dev/null @@ -1,1030 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@ampproject/remapping@^2.1.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" - integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== - dependencies: - "@jridgewell/gen-mapping" "^0.1.0" - "@jridgewell/trace-mapping" "^0.3.9" - -"@babel/code-frame@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" - integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== - dependencies: - "@babel/highlight" "^7.18.6" - -"@babel/compat-data@^7.20.5": - version "7.20.10" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.10.tgz#9d92fa81b87542fff50e848ed585b4212c1d34ec" - integrity sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg== - -"@babel/core@^7.20.7": - version "7.20.12" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.12.tgz#7930db57443c6714ad216953d1356dac0eb8496d" - integrity sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg== - dependencies: - "@ampproject/remapping" "^2.1.0" - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.7" - "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-module-transforms" "^7.20.11" - "@babel/helpers" "^7.20.7" - "@babel/parser" "^7.20.7" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.12" - "@babel/types" "^7.20.7" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.2" - semver "^6.3.0" - -"@babel/generator@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.7.tgz#f8ef57c8242665c5929fe2e8d82ba75460187b4a" - integrity sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw== - dependencies: - "@babel/types" "^7.20.7" - "@jridgewell/gen-mapping" "^0.3.2" - jsesc "^2.5.1" - -"@babel/helper-compilation-targets@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz#a6cd33e93629f5eb473b021aac05df62c4cd09bb" - integrity sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ== - dependencies: - "@babel/compat-data" "^7.20.5" - "@babel/helper-validator-option" "^7.18.6" - browserslist "^4.21.3" - lru-cache "^5.1.1" - semver "^6.3.0" - -"@babel/helper-environment-visitor@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" - integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== - -"@babel/helper-function-name@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" - integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== - dependencies: - "@babel/template" "^7.18.10" - "@babel/types" "^7.19.0" - -"@babel/helper-hoist-variables@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" - integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-module-imports@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" - integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-module-transforms@^7.20.11": - version "7.20.11" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz#df4c7af713c557938c50ea3ad0117a7944b2f1b0" - integrity sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-simple-access" "^7.20.2" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/helper-validator-identifier" "^7.19.1" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.10" - "@babel/types" "^7.20.7" - -"@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.19.0": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" - integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== - -"@babel/helper-simple-access@^7.20.2": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" - integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== - dependencies: - "@babel/types" "^7.20.2" - -"@babel/helper-split-export-declaration@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" - integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-string-parser@^7.19.4": - version "7.19.4" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" - integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== - -"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" - integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== - -"@babel/helper-validator-option@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" - integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== - -"@babel/helpers@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.7.tgz#04502ff0feecc9f20ecfaad120a18f011a8e6dce" - integrity sha512-PBPjs5BppzsGaxHQCDKnZ6Gd9s6xl8bBCluz3vEInLGRJmnZan4F6BYCeqtyXqkk4W5IlPmjK4JlOuZkpJ3xZA== - dependencies: - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.7" - "@babel/types" "^7.20.7" - -"@babel/highlight@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" - integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== - dependencies: - "@babel/helper-validator-identifier" "^7.18.6" - chalk "^2.0.0" - js-tokens "^4.0.0" - -"@babel/parser@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.7.tgz#66fe23b3c8569220817d5feb8b9dcdc95bb4f71b" - integrity sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg== - -"@babel/plugin-transform-react-jsx-self@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.18.6.tgz#3849401bab7ae8ffa1e3e5687c94a753fc75bda7" - integrity sha512-A0LQGx4+4Jv7u/tWzoJF7alZwnBDQd6cGLh9P+Ttk4dpiL+J5p7NSNv/9tlEFFJDq3kjxOavWmbm6t0Gk+A3Ig== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-react-jsx-source@^7.19.6": - version "7.19.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.19.6.tgz#88578ae8331e5887e8ce28e4c9dc83fb29da0b86" - integrity sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ== - dependencies: - "@babel/helper-plugin-utils" "^7.19.0" - -"@babel/template@^7.18.10", "@babel/template@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" - integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== - dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/parser" "^7.20.7" - "@babel/types" "^7.20.7" - -"@babel/traverse@^7.20.10", "@babel/traverse@^7.20.12", "@babel/traverse@^7.20.7": - version "7.20.12" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.12.tgz#7f0f787b3a67ca4475adef1f56cb94f6abd4a4b5" - integrity sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ== - dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.7" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.20.7" - "@babel/types" "^7.20.7" - debug "^4.1.0" - globals "^11.1.0" - -"@babel/types@^7.18.6", "@babel/types@^7.19.0", "@babel/types@^7.20.2", "@babel/types@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.7.tgz#54ec75e252318423fc07fb644dc6a58a64c09b7f" - integrity sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg== - dependencies: - "@babel/helper-string-parser" "^7.19.4" - "@babel/helper-validator-identifier" "^7.19.1" - to-fast-properties "^2.0.0" - -"@esbuild/android-arm64@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.16.17.tgz#cf91e86df127aa3d141744edafcba0abdc577d23" - integrity sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg== - -"@esbuild/android-arm@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.16.17.tgz#025b6246d3f68b7bbaa97069144fb5fb70f2fff2" - integrity sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw== - -"@esbuild/android-x64@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.16.17.tgz#c820e0fef982f99a85c4b8bfdd582835f04cd96e" - integrity sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ== - -"@esbuild/darwin-arm64@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.16.17.tgz#edef4487af6b21afabba7be5132c26d22379b220" - integrity sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w== - -"@esbuild/darwin-x64@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.16.17.tgz#42829168730071c41ef0d028d8319eea0e2904b4" - integrity sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg== - -"@esbuild/freebsd-arm64@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.17.tgz#1f4af488bfc7e9ced04207034d398e793b570a27" - integrity sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw== - -"@esbuild/freebsd-x64@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.16.17.tgz#636306f19e9bc981e06aa1d777302dad8fddaf72" - integrity sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug== - -"@esbuild/linux-arm64@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.16.17.tgz#a003f7ff237c501e095d4f3a09e58fc7b25a4aca" - integrity sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g== - -"@esbuild/linux-arm@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.16.17.tgz#b591e6a59d9c4fe0eeadd4874b157ab78cf5f196" - integrity sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ== - -"@esbuild/linux-ia32@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.16.17.tgz#24333a11027ef46a18f57019450a5188918e2a54" - integrity sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg== - -"@esbuild/linux-loong64@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.16.17.tgz#d5ad459d41ed42bbd4d005256b31882ec52227d8" - integrity sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ== - -"@esbuild/linux-mips64el@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.16.17.tgz#4e5967a665c38360b0a8205594377d4dcf9c3726" - integrity sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw== - -"@esbuild/linux-ppc64@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.16.17.tgz#206443a02eb568f9fdf0b438fbd47d26e735afc8" - integrity sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g== - -"@esbuild/linux-riscv64@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.16.17.tgz#c351e433d009bf256e798ad048152c8d76da2fc9" - integrity sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw== - -"@esbuild/linux-s390x@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.16.17.tgz#661f271e5d59615b84b6801d1c2123ad13d9bd87" - integrity sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w== - -"@esbuild/linux-x64@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.16.17.tgz#e4ba18e8b149a89c982351443a377c723762b85f" - integrity sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw== - -"@esbuild/netbsd-x64@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.16.17.tgz#7d4f4041e30c5c07dd24ffa295c73f06038ec775" - integrity sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA== - -"@esbuild/openbsd-x64@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.16.17.tgz#970fa7f8470681f3e6b1db0cc421a4af8060ec35" - integrity sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg== - -"@esbuild/sunos-x64@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.16.17.tgz#abc60e7c4abf8b89fb7a4fe69a1484132238022c" - integrity sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw== - -"@esbuild/win32-arm64@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.16.17.tgz#7b0ff9e8c3265537a7a7b1fd9a24e7bd39fcd87a" - integrity sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw== - -"@esbuild/win32-ia32@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.16.17.tgz#e90fe5267d71a7b7567afdc403dfd198c292eb09" - integrity sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig== - -"@esbuild/win32-x64@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.16.17.tgz#c5a1a4bfe1b57f0c3e61b29883525c6da3e5c091" - integrity sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q== - -"@jridgewell/gen-mapping@^0.1.0": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" - integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== - dependencies: - "@jridgewell/set-array" "^1.0.0" - "@jridgewell/sourcemap-codec" "^1.4.10" - -"@jridgewell/gen-mapping@^0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" - integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== - dependencies: - "@jridgewell/set-array" "^1.0.1" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping" "^0.3.9" - -"@jridgewell/resolve-uri@^3.0.3": - version "3.0.7" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz#30cd49820a962aff48c8fffc5cd760151fca61fe" - integrity sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA== - -"@jridgewell/set-array@^1.0.0": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.1.tgz#36a6acc93987adcf0ba50c66908bd0b70de8afea" - integrity sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ== - -"@jridgewell/set-array@^1.0.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" - integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== - -"@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.13" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz#b6461fb0c2964356c469e115f504c95ad97ab88c" - integrity sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w== - -"@jridgewell/sourcemap-codec@^1.4.13": - version "1.4.14" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" - integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== - -"@jridgewell/trace-mapping@^0.3.9": - version "0.3.13" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz#dcfe3e95f224c8fe97a87a5235defec999aa92ea" - integrity sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w== - dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" - -"@playwright/test@^1.30.0": - version "1.30.0" - resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.30.0.tgz#8c0c4930ff2c7be7b3ec3fd434b2a3b4465ed7cb" - integrity sha512-SVxkQw1xvn/Wk/EvBnqWIq6NLo1AppwbYOjNLmyU0R1RoQ3rLEBtmjTnElcnz8VEtn11fptj1ECxK0tgURhajw== - dependencies: - "@types/node" "*" - playwright-core "1.30.0" - -"@remix-run/router@1.3.1": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.3.1.tgz#3bb0b6ddc0a276e8dc1138d08f63035e4e23e8bf" - integrity sha512-+eun1Wtf72RNRSqgU7qM2AMX/oHp+dnx7BHk1qhK5ZHzdHTUU4LA1mGG1vT+jMc8sbhG3orvsfOmryjzx2PzQw== - -"@types/node@*", "@types/node@^18.11.18": - version "18.11.18" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.18.tgz#8dfb97f0da23c2293e554c5a50d61ef134d7697f" - integrity sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA== - -"@types/prop-types@*": - version "15.7.3" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" - integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== - -"@types/react-dom@^18.0.10": - version "18.0.10" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.10.tgz#3b66dec56aa0f16a6cc26da9e9ca96c35c0b4352" - integrity sha512-E42GW/JA4Qv15wQdqJq8DL4JhNpB3prJgjgapN3qJT9K2zO5IIAQh4VXvCEDupoqAwnz0cY4RlXeC/ajX5SFHg== - dependencies: - "@types/react" "*" - -"@types/react@*", "@types/react@^18.0.27": - version "18.0.27" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.27.tgz#d9425abe187a00f8a5ec182b010d4fd9da703b71" - integrity sha512-3vtRKHgVxu3Jp9t718R9BuzoD4NcQ8YJ5XRzsSKxNDiDonD2MXIT1TmSkenxuCycZJoQT5d2vE8LwWJxBC1gmA== - dependencies: - "@types/prop-types" "*" - "@types/scheduler" "*" - csstype "^3.0.2" - -"@types/scheduler@*": - version "0.16.1" - resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.1.tgz#18845205e86ff0038517aab7a18a62a6b9f71275" - integrity sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA== - -"@vitejs/plugin-react@^3.0.1": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-3.0.1.tgz#ad21fb81377970dd4021a31cd95a03eb6f5c4c48" - integrity sha512-mx+QvYwIbbpOIJw+hypjnW1lAbKDHtWK5ibkF/V1/oMBu8HU/chb+SnqJDAsLq1+7rGqjktCEomMTM5KShzUKQ== - dependencies: - "@babel/core" "^7.20.7" - "@babel/plugin-transform-react-jsx-self" "^7.18.6" - "@babel/plugin-transform-react-jsx-source" "^7.19.6" - magic-string "^0.27.0" - react-refresh "^0.14.0" - -ansi-regex@^5.0.0, ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - -ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -brace-expansion@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" - integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== - dependencies: - balanced-match "^1.0.0" - -browserslist@^4.21.3: - version "4.21.4" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987" - integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw== - dependencies: - caniuse-lite "^1.0.30001400" - electron-to-chromium "^1.4.251" - node-releases "^2.0.6" - update-browserslist-db "^1.0.9" - -caniuse-lite@^1.0.30001400: - version "1.0.30001446" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001446.tgz#6d4ba828ab19f49f9bcd14a8430d30feebf1e0c5" - integrity sha512-fEoga4PrImGcwUUGEol/PoFCSBnSkA9drgdkxXkJLsUBOnJ8rs3zDv6ApqYXGQFOyMPsjh79naWhF4DAxbF8rw== - -chalk@^2.0.0: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chalk@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad" - integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -cliui@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" - integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.1" - wrap-ansi "^7.0.0" - -color-convert@^1.9.0: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -concurrently@^7.6.0: - version "7.6.0" - resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-7.6.0.tgz#531a6f5f30cf616f355a4afb8f8fcb2bba65a49a" - integrity sha512-BKtRgvcJGeZ4XttiDiNcFiRlxoAeZOseqUvyYRUp/Vtd+9p1ULmeoSqGsDA+2ivdeDFpqrJvGvmI+StKfKl5hw== - dependencies: - chalk "^4.1.0" - date-fns "^2.29.1" - lodash "^4.17.21" - rxjs "^7.0.0" - shell-quote "^1.7.3" - spawn-command "^0.0.2-1" - supports-color "^8.1.0" - tree-kill "^1.2.2" - yargs "^17.3.1" - -convert-source-map@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" - integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== - dependencies: - safe-buffer "~5.1.1" - -csstype@^3.0.2: - version "3.0.8" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.8.tgz#d2266a792729fb227cd216fb572f43728e1ad340" - integrity sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw== - -date-fns@^2.29.1: - version "2.29.3" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8" - integrity sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA== - -debug@^4.1.0: - version "4.3.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" - integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== - dependencies: - ms "2.1.2" - -dotenv@^16.0.3: - version "16.0.3" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07" - integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ== - -electron-to-chromium@^1.4.251: - version "1.4.284" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592" - integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA== - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -esbuild@^0.16.3: - version "0.16.17" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.16.17.tgz#fc2c3914c57ee750635fee71b89f615f25065259" - integrity sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg== - optionalDependencies: - "@esbuild/android-arm" "0.16.17" - "@esbuild/android-arm64" "0.16.17" - "@esbuild/android-x64" "0.16.17" - "@esbuild/darwin-arm64" "0.16.17" - "@esbuild/darwin-x64" "0.16.17" - "@esbuild/freebsd-arm64" "0.16.17" - "@esbuild/freebsd-x64" "0.16.17" - "@esbuild/linux-arm" "0.16.17" - "@esbuild/linux-arm64" "0.16.17" - "@esbuild/linux-ia32" "0.16.17" - "@esbuild/linux-loong64" "0.16.17" - "@esbuild/linux-mips64el" "0.16.17" - "@esbuild/linux-ppc64" "0.16.17" - "@esbuild/linux-riscv64" "0.16.17" - "@esbuild/linux-s390x" "0.16.17" - "@esbuild/linux-x64" "0.16.17" - "@esbuild/netbsd-x64" "0.16.17" - "@esbuild/openbsd-x64" "0.16.17" - "@esbuild/sunos-x64" "0.16.17" - "@esbuild/win32-arm64" "0.16.17" - "@esbuild/win32-ia32" "0.16.17" - "@esbuild/win32-x64" "0.16.17" - -escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== - -escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= - -fsevents@~2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== - -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== - -gensync@^1.0.0-beta.2: - version "1.0.0-beta.2" - resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" - integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== - -get-caller-file@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - -glob@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" - integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^5.0.1" - once "^1.3.0" - -globals@^11.1.0: - version "11.12.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" - integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== - dependencies: - function-bind "^1.1.1" - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -is-core-module@^2.9.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" - integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== - dependencies: - has "^1.0.3" - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -jsesc@^2.5.1: - version "2.5.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" - integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== - -json5@^2.2.2: - version "2.2.3" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" - integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== - -lodash@^4.17.21: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -loose-envify@^1.1.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" - integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== - dependencies: - js-tokens "^3.0.0 || ^4.0.0" - -lru-cache@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" - integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== - dependencies: - yallist "^3.0.2" - -magic-string@^0.27.0: - version "0.27.0" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.27.0.tgz#e4a3413b4bab6d98d2becffd48b4a257effdbbf3" - integrity sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA== - dependencies: - "@jridgewell/sourcemap-codec" "^1.4.13" - -minimatch@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" - integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== - dependencies: - brace-expansion "^2.0.1" - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -nanoid@^3.3.4: - version "3.3.4" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" - integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== - -node-releases@^2.0.6: - version "2.0.8" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.8.tgz#0f349cdc8fcfa39a92ac0be9bc48b7706292b9ae" - integrity sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A== - -once@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -path-parse@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" - integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== - -picocolors@^1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/picocolors/download/picocolors-1.0.0.tgz?cache=0&sync_timestamp=1634093339035&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fpicocolors%2Fdownload%2Fpicocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" - integrity sha1-y1vcdP8/UYkiNur3nWi8RFZKuBw= - -playwright-core@1.30.0: - version "1.30.0" - resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.30.0.tgz#de987cea2e86669e3b85732d230c277771873285" - integrity sha512-7AnRmTCf+GVYhHbLJsGUtskWTE33SwMZkybJ0v6rqR1boxq2x36U7p1vDRV7HO2IwTZgmycracLxPEJI49wu4g== - -postcss@^8.4.20: - version "8.4.21" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.21.tgz#c639b719a57efc3187b13a1d765675485f4134f4" - integrity sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg== - dependencies: - nanoid "^3.3.4" - picocolors "^1.0.0" - source-map-js "^1.0.2" - -react-dom@^18.2.0: - version "18.2.0" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" - integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== - dependencies: - loose-envify "^1.1.0" - scheduler "^0.23.0" - -react-refresh@^0.14.0: - version "0.14.0" - resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e" - integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ== - -react-router-dom@^6.8.0: - version "6.8.0" - resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.8.0.tgz#5e5f4c4b15fdec3965d2ad9d7460d0c61971e744" - integrity sha512-hQouduSTywGJndE86CXJ2h7YEy4HYC6C/uh19etM+79FfQ6cFFFHnHyDlzO4Pq0eBUI96E4qVE5yUjA00yJZGQ== - dependencies: - "@remix-run/router" "1.3.1" - react-router "6.8.0" - -react-router@6.8.0: - version "6.8.0" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.8.0.tgz#dd61fd1ec44daa2cceaef8e6baa00f99a01a650f" - integrity sha512-760bk7y3QwabduExtudhWbd88IBbuD1YfwzpuDUAlJUJ7laIIcqhMvdhSVh1Fur1PE8cGl84L0dxhR3/gvHF7A== - dependencies: - "@remix-run/router" "1.3.1" - -react@^18.2.0: - version "18.2.0" - resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" - integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== - dependencies: - loose-envify "^1.1.0" - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= - -resolve@^1.22.1: - version "1.22.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" - integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== - dependencies: - is-core-module "^2.9.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -rollup@^3.7.0: - version "3.10.0" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.10.0.tgz#6eb19196d8b3b375ca651cb78261faac48e24cd6" - integrity sha512-JmRYz44NjC1MjVF2VKxc0M1a97vn+cDxeqWmnwyAF4FvpjK8YFdHpaqvQB+3IxCvX05vJxKZkoMDU8TShhmJVA== - optionalDependencies: - fsevents "~2.3.2" - -rxjs@^7.0.0: - version "7.8.0" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.0.tgz#90a938862a82888ff4c7359811a595e14e1e09a4" - integrity sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg== - dependencies: - tslib "^2.1.0" - -safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -scheduler@^0.23.0: - version "0.23.0" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" - integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== - dependencies: - loose-envify "^1.1.0" - -semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -shell-quote@^1.7.3: - version "1.7.4" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.4.tgz#33fe15dee71ab2a81fcbd3a52106c5cfb9fb75d8" - integrity sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw== - -source-map-js@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" - integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== - -spawn-command@^0.0.2-1: - version "0.0.2-1" - resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0" - integrity sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A= - -string-width@^4.1.0, string-width@^4.2.0: - version "4.2.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" - integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.0" - -string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - 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" - -strip-ansi@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" - integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== - dependencies: - ansi-regex "^5.0.0" - -strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -supports-color@^8.1.0: - version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== - dependencies: - has-flag "^4.0.0" - -supports-preserve-symlinks-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" - integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== - -to-fast-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" - integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= - -tree-kill@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" - integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== - -tslib@^2.1.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" - integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== - -typescript@^4.9.4: - version "4.9.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.4.tgz#a2a3d2756c079abda241d75f149df9d561091e78" - integrity sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg== - -update-browserslist-db@^1.0.9: - version "1.0.10" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" - integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== - dependencies: - escalade "^3.1.1" - picocolors "^1.0.0" - -vite@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/vite/-/vite-4.0.4.tgz#4612ce0b47bbb233a887a54a4ae0c6e240a0da31" - integrity sha512-xevPU7M8FU0i/80DMR+YhgrzR5KS2ORy1B4xcX/cXLsvnUWvfHuqMmVU6N0YiJ4JWGRJJsLCgjEzKjG9/GKoSw== - dependencies: - esbuild "^0.16.3" - postcss "^8.4.20" - resolve "^1.22.1" - rollup "^3.7.0" - optionalDependencies: - fsevents "~2.3.2" - -web-vitals@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-3.1.1.tgz#bb124a03df7a135617f495c5bb7dbc30ecf2cce3" - integrity sha512-qvllU+ZeQChqzBhZ1oyXmWsjJ8a2jHYpH8AMaVuf29yscOPZfTQTjQFRX6+eADTdsDE8IanOZ0cetweHMs8/2A== - -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - -y18n@^5.0.5: - version "5.0.8" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" - integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== - -yallist@^3.0.2: - version "3.1.1" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" - integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== - -yargs-parser@^21.1.1: - version "21.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" - integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== - -yargs@^17.3.1: - version "17.6.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.6.2.tgz#2e23f2944e976339a1ee00f18c77fedee8332541" - integrity sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw== - dependencies: - cliui "^8.0.1" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.3" - y18n "^5.0.5" - yargs-parser "^21.1.1"