From bd6288c71c9e7923c4d850b259454134430369f1 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:28:48 +0200 Subject: [PATCH 01/51] Register endpoint, URLs #288 #48gister Endpoint and true URLs --- lib/Cargo.toml | 1 + lib/src/collections.rs | 2 +- lib/src/commit.rs | 2 +- lib/src/db.rs | 27 +++++++------ lib/src/db/query_index.rs | 2 +- lib/src/endpoints.rs | 1 + lib/src/plugins/mod.rs | 1 + lib/src/plugins/register.rs | 73 +++++++++++++++++++++++++++++++++++ lib/src/populate.rs | 68 +++++++++++++++++++++++++------- lib/src/resources.rs | 8 +--- lib/src/store.rs | 14 +++++-- lib/src/storelike.rs | 53 +++++++++++++++++-------- lib/src/urls.rs | 4 +- server/src/appstate.rs | 4 +- server/src/bin.rs | 2 +- server/src/commit_monitor.rs | 5 ++- server/src/handlers/commit.rs | 8 +--- server/src/helpers.rs | 1 + 18 files changed, 207 insertions(+), 69 deletions(-) create mode 100644 lib/src/plugins/register.rs diff --git a/lib/Cargo.toml b/lib/Cargo.toml index ac777b038..7e88e6588 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -23,6 +23,7 @@ html2md = { version = "0.2.14", optional = true } kuchikiki = { version = "0.8.2", optional = true } lol_html = { version = "1", optional = true } rand = { version = "0.8" } +lazy_static = "1" regex = "1" ring = "0.17.6" rio_api = { version = "0.8", optional = true } diff --git a/lib/src/collections.rs b/lib/src/collections.rs index fa52713c0..a8d585a14 100644 --- a/lib/src/collections.rs +++ b/lib/src/collections.rs @@ -416,7 +416,7 @@ pub fn create_collection_resource_for_class( // Let the Collections collection be the top level item let parent = if class.subject == urls::COLLECTION { - drive + drive.to_string() } else { format!("{}/collections", drive) }; diff --git a/lib/src/commit.rs b/lib/src/commit.rs index 9470bdf0d..18b6df298 100644 --- a/lib/src/commit.rs +++ b/lib/src/commit.rs @@ -240,7 +240,7 @@ impl Commit { let default_parent = store.get_self_url().ok_or("There is no self_url set, and no parent in the Commit. The commit can not be applied.")?; resource_old.set( urls::PARENT.into(), - Value::AtomicUrl(default_parent), + Value::AtomicUrl(default_parent.to_string()), store, )?; } diff --git a/lib/src/db.rs b/lib/src/db.rs index f64fec2a8..c9fc1254c 100644 --- a/lib/src/db.rs +++ b/lib/src/db.rs @@ -18,6 +18,7 @@ use std::{ use tracing::{info, instrument}; use trees::{Method, Operation, Transaction, Tree}; +use url::Url; use crate::{ agents::ForAgent, @@ -79,7 +80,7 @@ pub struct Db { /// [Tree::WatchedQueries] watched_queries: sled::Tree, /// The address where the db will be hosted, e.g. http://localhost/ - server_url: String, + server_url: Url, /// Endpoints are checked whenever a resource is requested. They calculate (some properties of) the resource and return it. endpoints: Vec, /// Function called whenever a Commit is applied. @@ -109,7 +110,7 @@ impl Db { reference_index, query_index, prop_val_sub_index, - server_url, + server_url: Url::parse(&server_url)?, watched_queries, endpoints: default_endpoints(), on_commit: None, @@ -268,6 +269,7 @@ impl Db { } fn map_sled_item_to_resource( + &self, item: Result<(sled::IVec, sled::IVec), sled::Error>, self_url: String, include_external: bool, @@ -275,7 +277,7 @@ impl Db { let (subject, resource_bin) = item.expect(DB_CORRUPT_MSG); let subject: String = String::from_utf8_lossy(&subject).to_string(); - if !include_external && !subject.starts_with(&self_url) { + if !include_external && self.is_external_subject(&subject).ok()? { return None; } @@ -398,7 +400,7 @@ impl Db { for (i, atom_res) in atoms.enumerate() { let atom = atom_res?; - if !q.include_external && !atom.subject.starts_with(&self_url) { + if !q.include_external && self.is_external_subject(&atom.subject).unwrap() { continue; } @@ -660,14 +662,14 @@ impl Storelike for Db { Ok(commit_response) } - fn get_server_url(&self) -> &str { + fn get_server_url(&self) -> &Url { &self.server_url } - // Since the DB is often also the server, this should make sense. - // Some edge cases might appear later on (e.g. a slave DB that only stores copies?) - fn get_self_url(&self) -> Option { - Some(self.get_server_url().into()) + fn get_self_url(&self) -> Option<&Url> { + // Since the DB is often also the server, this should make sense. + // Some edge cases might appear later on (e.g. a slave DB that only stores copies?) + Some(self.get_server_url()) } fn get_default_agent(&self) -> AtomicResult { @@ -686,7 +688,7 @@ impl Storelike for Db { let resource = crate::resources::Resource::from_propvals(propvals, subject.into()); Ok(resource) } - Err(e) => self.handle_not_found(subject, e, None), + Err(e) => self.handle_not_found(subject, e), } } @@ -836,7 +838,7 @@ impl Storelike for Db { .expect("No self URL set, is required in DB"); let result = self.resources.into_iter().filter_map(move |item| { - Db::map_sled_item_to_resource(item, self_url.clone(), include_external) + Db::map_sled_item_to_resource(self, item, self_url.to_string(), include_external) }); Box::new(result) @@ -889,7 +891,8 @@ impl Storelike for Db { } fn populate(&self) -> AtomicResult<()> { - crate::populate::populate_all(self) + crate::populate::populate_all(self)?; + Ok(()) } #[instrument(skip(self))] diff --git a/lib/src/db/query_index.rs b/lib/src/db/query_index.rs index 325b0da0c..0066b0b64 100644 --- a/lib/src/db/query_index.rs +++ b/lib/src/db/query_index.rs @@ -117,7 +117,7 @@ pub fn query_sorted_indexed( let (_q_filter, _val, subject) = parse_collection_members_key(&k)?; // If no external resources should be included, skip this one if it's an external resource - if !q.include_external && !subject.starts_with(&self_url) { + if !q.include_external && store.is_external_subject(subject)? { continue; } diff --git a/lib/src/endpoints.rs b/lib/src/endpoints.rs index c3699fd60..fd81ba9e7 100644 --- a/lib/src/endpoints.rs +++ b/lib/src/endpoints.rs @@ -83,6 +83,7 @@ pub fn default_endpoints() -> Vec { plugins::files::upload_endpoint(), plugins::files::download_endpoint(), plugins::export::export_endpoint(), + plugins::register::register_endpoint(), #[cfg(feature = "html")] plugins::bookmark::bookmark_endpoint(), plugins::importer::import_endpoint(), diff --git a/lib/src/plugins/mod.rs b/lib/src/plugins/mod.rs index 9f383bf51..02f79c5b4 100644 --- a/lib/src/plugins/mod.rs +++ b/lib/src/plugins/mod.rs @@ -46,5 +46,6 @@ pub mod files; pub mod path; pub mod prunetests; pub mod query; +pub mod register; pub mod search; pub mod versioning; diff --git a/lib/src/plugins/register.rs b/lib/src/plugins/register.rs new file mode 100644 index 000000000..b097cb045 --- /dev/null +++ b/lib/src/plugins/register.rs @@ -0,0 +1,73 @@ +//! Creates a new Drive and optionally also an Agent. + +use crate::{agents::Agent, endpoints::Endpoint, errors::AtomicResult, urls, Resource, Storelike}; + +pub fn register_endpoint() -> Endpoint { + Endpoint { + path: "/register".to_string(), + params: [ + urls::INVITE_PUBKEY.to_string(), + urls::NAME.to_string(), + ].into(), + description: "Allows new users to easily, in one request, make both an Agent and a Drive. This drive will be created at the subdomain of `name`.".to_string(), + shortname: "register".to_string(), + handle: Some(construct_register_redirect), + handle_post: None, + } +} + +#[tracing::instrument(skip(store))] +pub fn construct_register_redirect( + url: url::Url, + store: &impl Storelike, + for_agent: Option<&str>, +) -> AtomicResult { + let requested_subject = url.to_string(); + let mut pub_key = None; + let mut name_option = None; + for (k, v) in url.query_pairs() { + match k.as_ref() { + "public-key" | urls::INVITE_PUBKEY => pub_key = Some(v.to_string()), + "name" | urls::NAME => name_option = Some(v.to_string()), + _ => {} + } + } + if pub_key.is_none() && name_option.is_none() { + return register_endpoint().to_resource(store); + } + + let name = if let Some(n) = name_option { + n + } else { + return Err("No name provided".into()); + }; + + let mut new_agent = None; + + let drive_creator_agent: String = if let Some(key) = pub_key { + let new = Agent::new_from_public_key(store, &key)?.subject; + new_agent = Some(new.clone()); + new + } else if let Some(agent) = for_agent { + agent.to_string() + } else { + return Err("No `public-key` provided".into()); + }; + + // Create the new Drive + let drive = crate::populate::create_drive(store, Some(&name), &drive_creator_agent, false)?; + + // Construct the Redirect Resource, which might provide the Client with a Subject for his Agent. + let mut redirect = Resource::new_instance(urls::REDIRECT, store)?; + redirect.set_string(urls::DESTINATION.into(), drive.get_subject(), store)?; + if let Some(agent) = new_agent { + redirect.set( + urls::REDIRECT_AGENT.into(), + crate::Value::AtomicUrl(agent), + store, + )?; + } + // The front-end requires the @id to be the same as requested + redirect.set_subject(requested_subject); + Ok(redirect) +} diff --git a/lib/src/populate.rs b/lib/src/populate.rs index 829850830..9f4551dea 100644 --- a/lib/src/populate.rs +++ b/lib/src/populate.rs @@ -9,7 +9,7 @@ use crate::{ parse::ParseOpts, schema::{Class, Property}, storelike::Query, - urls, Storelike, Value, + urls, Resource, Storelike, Value, }; const DEFAULT_ONTOLOGY_PATH: &str = "defaultOntology"; @@ -153,26 +153,52 @@ pub fn populate_base_models(store: &impl Storelike) -> AtomicResult<()> { Ok(()) } -/// Creates a Drive resource at the base URL. Does not set rights. Use set_drive_rights for that. -pub fn create_drive(store: &impl Storelike) -> AtomicResult<()> { - let self_url = store - .get_self_url() - .ok_or("No self_url set, cannot populate store with Drive")?; - let mut drive = store.get_resource_new(&self_url); +/// Creates a Drive resource at the base URL if no name is passed. +#[tracing::instrument(skip(store), level = "info")] +pub fn create_drive( + store: &impl Storelike, + drive_name: Option<&str>, + for_agent: &str, + public_read: bool, +) -> AtomicResult { + let mut self_url = if let Some(url) = store.get_self_url() { + url.to_owned() + } else { + return Err("No self URL set. Cannot create drive.".into()); + }; + let drive_subject: String = if let Some(name) = drive_name { + // Let's make a subdomain + let host = self_url.host().expect("No host in server_url"); + let subdomain_host = format!("{}.{}", name, host); + self_url.set_host(Some(&subdomain_host))?; + self_url.to_string() + } else { + self_url.to_string() + }; + + let mut drive = if drive_name.is_some() { + if store.get_resource(&drive_subject).is_ok() { + return Err("Drive URL is already taken".into()); + } + Resource::new(drive_subject) + } else { + // Only for the base URL (of no drive name is passed), we should not check if the drive exists. + // This is because we use `create_drive` in the `--initialize` command. + store.get_resource_new(&drive_subject) + }; drive.set_class(urls::DRIVE); - let server_url = url::Url::parse(store.get_server_url())?; drive.set_string( urls::NAME.into(), - server_url.host_str().ok_or("Can't use current base URL")?, + drive_name.unwrap_or_else(|| self_url.host_str().unwrap()), store, )?; drive.save_locally(store)?; - Ok(()) + Ok(drive) } pub fn create_default_ontology(store: &impl Storelike) -> AtomicResult<()> { - let mut drive = store.get_resource(store.get_server_url())?; + let mut drive = store.get_resource(store.get_server_url().as_str())?; let ontology_subject = format!("{}/{}", drive.get_subject(), DEFAULT_ONTOLOGY_PATH); @@ -209,7 +235,7 @@ pub fn create_default_ontology(store: &impl Storelike) -> AtomicResult<()> { /// Adds rights to the default agent to the Drive resource (at the base URL). Optionally give Public Read rights. pub fn set_drive_rights(store: &impl Storelike, public_read: bool) -> AtomicResult<()> { // Now let's add the agent as the Root user and provide write access - let mut drive = store.get_resource(store.get_server_url())?; + let mut drive = store.get_resource(store.get_server_url().as_str())?; let write_agent = store.get_default_agent()?.subject; let read_agent = write_agent.clone(); @@ -236,7 +262,9 @@ To use the data in your web apps checkout our client libraries: [@tomic/lib](htt Use [@tomic/cli](https://docs.atomicdata.dev/js-cli) to generate typed ontologies inside your code. "#, store.get_server_url(), &format!("{}/{}", drive.get_subject(), DEFAULT_ONTOLOGY_PATH)), store)?; } + drive.save_locally(store)?; + Ok(()) } @@ -312,7 +340,11 @@ pub fn populate_importer(store: &crate::Db) -> AtomicResult<()> { .ok_or("No self URL in this Store - required for populating importer")?; let mut importer = crate::Resource::new(urls::construct_path_import(&base)); importer.set_class(urls::IMPORTER); - importer.set(urls::PARENT.into(), Value::AtomicUrl(base), store)?; + importer.set( + urls::PARENT.into(), + Value::AtomicUrl(base.to_string()), + store, + )?; importer.set(urls::NAME.into(), Value::String("Import".into()), store)?; importer.save_locally(store)?; Ok(()) @@ -323,7 +355,7 @@ pub fn populate_importer(store: &crate::Db) -> AtomicResult<()> { /// Useful for helping a new user get started. pub fn populate_sidebar_items(store: &crate::Db) -> AtomicResult<()> { let base = store.get_self_url().ok_or("No self_url")?; - let mut drive = store.get_resource(&base)?; + let mut drive = store.get_resource(base.as_str())?; let arr = vec![ format!("{}/setup", base), format!("{}/import", base), @@ -342,7 +374,13 @@ pub fn populate_all(store: &crate::Db) -> AtomicResult<()> { // populate_base_models should be run in init, instead of here, since it will result in infinite loops without populate_default_store(store) .map_err(|e| format!("Failed to populate default store. {}", e))?; - create_drive(store).map_err(|e| format!("Failed to create drive. {}", e))?; + create_drive( + store, + None, + &store.get_default_agent().unwrap().subject, + true, + ) + .map_err(|e| format!("Failed to create drive. {}", e))?; create_default_ontology(store) .map_err(|e| format!("Failed to create default ontology. {}", e))?; set_drive_rights(store, true)?; diff --git a/lib/src/resources.rs b/lib/src/resources.rs index ee1001fa1..3a711eed3 100644 --- a/lib/src/resources.rs +++ b/lib/src/resources.rs @@ -347,13 +347,7 @@ impl Resource { let commit_builder = self.get_commit_builder().clone(); let commit = commit_builder.sign(&agent, store, self)?; // If the current client is a server, and the subject is hosted here, don't post - let should_post = if let Some(self_url) = store.get_self_url() { - !self.subject.starts_with(&self_url) - } else { - // Current client is not a server, has no own persisted store - true - }; - if should_post { + if store.is_external_subject(&commit.subject)? { crate::client::post_commit(&commit, store)?; } let opts = CommitOpts { diff --git a/lib/src/store.rs b/lib/src/store.rs index 8aeb82913..7402615cb 100644 --- a/lib/src/store.rs +++ b/lib/src/store.rs @@ -1,6 +1,8 @@ //! In-memory store of Atomic data. //! This provides many methods for finding, changing, serializing and parsing Atomic Data. +use url::Url; + use crate::agents::Agent; use crate::storelike::QueryResult; use crate::Value; @@ -17,6 +19,10 @@ pub struct Store { default_agent: Arc>>, } +lazy_static::lazy_static! { + static ref LOCAL_STORE_URL: Url = Url::parse("local:store").unwrap(); +} + impl Store { /// Creates an empty Store. /// Run `.populate()` to get useful standard models loaded into your store. @@ -158,14 +164,14 @@ impl Storelike for Store { Box::new(self.hashmap.lock().unwrap().clone().into_values()) } - fn get_server_url(&self) -> &str { + fn get_server_url(&self) -> &Url { // TODO Should be implemented later when companion functionality is here // https://github.com/atomicdata-dev/atomic-server/issues/6 - "local:store" + &LOCAL_STORE_URL } - fn get_self_url(&self) -> Option { - Some(self.get_server_url().into()) + fn get_self_url(&self) -> Option<&Url> { + Some(self.get_server_url()) } fn get_default_agent(&self) -> AtomicResult { diff --git a/lib/src/storelike.rs b/lib/src/storelike.rs index 54705bfca..633569a3d 100644 --- a/lib/src/storelike.rs +++ b/lib/src/storelike.rs @@ -1,5 +1,7 @@ //! The Storelike Trait contains many useful methods for maniupulting / retrieving data. +use url::Url; + use crate::{ agents::{Agent, ForAgent}, commit::CommitResponse, @@ -98,12 +100,12 @@ pub trait Storelike: Sized { /// E.g. `https://example.com` /// This is where deltas should be sent to. /// Also useful for Subject URL generation. - fn get_server_url(&self) -> &str; + fn get_server_url(&self) -> &Url; /// Returns the root URL where this instance of the store is hosted. /// Should return `None` if this is simply a client and not a server. /// E.g. `https://example.com` - fn get_self_url(&self) -> Option { + fn get_self_url(&self) -> Option<&Url> { None } @@ -213,21 +215,16 @@ pub trait Storelike: Sized { /// Implement this if you want to have custom handlers for Commits. fn handle_commit(&self, _commit_response: &CommitResponse) {} - fn handle_not_found( - &self, - subject: &str, - _error: AtomicError, - for_agent: Option<&Agent>, - ) -> AtomicResult { - if let Some(self_url) = self.get_self_url() { - if subject.starts_with(&self_url) { - return Err(AtomicError::not_found(format!( - "Failed to retrieve locally: '{}'", - subject - ))); - } + fn handle_not_found(&self, subject: &str, error: AtomicError) -> AtomicResult { + // This does not work for subdomains + if self.is_external_subject(subject)? { + self.fetch_resource(subject, None) + } else { + Err(AtomicError::not_found(format!( + "Failed to retrieve locally: '{}'. {}", + subject, error + ))) } - self.fetch_resource(subject, for_agent) } /// Imports a JSON-AD string, returns the amount of imported resources. @@ -237,6 +234,30 @@ pub trait Storelike: Sized { Ok(len) } + /// Checks if the URL of some resource is owned by some external store. + /// If true, then the Subject points to a different server. + /// If you're using `Storelike` on something that does not persist (e.g. a client app), + /// the answer should always be `true`. + fn is_external_subject(&self, subject: &str) -> AtomicResult { + if let Some(self_url) = self.get_self_url() { + if subject.starts_with(&self_url.as_str()) { + return Ok(false); + } else { + let subject_url = url::Url::parse(subject)?; + let subject_host = subject_url.host().ok_or_else(|| { + AtomicError::not_found(format!("Subject URL has no host: {}", subject)) + })?; + let self_host = self_url.host().ok_or_else(|| { + AtomicError::not_found(format!("Self URL has no host: {}", self_url)) + })?; + if subject_host == self_host { + return Ok(false); + } + } + } + Ok(true) + } + /// Removes a resource from the store. Errors if not present. fn remove_resource(&self, subject: &str) -> AtomicResult<()>; diff --git a/lib/src/urls.rs b/lib/src/urls.rs index 7ba27457f..c9122cadf 100644 --- a/lib/src/urls.rs +++ b/lib/src/urls.rs @@ -1,5 +1,7 @@ //! Contains some of the most important Atomic Data URLs. +use url::Url; + // Classes pub const CLASS: &str = "https://atomicdata.dev/classes/Class"; pub const PROPERTY: &str = "https://atomicdata.dev/classes/Property"; @@ -154,7 +156,7 @@ pub const PUBLIC_AGENT: &str = "https://atomicdata.dev/agents/publicAgent"; pub const SUDO_AGENT: &str = "sudo:agent"; // Paths -pub fn construct_path_import(base: &str) -> String { +pub fn construct_path_import(base: &Url) -> String { format!("{base}{PATH_IMPORT}") } diff --git a/server/src/appstate.rs b/server/src/appstate.rs index fc6ca11ab..eb6ee86f2 100644 --- a/server/src/appstate.rs +++ b/server/src/appstate.rs @@ -197,12 +197,12 @@ fn set_up_initial_invite(store: &impl Storelike) -> AtomicServerResult<()> { )?; invite.set( atomic_lib::urls::TARGET.into(), - atomic_lib::Value::AtomicUrl(store.get_server_url().into()), + atomic_lib::Value::AtomicUrl(store.get_server_url().to_string()), store, )?; invite.set( atomic_lib::urls::PARENT.into(), - atomic_lib::Value::AtomicUrl(store.get_server_url().into()), + atomic_lib::Value::AtomicUrl(store.get_server_url().to_string()), store, )?; invite.set( diff --git a/server/src/bin.rs b/server/src/bin.rs index e965c919b..867e26262 100644 --- a/server/src/bin.rs +++ b/server/src/bin.rs @@ -68,7 +68,7 @@ async fn main_wrapped() -> errors::AtomicServerResult<()> { let importer_subject = if let Some(i) = &import_opts.parent { i.into() } else { - urls::construct_path_import(&appstate.store.get_self_url().expect("No self url")) + urls::construct_path_import(appstate.store.get_self_url().expect("No self url")) }; let parse_opts = atomic_lib::parse::ParseOpts { importer: Some(importer_subject), diff --git a/server/src/commit_monitor.rs b/server/src/commit_monitor.rs index 4e18a61ca..27bf45569 100644 --- a/server/src/commit_monitor.rs +++ b/server/src/commit_monitor.rs @@ -54,8 +54,9 @@ impl Handler for CommitMonitor { fields(to = %msg.subject, agent = %msg.agent) )] fn handle(&mut self, msg: Subscribe, _ctx: &mut Context) { - // check if the agent has the rights to subscribe to this resource - if !msg.subject.starts_with(&self.store.get_self_url().unwrap()) { + // check if the agent has the rights to subscribe to this resourceif let Some(self_url) = self.get_self_url() { + + if self.store.is_external_subject(&msg.subject).unwrap_or(true) { tracing::warn!("can't subscribe to external resource"); return; } diff --git a/server/src/handlers/commit.rs b/server/src/handlers/commit.rs index 43a7e2797..79f53bbd3 100644 --- a/server/src/handlers/commit.rs +++ b/server/src/handlers/commit.rs @@ -19,12 +19,8 @@ pub async fn post_commit( let mut builder = HttpResponse::Ok(); let incoming_commit_resource = parse_json_ad_commit_resource(&body, store)?; let incoming_commit = Commit::from_resource(incoming_commit_resource)?; - if !incoming_commit.subject.contains( - &store - .get_self_url() - .ok_or("Cannot apply commits to this store. No self_url is set.")?, - ) { - return Err("Subject of commit should be sent to other domain - this store can not own this resource.".into()); + if store.is_external_subject(&incoming_commit.subject)? { + return Err("Subject of commit is external, and should be sent to its origin domain. This store can not own this resource. See https://github.com/atomicdata-dev/atomic-data-rust/issues/509".into()); } let opts = CommitOpts { validate_schema: true, diff --git a/server/src/helpers.rs b/server/src/helpers.rs index 4fc776de6..e2476328a 100644 --- a/server/src/helpers.rs +++ b/server/src/helpers.rs @@ -164,6 +164,7 @@ pub fn get_auth( /// Checks for authentication headers and returns Some agent's subject if everything is well. /// Skips these checks in public_mode and returns Ok(None). +/// Returns the Agent's subject or the Public Agent. #[tracing::instrument(skip(appstate))] pub fn get_client_agent( headers: &HeaderMap, From 430e136e99c775f2692c1ce1a5380cad7a77a088 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:29:57 +0200 Subject: [PATCH 02/51] Fix some tests --- lib/src/store.rs | 5 ++++- lib/src/storelike.rs | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/src/store.rs b/lib/src/store.rs index 7402615cb..92eecdc19 100644 --- a/lib/src/store.rs +++ b/lib/src/store.rs @@ -19,8 +19,11 @@ pub struct Store { default_agent: Arc>>, } +/// The URL used for stores that are not accessible on the web. +pub const LOCAL_STORE_URL_STR: &str = "local:store"; + lazy_static::lazy_static! { - static ref LOCAL_STORE_URL: Url = Url::parse("local:store").unwrap(); + static ref LOCAL_STORE_URL: Url = Url::parse(LOCAL_STORE_URL_STR).unwrap(); } impl Store { diff --git a/lib/src/storelike.rs b/lib/src/storelike.rs index 633569a3d..11574874f 100644 --- a/lib/src/storelike.rs +++ b/lib/src/storelike.rs @@ -8,6 +8,7 @@ use crate::{ errors::AtomicError, hierarchy, schema::{Class, Property}, + store::LOCAL_STORE_URL_STR, urls, }; use crate::{errors::AtomicResult, parse::parse_json_ad_string}; @@ -240,6 +241,9 @@ pub trait Storelike: Sized { /// the answer should always be `true`. fn is_external_subject(&self, subject: &str) -> AtomicResult { if let Some(self_url) = self.get_self_url() { + if self_url.as_str() == LOCAL_STORE_URL_STR { + return Ok(true); + } if subject.starts_with(&self_url.as_str()) { return Ok(false); } else { From 40e88c0cf9a2fac15235e88c0ee93743d0c2ef16 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:29:57 +0200 Subject: [PATCH 03/51] WIP Path URL --- .cargo/config.toml | 32 ++++ Cargo.lock | 1 + cli/src/main.rs | 3 +- lib/src/agents.rs | 18 ++- lib/src/atomic_url.rs | 195 +++++++++++++++++++++++++ lib/src/collections.rs | 8 +- lib/src/commit.rs | 21 ++- lib/src/db.rs | 27 ++-- lib/src/db/test.rs | 11 +- lib/src/lib.rs | 2 + lib/src/parse.rs | 2 +- lib/src/populate.rs | 21 +-- lib/src/store.rs | 14 +- lib/src/storelike.rs | 26 ++-- lib/src/urls.rs | 12 +- lib/src/values.rs | 12 +- server/Cargo.toml | 1 + server/src/appstate.rs | 5 +- server/src/bin.rs | 7 +- server/src/errors.rs | 20 ++- server/src/handlers/search.rs | 17 ++- server/src/handlers/single_page_app.rs | 1 + server/src/tests.rs | 19 ++- 23 files changed, 385 insertions(+), 90 deletions(-) create mode 100644 .cargo/config.toml create mode 100644 lib/src/atomic_url.rs diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 000000000..10dda65b3 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,32 @@ +[build] +rustc-wrapper = '/Users/joep/.cargo/bin/sccache' +[target.x86_64-unknown-linux-gnu] +rustflags = [ + '-Clink-arg=-fuse-ld=lld', + '-Zshare-generics=y', +] +linker = '/usr/bin/clang' + +[target.x86_64-pc-windows-msvc] +rustflags = ['-Zshare-generics=y'] +linker = 'rust-lld.exe' + +[target.x86_64-apple-darwin] +rustflags = [ + '-C', + 'link-arg=-fuse-ld=/usr/local/bin/zld', + '-Zshare-generics=y', + '-Csplit-debuginfo=unpacked', +] +[profile.dev] +opt-level = 0 +debug = 2 +incremental = true +codegen-units = 512 + +[profile.release] +opt-level = 3 +debug = 0 +incremental = false +codegen-units = 256 +split-debuginfo = '...' diff --git a/Cargo.lock b/Cargo.lock index 1ede40ce3..40df6eed8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -617,6 +617,7 @@ dependencies = [ "tracing-opentelemetry", "tracing-subscriber", "ureq", + "url", "urlencoding", "walkdir", "webp", diff --git a/cli/src/main.rs b/cli/src/main.rs index 756432f5c..f10ad2acb 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,4 +1,5 @@ use atomic_lib::serialize::Format; +use atomic_lib::atomic_url::Routes; use atomic_lib::{agents::generate_public_key, mapping::Mapping}; use atomic_lib::{agents::Agent, config::Config}; use atomic_lib::{errors::AtomicResult, Storelike}; @@ -165,7 +166,7 @@ fn set_agent_config() -> CLIResult { "No config found at {:?}. Let's create one!", &agent_config_path ); - let server = promptly::prompt("What's the base url of your Atomic Server?")?; + let server: String = promptly::prompt("What's the base url of your Atomic Server?")?; let agent = promptly::prompt("What's the URL of your Agent?")?; let private_key = promptly::prompt("What's the private key of this Agent?")?; let config = atomic_lib::config::Config { diff --git a/lib/src/agents.rs b/lib/src/agents.rs index fcf5facae..e1dafdd01 100644 --- a/lib/src/agents.rs +++ b/lib/src/agents.rs @@ -85,7 +85,7 @@ impl Agent { pub fn new(name: Option<&str>, store: &impl Storelike) -> AtomicResult { let keypair = generate_keypair()?; - Ok(Agent::new_from_private_key(name, store, &keypair.private)) + Agent::new_from_private_key(name, store, &keypair.private) } /// Creates a new Agent on this server, using the server's Server URL. @@ -94,16 +94,22 @@ impl Agent { name: Option<&str>, store: &impl Storelike, private_key: &str, - ) -> Agent { + ) -> AtomicResult { let keypair = generate_public_key(private_key); + println!("server url: {}", store.get_server_url()); + let subject = store + .get_server_url() + .url() + .join(&format!("agents/{}", &keypair.public))? + .to_string(); - Agent { + Ok(Agent { private_key: Some(keypair.private), - public_key: keypair.public.clone(), - subject: format!("{}/agents/{}", store.get_server_url(), keypair.public), + public_key: keypair.public, + subject, name: name.map(|x| x.to_owned()), created_at: crate::utils::now(), - } + }) } /// Creates a new Agent on this server, using the server's Server URL. diff --git a/lib/src/atomic_url.rs b/lib/src/atomic_url.rs new file mode 100644 index 000000000..babdc71a4 --- /dev/null +++ b/lib/src/atomic_url.rs @@ -0,0 +1,195 @@ +use serde::{Deserialize, Serialize, Serializer}; +use url::Url; + +use crate::{errors::AtomicResult, utils::random_string}; + +pub enum Routes { + Agents, + AllVersions, + Collections, + Commits, + CommitsUnsigned, + Endpoints, + Import, + Tpf, + Version, + Setup, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +/// Wrapper for URLs / subjects. +/// Has a bunch of methods for finding or creating commonly used paths. +pub struct AtomicUrl { + url: Url, +} + +impl AtomicUrl { + pub fn new(url: Url) -> Self { + Self { url } + } + + pub fn as_str(&self) -> &str { + self.url.as_str() + } + + /// Returns the route to some common Endpoint + pub fn set_route(&self, route: Routes) -> Self { + let path = match route { + Routes::AllVersions => "/all-versions".to_string(), + Routes::Agents => "/agents".to_string(), + Routes::Collections => "/collections".to_string(), + Routes::Commits => "/commits".to_string(), + Routes::CommitsUnsigned => "/commits-unsigned".to_string(), + Routes::Endpoints => "/endpoints".to_string(), + Routes::Import => "/import".to_string(), + Routes::Tpf => "/tpf".to_string(), + Routes::Version => "/version".to_string(), + Routes::Setup => "/setup".to_string(), + }; + let mut new = self.url.clone(); + new.set_path(&path); + Self::new(new) + } + + /// Returns a new URL generated from the provided path_shortname and a random string. + /// ``` + /// let url = atomic_lib::AtomicUrl::try_from("https://example.com").unwrap(); + /// let generated = url.generate_random("my-type"); + /// assert!(generated.to_string().starts_with("https://example.com/my-type/")); + /// ``` + pub fn generate_random(&self, path_shortname: &str) -> Self { + let mut url = self.url.clone(); + let path = format!("{path_shortname}/{}", random_string(10)); + url.set_path(&path); + Self { url } + } + + /// Adds a sub-path to a URL. + /// Adds a slash to the existing URL, followed by the passed path. + /// + /// ``` + /// use atomic_lib::AtomicUrl; + /// let start = "http://localhost"; + /// let mut url = AtomicUrl::try_from(start).unwrap(); + /// assert_eq!(url.to_string(), "http://localhost/"); + /// url.append("/"); + /// assert_eq!(url.to_string(), "http://localhost/"); + /// url.append("someUrl/123"); + /// assert_eq!(url.to_string(), "http://localhost/someUrl/123"); + /// url.append("/345"); + /// assert_eq!(url.to_string(), "http://localhost/someUrl/123/345"); + /// ``` + pub fn append(&mut self, path: &str) -> &Self { + let mut new_path = self.url.path().to_string(); + match (new_path.ends_with('/'), path.starts_with('/')) { + (true, true) => { + new_path.pop(); + } + (false, false) => new_path.push('/'), + _other => {} + }; + + // Remove first slash if it exists + if new_path.starts_with('/') { + new_path.remove(0); + } + + new_path.push_str(path); + + self.url.set_path(&new_path); + self + } + + /// Sets the subdomain of the URL. + /// Removes an existing subdomain if the subdomain is None + pub fn set_subdomain(&mut self, subdomain: Option<&str>) -> AtomicResult<&Self> { + let mut host = self.url.host_str().unwrap().to_string(); + if let Some(subdomain) = subdomain { + host = format!("{}.{}", subdomain, host); + } + self.url.set_host(Some(host.as_str()))?; + Ok(self) + } + + pub fn set_path(&mut self, path: &str) -> &Self { + self.url.set_path(path); + self + } + + pub fn subdomain(&self) -> Option { + let url = self.url.clone(); + let host = url.host_str().unwrap(); + let parts: Vec<&str> = host.split('.').collect(); + if parts.len() > 2 { + Some(parts[0].to_string()) + } else { + None + } + } + + /// Returns the inner {url::Url} struct that has a bunch of regular URL methods + /// Useful if you need the host or something. + pub fn url(&self) -> Url { + self.url.clone() + } +} + +impl TryFrom<&str> for AtomicUrl { + type Error = url::ParseError; + + fn try_from(value: &str) -> Result { + let url = Url::parse(value)?; + Ok(Self { url }) + } +} + +impl Serialize for AtomicUrl { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(self.url.as_str()) + } +} + +impl<'de> Deserialize<'de> for AtomicUrl { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + let url = Url::parse(&s).map_err(serde::de::Error::custom)?; + Ok(Self { url }) + } +} + +impl std::fmt::Display for AtomicUrl { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.url) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_url() { + let _should_fail = AtomicUrl::try_from("nonsense").unwrap_err(); + let _should_succeed = AtomicUrl::try_from("http://localhost/someUrl").unwrap(); + } + + #[test] + fn subdomain() { + let sub = "http://test.example.com"; + assert_eq!( + AtomicUrl::try_from(sub).unwrap().subdomain(), + Some("test".to_string()) + ); + let mut no_sub = AtomicUrl::try_from("http://example.com").unwrap(); + assert_eq!(no_sub.subdomain(), None); + + let to_sub = no_sub.set_subdomain(Some("mysub")).unwrap(); + assert_eq!(to_sub.subdomain(), Some("mysub".to_string())); + } +} diff --git a/lib/src/collections.rs b/lib/src/collections.rs index a8d585a14..0e767748a 100644 --- a/lib/src/collections.rs +++ b/lib/src/collections.rs @@ -88,7 +88,7 @@ impl CollectionBuilder { store: &impl Storelike, ) -> CollectionBuilder { CollectionBuilder { - subject: format!("{}/{}", store.get_server_url(), path), + subject: store.get_server_url().clone().set_path(path).to_string(), property: Some(urls::IS_A.into()), value: Some(class_url.into()), sort_by: None, @@ -418,7 +418,9 @@ pub fn create_collection_resource_for_class( let parent = if class.subject == urls::COLLECTION { drive.to_string() } else { - format!("{}/collections", drive) + drive + .set_route(crate::atomic_url::Routes::Collections) + .to_string() }; collection_resource.set_string(urls::PARENT.into(), &parent, store)?; @@ -524,7 +526,7 @@ mod test { println!("{:?}", subjects); let collections_collection = store .get_resource_extended( - &format!("{}/collections", store.get_server_url()), + &format!("{}collections", store.get_server_url()), false, &ForAgent::Public, ) diff --git a/lib/src/commit.rs b/lib/src/commit.rs index 18b6df298..f8a84e7e2 100644 --- a/lib/src/commit.rs +++ b/lib/src/commit.rs @@ -429,12 +429,21 @@ impl Commit { #[tracing::instrument(skip(store))] pub fn into_resource(&self, store: &impl Storelike) -> AtomicResult { let commit_subject = match self.signature.as_ref() { - Some(sig) => format!("{}/commits/{}", store.get_server_url(), sig), + Some(sig) => store + .get_server_url() + .set_route(Routes::Commits) + .append(sig) + .to_string(), None => { let now = crate::utils::now(); - format!("{}/commitsUnsigned/{}", store.get_server_url(), now) + store + .get_server_url() + .set_route(Routes::CommitsUnsigned) + .append(&now.to_string()) + .to_string() } }; + println!("commit subject: {}", commit_subject); let mut resource = Resource::new_instance(urls::COMMIT, store)?; resource.set_subject(commit_subject); resource.set_unsafe( @@ -742,10 +751,10 @@ mod test { let private_key = "CapMWIhFUT+w7ANv9oCPqrHrwZpkP2JhzF9JnyT6WcI="; let store = crate::Store::init().unwrap(); store.populate().unwrap(); - let agent = Agent::new_from_private_key(None, &store, private_key); + let agent = Agent::new_from_private_key(None, &store, private_key).unwrap(); assert_eq!( &agent.subject, - "local:store/agents/7LsjMW5gOfDdJzK/atgjQ1t20J/rw8MjVg6xwqm+h8U=" + "http://noresolve.localhost/agents/7LsjMW5gOfDdJzK/atgjQ1t20J/rw8MjVg6xwqm+h8U=" ); store.add_resource(&agent.to_resource().unwrap()).unwrap(); let subject = "https://localhost/new_thing"; @@ -760,8 +769,8 @@ mod test { let signature = commit.signature.clone().unwrap(); let serialized = commit.serialize_deterministically_json_ad(&store).unwrap(); - assert_eq!(serialized, "{\"https://atomicdata.dev/properties/createdAt\":0,\"https://atomicdata.dev/properties/isA\":[\"https://atomicdata.dev/classes/Commit\"],\"https://atomicdata.dev/properties/set\":{\"https://atomicdata.dev/properties/description\":\"Some value\",\"https://atomicdata.dev/properties/shortname\":\"someval\"},\"https://atomicdata.dev/properties/signer\":\"local:store/agents/7LsjMW5gOfDdJzK/atgjQ1t20J/rw8MjVg6xwqm+h8U=\",\"https://atomicdata.dev/properties/subject\":\"https://localhost/new_thing\"}"); - assert_eq!(signature, "JOGRyp1NCulc0RNuuNozgIagQPRoZy0Y5+mbSpHY2DKiN3vqUNYLjXbAPYT6Cga6vSG9zztEIa/ZcbQPo7wgBg=="); + assert_eq!(serialized, "{\"https://atomicdata.dev/properties/createdAt\":0,\"https://atomicdata.dev/properties/isA\":[\"https://atomicdata.dev/classes/Commit\"],\"https://atomicdata.dev/properties/set\":{\"https://atomicdata.dev/properties/description\":\"Some value\",\"https://atomicdata.dev/properties/shortname\":\"someval\"},\"https://atomicdata.dev/properties/signer\":\"http://noresolve.localhost/agents/7LsjMW5gOfDdJzK/atgjQ1t20J/rw8MjVg6xwqm+h8U=\",\"https://atomicdata.dev/properties/subject\":\"https://localhost/new_thing\"}"); + assert_eq!(signature, "CZbjUJW/tokEKSZTCFjEHWbWqGW+jyhZWYs82K9wt0SArxu9xGg+D3IniAlygQp0F3KcI4Z876th3/X3fJIVAQ=="); } #[test] diff --git a/lib/src/db.rs b/lib/src/db.rs index c9fc1254c..f9e7bf4a7 100644 --- a/lib/src/db.rs +++ b/lib/src/db.rs @@ -22,6 +22,7 @@ use url::Url; use crate::{ agents::ForAgent, + atomic_url::AtomicUrl, atoms::IndexAtom, commit::{CommitOpts, CommitResponse}, db::{ @@ -80,7 +81,7 @@ pub struct Db { /// [Tree::WatchedQueries] watched_queries: sled::Tree, /// The address where the db will be hosted, e.g. http://localhost/ - server_url: Url, + server_url: AtomicUrl, /// Endpoints are checked whenever a resource is requested. They calculate (some properties of) the resource and return it. endpoints: Vec, /// Function called whenever a Commit is applied. @@ -93,7 +94,7 @@ impl Db { /// Creates a new store at the specified path, or opens the store if it already exists. /// The server_url is the domain where the db will be hosted, e.g. http://localhost/ /// It is used for distinguishing locally defined items from externally defined ones. - pub fn init(path: &std::path::Path, server_url: String) -> AtomicResult { + pub fn init(path: &std::path::Path, server_url: &str) -> AtomicResult { tracing::info!("Opening database at {:?}", path); let db = sled::open(path).map_err(|e|format!("Failed opening DB at this location: {:?} . Is another instance of Atomic Server running? {}", path, e))?; @@ -110,7 +111,7 @@ impl Db { reference_index, query_index, prop_val_sub_index, - server_url: Url::parse(&server_url)?, + server_url: AtomicUrl::try_from(server_url)?, watched_queries, endpoints: default_endpoints(), on_commit: None, @@ -126,10 +127,7 @@ impl Db { pub fn init_temp(id: &str) -> AtomicResult { let tmp_dir_path = format!(".temp/db/{}", id); let _try_remove_existing = std::fs::remove_dir_all(&tmp_dir_path); - let store = Db::init( - std::path::Path::new(&tmp_dir_path), - "https://localhost".into(), - )?; + let store = Db::init(std::path::Path::new(&tmp_dir_path), "https://localhost")?; let agent = store.create_agent(None)?; store.set_default_agent(agent); store.populate()?; @@ -243,7 +241,7 @@ impl Db { Ok(propval) } None => Err(AtomicError::not_found(format!( - "Resource {} not found", + "Resource {} does not exist", subject ))), } @@ -530,6 +528,7 @@ impl Storelike for Db { update_index: bool, overwrite_existing: bool, ) -> AtomicResult<()> { + println!("add_resource_opts {}", resource.get_subject()); // This only works if no external functions rely on using add_resource for atom-like operations! // However, add_atom uses set_propvals, which skips the validation. let existing = self.get_propvals(resource.get_subject()).ok(); @@ -662,11 +661,11 @@ impl Storelike for Db { Ok(commit_response) } - fn get_server_url(&self) -> &Url { + fn get_server_url(&self) -> &AtomicUrl { &self.server_url } - fn get_self_url(&self) -> Option<&Url> { + fn get_self_url(&self) -> Option<&AtomicUrl> { // Since the DB is often also the server, this should make sense. // Some edge cases might appear later on (e.g. a slave DB that only stores copies?) Some(self.get_server_url()) @@ -702,17 +701,13 @@ impl Storelike for Db { let url_span = tracing::span!(tracing::Level::TRACE, "URL parse").entered(); // This might add a trailing slash let url = url::Url::parse(subject)?; - let mut removed_query_params = { + + let removed_query_params = { let mut url_altered = url.clone(); url_altered.set_query(None); url_altered.to_string() }; - // Remove trailing slash - if removed_query_params.ends_with('/') { - removed_query_params.pop(); - } - url_span.exit(); let endpoint_span = tracing::span!(tracing::Level::TRACE, "Endpoint").entered(); diff --git a/lib/src/db/test.rs b/lib/src/db/test.rs index f3ec91cd3..02dcfe0ac 100644 --- a/lib/src/db/test.rs +++ b/lib/src/db/test.rs @@ -65,7 +65,7 @@ fn populate_collections() { .map(|r| r.get_subject().into()) .collect(); println!("{:?}", subjects); - let collections_collection_url = format!("{}/collections", store.get_server_url()); + let collections_collection_url = format!("{}collections", store.get_server_url()); let collections_resource = store .get_resource_extended(&collections_collection_url, false, &ForAgent::Public) .unwrap(); @@ -90,8 +90,8 @@ fn populate_collections() { /// Also counts commits. fn destroy_resource_and_check_collection_and_commits() { let store = Db::init_temp("counter").unwrap(); + let agents_url = store.get_server_url().set_route(Routes::Agents).to_string(); let for_agent = &ForAgent::Public; - let agents_url = format!("{}/agents", store.get_server_url()); let agents_collection_1 = store .get_resource_extended(&agents_url, false, for_agent) .unwrap(); @@ -110,7 +110,10 @@ fn destroy_resource_and_check_collection_and_commits() { ); // We will count the commits, and check if they've incremented later on. - let commits_url = format!("{}/commits", store.get_server_url()); + let commits_url = store + .get_server_url() + .set_route(Routes::Commits) + .to_string(); let commits_collection_1 = store .get_resource_extended(&commits_url, false, for_agent) .unwrap(); @@ -197,7 +200,7 @@ fn destroy_resource_and_check_collection_and_commits() { fn get_extended_resource_pagination() { let store = Db::init_temp("get_extended_resource_pagination").unwrap(); let subject = format!( - "{}/commits?current_page=2&page_size=99999", + "{}commits?current_page=2&page_size=99999", store.get_server_url() ); let for_agent = &ForAgent::Public; diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 4238fcb8e..f3ab9a290 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -59,6 +59,7 @@ assert!(fetched_new_resource.get_shortname("description", &store).unwrap().to_st */ pub mod agents; +pub mod atomic_url; pub mod atoms; pub mod authentication; pub mod client; @@ -90,6 +91,7 @@ pub mod utils; pub mod validate; pub mod values; +pub use atomic_url::AtomicUrl; pub use atoms::Atom; pub use commit::Commit; #[cfg(feature = "db")] diff --git a/lib/src/parse.rs b/lib/src/parse.rs index b746504e6..0d5497c28 100644 --- a/lib/src/parse.rs +++ b/lib/src/parse.rs @@ -221,7 +221,7 @@ pub fn parse_json_ad_commit_resource( .get(urls::SUBJECT) .ok_or("No subject field in Commit.")? .to_string(); - let subject = format!("{}/commits/{}", store.get_server_url(), signature); + let subject = format!("{}commits/{}", store.get_server_url(), signature); let mut resource = Resource::new(subject); let propvals = match parse_json_ad_map_to_resource(json, store, &ParseOpts::default())? { SubResource::Resource(r) => r.into_propvals(), diff --git a/lib/src/populate.rs b/lib/src/populate.rs index 9f4551dea..b34dd824c 100644 --- a/lib/src/populate.rs +++ b/lib/src/populate.rs @@ -161,17 +161,18 @@ pub fn create_drive( for_agent: &str, public_read: bool, ) -> AtomicResult { - let mut self_url = if let Some(url) = store.get_self_url() { + let self_url = if let Some(url) = store.get_self_url() { url.to_owned() } else { return Err("No self URL set. Cannot create drive.".into()); }; let drive_subject: String = if let Some(name) = drive_name { // Let's make a subdomain - let host = self_url.host().expect("No host in server_url"); + let mut url = self_url.url(); + let host = url.host().expect("No host in server_url"); let subdomain_host = format!("{}.{}", name, host); - self_url.set_host(Some(&subdomain_host))?; - self_url.to_string() + url.set_host(Some(&subdomain_host))?; + url.to_string() } else { self_url.to_string() }; @@ -317,13 +318,15 @@ pub fn populate_collections(store: &impl Storelike) -> AtomicResult<()> { /// Adds default Endpoints (versioning) to the Db. /// Makes sure they are fetchable pub fn populate_endpoints(store: &crate::Db) -> AtomicResult<()> { + use crate::atomic_url::Routes; + let endpoints = crate::endpoints::default_endpoints(); - let endpoints_collection = format!("{}/endpoints", store.get_server_url()); + let endpoints_collection = store.get_server_url().set_route(Routes::Endpoints); for endpoint in endpoints { let mut resource = endpoint.to_resource(store)?; resource.set( urls::PARENT.into(), - Value::AtomicUrl(endpoints_collection.clone()), + Value::AtomicUrl(endpoints_collection.to_string()), store, )?; resource.save_locally(store)?; @@ -357,9 +360,9 @@ pub fn populate_sidebar_items(store: &crate::Db) -> AtomicResult<()> { let base = store.get_self_url().ok_or("No self_url")?; let mut drive = store.get_resource(base.as_str())?; let arr = vec![ - format!("{}/setup", base), - format!("{}/import", base), - format!("{}/collections", base), + base.set_route(crate::atomic_url::Routes::Setup), + base.set_route(crate::atomic_url::Routes::Import), + base.set_route(crate::atomic_url::Routes::Collections), ]; for item in arr { drive.push(urls::SUBRESOURCES, item.into(), true)?; diff --git a/lib/src/store.rs b/lib/src/store.rs index 92eecdc19..94ad95849 100644 --- a/lib/src/store.rs +++ b/lib/src/store.rs @@ -7,6 +7,11 @@ use crate::agents::Agent; use crate::storelike::QueryResult; use crate::Value; use crate::{atoms::Atom, storelike::Storelike}; +use crate::{ + atomic_url::AtomicUrl, + atoms::Atom, + storelike::{ResourceCollection, Storelike}, +}; use crate::{errors::AtomicResult, Resource}; use std::{collections::HashMap, sync::Arc, sync::Mutex}; @@ -20,10 +25,11 @@ pub struct Store { } /// The URL used for stores that are not accessible on the web. -pub const LOCAL_STORE_URL_STR: &str = "local:store"; +// I'd prefer this to a non-HTTP URI, but that causes parsing issues when we combine it with some paths (at least with Commits) +pub const LOCAL_STORE_URL_STR: &str = "http://noresolve.localhost"; lazy_static::lazy_static! { - static ref LOCAL_STORE_URL: Url = Url::parse(LOCAL_STORE_URL_STR).unwrap(); + static ref LOCAL_STORE_URL: AtomicUrl = AtomicUrl::try_from(LOCAL_STORE_URL_STR).unwrap(); } impl Store { @@ -167,13 +173,13 @@ impl Storelike for Store { Box::new(self.hashmap.lock().unwrap().clone().into_values()) } - fn get_server_url(&self) -> &Url { + fn get_server_url(&self) -> &AtomicUrl { // TODO Should be implemented later when companion functionality is here // https://github.com/atomicdata-dev/atomic-server/issues/6 &LOCAL_STORE_URL } - fn get_self_url(&self) -> Option<&Url> { + fn get_self_url(&self) -> Option<&AtomicUrl> { Some(self.get_server_url()) } diff --git a/lib/src/storelike.rs b/lib/src/storelike.rs index 11574874f..8c8e7a131 100644 --- a/lib/src/storelike.rs +++ b/lib/src/storelike.rs @@ -1,9 +1,8 @@ //! The Storelike Trait contains many useful methods for maniupulting / retrieving data. -use url::Url; - use crate::{ agents::{Agent, ForAgent}, + atomic_url::AtomicUrl, commit::CommitResponse, errors::AtomicError, hierarchy, @@ -98,15 +97,15 @@ pub trait Storelike: Sized { } /// Returns the base URL where the default store is. - /// E.g. `https://example.com` + /// E.g. `https://example.com/` /// This is where deltas should be sent to. /// Also useful for Subject URL generation. - fn get_server_url(&self) -> &Url; + fn get_server_url(&self) -> &AtomicUrl; /// Returns the root URL where this instance of the store is hosted. /// Should return `None` if this is simply a client and not a server. - /// E.g. `https://example.com` - fn get_self_url(&self) -> Option<&Url> { + /// E.g. `https://example.com.` + fn get_self_url(&self) -> Option<&AtomicUrl> { None } @@ -195,11 +194,11 @@ pub trait Storelike: Sized { Property::from_resource(prop) } - /// Get's the resource, parses the Query parameters and calculates dynamic properties. + /// Gets the resource, parses the Query parameters and calculates dynamic properties. /// Defaults to get_resource if store doesn't support extended resources /// If `for_agent` is None, no authorization checks will be done, and all resources will return. - /// If you want public only resurces, pass `Some(crate::authentication::public_agent)` as the agent. - /// - *skip_dynamic* Does not calculte dynamic properties. Adds an `incomplete=true` property if the resource should have been dynamic. + /// If you want public only resources, pass `Some(crate::authentication::public_agent)` as the agent. + /// - *skip_dynamic* Does not calculate dynamic properties. Adds an `incomplete=true` property if the resource should have been dynamic. fn get_resource_extended( &self, subject: &str, @@ -216,14 +215,14 @@ pub trait Storelike: Sized { /// Implement this if you want to have custom handlers for Commits. fn handle_commit(&self, _commit_response: &CommitResponse) {} - fn handle_not_found(&self, subject: &str, error: AtomicError) -> AtomicResult { + fn handle_not_found(&self, subject: &str, _error: AtomicError) -> AtomicResult { // This does not work for subdomains if self.is_external_subject(subject)? { self.fetch_resource(subject, None) } else { Err(AtomicError::not_found(format!( - "Failed to retrieve locally: '{}'. {}", - subject, error + "Subject is not stored on this server: '{}'", + subject ))) } } @@ -244,13 +243,14 @@ pub trait Storelike: Sized { if self_url.as_str() == LOCAL_STORE_URL_STR { return Ok(true); } - if subject.starts_with(&self_url.as_str()) { + if subject.starts_with(self_url.as_str()) { return Ok(false); } else { let subject_url = url::Url::parse(subject)?; let subject_host = subject_url.host().ok_or_else(|| { AtomicError::not_found(format!("Subject URL has no host: {}", subject)) })?; + let self_url = self_url.url(); let self_host = self_url.host().ok_or_else(|| { AtomicError::not_found(format!("Self URL has no host: {}", self_url)) })?; diff --git a/lib/src/urls.rs b/lib/src/urls.rs index c9122cadf..7519cdbb9 100644 --- a/lib/src/urls.rs +++ b/lib/src/urls.rs @@ -1,6 +1,5 @@ //! Contains some of the most important Atomic Data URLs. - -use url::Url; +//! See [crate::atomic_url] for the URL datatype. // Classes pub const CLASS: &str = "https://atomicdata.dev/classes/Class"; @@ -156,9 +155,12 @@ pub const PUBLIC_AGENT: &str = "https://atomicdata.dev/agents/publicAgent"; pub const SUDO_AGENT: &str = "sudo:agent"; // Paths -pub fn construct_path_import(base: &Url) -> String { - format!("{base}{PATH_IMPORT}") -} +pub const PATH_IMPORT: &str = "import"; +pub const PATH_FETCH_BOOKMARK: &str = "fetch-bookmark"; +pub const PATH_TPF: &str = "tpf"; +pub const PATH_PATH: &str = "path"; +pub const PATH_COMMITS: &str = "commits"; +pub const PATH_ENDPOINTS: &str = "endpoints"; pub const PATH_IMPORT: &str = "/import"; pub const PATH_FETCH_BOOKMARK: &str = "/fetch-bookmark"; diff --git a/lib/src/values.rs b/lib/src/values.rs index b6c75ace9..33603f1a2 100644 --- a/lib/src/values.rs +++ b/lib/src/values.rs @@ -2,7 +2,7 @@ use crate::{ datatype::match_datatype, datatype::DataType, errors::AtomicResult, resources::PropVals, - utils::check_valid_url, Resource, + utils::check_valid_url, AtomicUrl, Resource, }; use regex::Regex; use serde::{Deserialize, Serialize}; @@ -353,6 +353,16 @@ impl From> for Value { } } +impl From> for Value { + fn from(val: Vec) -> Self { + let mut vec = Vec::new(); + for i in val { + vec.push(SubResource::Subject(i.to_string())); + } + Value::ResourceArray(vec) + } +} + use std::fmt; impl fmt::Display for Value { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/server/Cargo.toml b/server/Cargo.toml index bebe64467..5ee35433e 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -55,6 +55,7 @@ urlencoding = "2" image = "0.25.2" webp = "0.3" ravif = "0.11.8" +url = "2.3.1" [dependencies.instant-acme] optional = true diff --git a/server/src/appstate.rs b/server/src/appstate.rs index eb6ee86f2..60ecb4f34 100644 --- a/server/src/appstate.rs +++ b/server/src/appstate.rs @@ -4,6 +4,7 @@ use crate::{ }; use atomic_lib::{ agents::{generate_public_key, Agent}, + atomic_url::Routes, commit::CommitResponse, Storelike, }; @@ -137,7 +138,7 @@ fn set_default_agent(config: &Config, store: &impl Storelike) -> AtomicServerRes "server".into(), store, &agent_config.private_key, - ); + )?; store.add_resource(&recreated_agent.to_resource()?)?; agent_config } else { @@ -179,7 +180,7 @@ fn set_default_agent(config: &Config, store: &impl Storelike) -> AtomicServerRes /// Creates the first Invitation that is opened by the user on the Home page. fn set_up_initial_invite(store: &impl Storelike) -> AtomicServerResult<()> { - let subject = format!("{}/setup", store.get_server_url()); + let subject = store.get_server_url().set_route(Routes::Setup).to_string(); tracing::info!("Creating initial Invite at {}", subject); let mut invite = store.get_resource_new(&subject); invite.set_class(atomic_lib::urls::INVITE); diff --git a/server/src/bin.rs b/server/src/bin.rs index 867e26262..dd48aa97a 100644 --- a/server/src/bin.rs +++ b/server/src/bin.rs @@ -68,7 +68,12 @@ async fn main_wrapped() -> errors::AtomicServerResult<()> { let importer_subject = if let Some(i) = &import_opts.parent { i.into() } else { - urls::construct_path_import(appstate.store.get_self_url().expect("No self url")) + appstate + .store + .get_self_url() + .expect("No self URL") + .set_route(Routes::Import) + .to_string() }; let parse_opts = atomic_lib::parse::ParseOpts { importer: Some(importer_subject), diff --git a/server/src/errors.rs b/server/src/errors.rs index 6cf565216..b0e144c47 100644 --- a/server/src/errors.rs +++ b/server/src/errors.rs @@ -14,7 +14,7 @@ pub enum AppErrorType { Other, } -// More strict error type, supports HTTP responses +/// Error type that includes a Resource representation of the Error, which can be sent to the client. pub struct AtomicServerError { pub message: String, pub error_type: AppErrorType, @@ -49,8 +49,8 @@ impl ResponseError for AtomicServerError { } fn error_response(&self) -> HttpResponse { // Creates a JSON-AD resource representing the Error. - let r = match &self.error_resource { - Some(r) => r.to_owned(), + let r: Resource = match &self.error_resource { + Some(r) => *r.clone(), None => { let mut r = Resource::new("subject".into()); r.set_class(urls::ERROR); @@ -58,12 +58,12 @@ impl ResponseError for AtomicServerError { urls::DESCRIPTION.into(), Value::String(self.message.clone()), ); - Box::new(r) + r } }; let body = r.to_json_ad().unwrap(); - tracing::info!("Error response: {}", self.message); + // tracing::info!("Error response: {}", self.message); HttpResponse::build(self.status_code()) .content_type(JSON_AD_MIME) .body(body) @@ -179,3 +179,13 @@ impl From for AtomicServerError { } } } + +impl From for AtomicServerError { + fn from(error: url::ParseError) -> Self { + AtomicServerError { + message: error.to_string(), + error_type: AppErrorType::Other, + error_resource: None, + } + } +} diff --git a/server/src/handlers/search.rs b/server/src/handlers/search.rs index b314f7182..2ab6ee672 100644 --- a/server/src/handlers/search.rs +++ b/server/src/handlers/search.rs @@ -83,11 +83,18 @@ pub async fn search_query( // Create a valid atomic data resource. // You'd think there would be a simpler way of getting the requested URL... - let subject = format!( - "{}{}", - store.get_self_url().ok_or("No base URL set")?, - req.uri().path_and_query().ok_or("Add a query param")? - ); + // See https://github.com/actix/actix-web/issues/2895 + let subject: String = store + .get_self_url() + .ok_or("No base URL set")? + .url() + .join( + req.uri() + .path_and_query() + .ok_or("Add a query param")? + .as_str(), + )? + .to_string(); let mut results_resource = atomic_lib::plugins::search::search_endpoint().to_resource(store)?; results_resource.set_subject(subject.clone()); diff --git a/server/src/handlers/single_page_app.rs b/server/src/handlers/single_page_app.rs index 6140a2c3b..c5bb89a37 100644 --- a/server/src/handlers/single_page_app.rs +++ b/server/src/handlers/single_page_app.rs @@ -35,6 +35,7 @@ pub async fn single_page( "Cache-Control", "no-store, no-cache, must-revalidate, private", )) + .append_header(("Vary", "Accept")) .body(body); Ok(resp) diff --git a/server/src/tests.rs b/server/src/tests.rs index ccd9d8424..eab3cb2f8 100644 --- a/server/src/tests.rs +++ b/server/src/tests.rs @@ -91,8 +91,10 @@ async fn server_tests() { let resp = test::call_service(&app, req).await; assert!(resp.status().is_client_error()); - // Edit the main drive, make it hidden to the public agent - let mut drive = store.get_resource(&appstate.config.server_url).unwrap(); + // Edit the properties collection, make it hidden to the public agent + let mut drive = store + .get_resource(appstate.store.get_server_url().as_str()) + .unwrap(); drive .set( urls::READ.into(), @@ -104,7 +106,7 @@ async fn server_tests() { // Should 401 (Unauthorized) let req = - test::TestRequest::with_uri("/properties").insert_header(("Accept", "application/ad+json")); + test::TestRequest::with_uri("properties").insert_header(("Accept", "application/ad+json")); let resp = test::call_service(&app, req.to_request()).await; assert_eq!( resp.status().as_u16(), @@ -113,17 +115,18 @@ async fn server_tests() { ); // Get JSON-AD - let req = build_request_authenticated("/properties", &appstate); + let req = build_request_authenticated("properties", &appstate); let resp = test::call_service(&app, req.to_request()).await; - assert!(resp.status().is_success(), "setup not returning JSON-AD"); let body = get_body(resp); + println!("DEBUG: {:?}", body); + // assert!(resp.status().is_success(), "setup not returning JSON-AD"); assert!( body.as_str().contains("{\n \"@id\""), "response should be json-ad" ); // Get JSON-LD - let req = build_request_authenticated("/properties", &appstate) + let req = build_request_authenticated("properties", &appstate) .insert_header(("Accept", "application/ld+json")); let resp = test::call_service(&app, req.to_request()).await; assert!(resp.status().is_success(), "setup not returning JSON-LD"); @@ -134,7 +137,7 @@ async fn server_tests() { ); // Get turtle - let req = build_request_authenticated("/properties", &appstate) + let req = build_request_authenticated("properties", &appstate) .insert_header(("Accept", "text/turtle")); let resp = test::call_service(&app, req.to_request()).await; assert!(resp.status().is_success()); @@ -146,7 +149,7 @@ async fn server_tests() { // Get Search // Does not test the contents of the results - the index isn't built at this point - let req = build_request_authenticated("/search?q=setup", &appstate); + let req = build_request_authenticated("search?q=setup", &appstate); let resp = test::call_service(&app, req.to_request()).await; assert!(resp.status().is_success()); let body = get_body(resp); From 01085e5ce63804acbd562ccb138d8e4dcdce2b94 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:29:57 +0200 Subject: [PATCH 04/51] #518 Fix cargo in CI --- .cargo/config.toml | 32 -------------------------------- 1 file changed, 32 deletions(-) delete mode 100644 .cargo/config.toml diff --git a/.cargo/config.toml b/.cargo/config.toml deleted file mode 100644 index 10dda65b3..000000000 --- a/.cargo/config.toml +++ /dev/null @@ -1,32 +0,0 @@ -[build] -rustc-wrapper = '/Users/joep/.cargo/bin/sccache' -[target.x86_64-unknown-linux-gnu] -rustflags = [ - '-Clink-arg=-fuse-ld=lld', - '-Zshare-generics=y', -] -linker = '/usr/bin/clang' - -[target.x86_64-pc-windows-msvc] -rustflags = ['-Zshare-generics=y'] -linker = 'rust-lld.exe' - -[target.x86_64-apple-darwin] -rustflags = [ - '-C', - 'link-arg=-fuse-ld=/usr/local/bin/zld', - '-Zshare-generics=y', - '-Csplit-debuginfo=unpacked', -] -[profile.dev] -opt-level = 0 -debug = 2 -incremental = true -codegen-units = 512 - -[profile.release] -opt-level = 3 -debug = 0 -incremental = false -codegen-units = 256 -split-debuginfo = '...' From 530a81af57f51424b6c7a505984fef0e892df628 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:29:58 +0200 Subject: [PATCH 05/51] Fix tests --- CHANGELOG.md | 1 + lib/src/agents.rs | 1 - lib/src/authentication.rs | 2 +- lib/src/commit.rs | 1 - lib/src/db.rs | 1 - server/src/tests.rs | 16 +++++++++------- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 55defc6a0..4c8b0b811 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -114,6 +114,7 @@ See [STATUS.md](server/STATUS.md) to learn more about which features will remain - `Store::all_resources` returns `Iterator` instead of `Vec` #522 #487 - Change authentication order #525 - Fix cookie subject check #525 +- Refactored Subject URLs to use `AtomicUrl` ## [v0.33.1] - 2022-09-25 diff --git a/lib/src/agents.rs b/lib/src/agents.rs index e1dafdd01..e2d7c0954 100644 --- a/lib/src/agents.rs +++ b/lib/src/agents.rs @@ -96,7 +96,6 @@ impl Agent { private_key: &str, ) -> AtomicResult { let keypair = generate_public_key(private_key); - println!("server url: {}", store.get_server_url()); let subject = store .get_server_url() .url() diff --git a/lib/src/authentication.rs b/lib/src/authentication.rs index 407fc4ee2..692ecdb88 100644 --- a/lib/src/authentication.rs +++ b/lib/src/authentication.rs @@ -41,7 +41,7 @@ pub fn check_auth_signature(subject: &str, auth_header: &AuthValues) -> AtomicRe .verify(message.as_bytes(), &signature_bytes) .map_err(|_e| { format!( - "Incorrect signature for auth headers. This could be due to an error during signing or serialization of the commit. Compare this to the serialized message in the client: {}", + "Incorrect signature for auth headers. This could be due to an error during signing or serialization of the commit. Compare this to the serialized message in the client: '{}'", message, ) })?; diff --git a/lib/src/commit.rs b/lib/src/commit.rs index f8a84e7e2..f3e79879a 100644 --- a/lib/src/commit.rs +++ b/lib/src/commit.rs @@ -443,7 +443,6 @@ impl Commit { .to_string() } }; - println!("commit subject: {}", commit_subject); let mut resource = Resource::new_instance(urls::COMMIT, store)?; resource.set_subject(commit_subject); resource.set_unsafe( diff --git a/lib/src/db.rs b/lib/src/db.rs index f9e7bf4a7..546097a80 100644 --- a/lib/src/db.rs +++ b/lib/src/db.rs @@ -528,7 +528,6 @@ impl Storelike for Db { update_index: bool, overwrite_existing: bool, ) -> AtomicResult<()> { - println!("add_resource_opts {}", resource.get_subject()); // This only works if no external functions rely on using add_resource for atom-like operations! // However, add_atom uses set_propvals, which skips the validation. let existing = self.get_propvals(resource.get_subject()).ok(); diff --git a/server/src/tests.rs b/server/src/tests.rs index eab3cb2f8..f3a9f9170 100644 --- a/server/src/tests.rs +++ b/server/src/tests.rs @@ -16,7 +16,9 @@ use atomic_lib::{urls, Storelike}; /// Returns the request with signed headers. Also adds a json-ad accept header - overwrite this if you need something else. fn build_request_authenticated(path: &str, appstate: &AppState) -> TestRequest { - let url = format!("{}{}", appstate.store.get_server_url(), path); + // remove last slash + let base = appstate.store.get_server_url().to_string(); + let url = format!("{}{}", base.trim_end_matches('/'), path); let headers = atomic_lib::client::get_authentication_headers( &url, &appstate.store.get_default_agent().unwrap(), @@ -106,7 +108,7 @@ async fn server_tests() { // Should 401 (Unauthorized) let req = - test::TestRequest::with_uri("properties").insert_header(("Accept", "application/ad+json")); + test::TestRequest::with_uri("/properties").insert_header(("Accept", "application/ad+json")); let resp = test::call_service(&app, req.to_request()).await; assert_eq!( resp.status().as_u16(), @@ -115,7 +117,7 @@ async fn server_tests() { ); // Get JSON-AD - let req = build_request_authenticated("properties", &appstate); + let req = build_request_authenticated("/properties", &appstate); let resp = test::call_service(&app, req.to_request()).await; let body = get_body(resp); println!("DEBUG: {:?}", body); @@ -126,7 +128,7 @@ async fn server_tests() { ); // Get JSON-LD - let req = build_request_authenticated("properties", &appstate) + let req = build_request_authenticated("/properties", &appstate) .insert_header(("Accept", "application/ld+json")); let resp = test::call_service(&app, req.to_request()).await; assert!(resp.status().is_success(), "setup not returning JSON-LD"); @@ -137,7 +139,7 @@ async fn server_tests() { ); // Get turtle - let req = build_request_authenticated("properties", &appstate) + let req = build_request_authenticated("/properties", &appstate) .insert_header(("Accept", "text/turtle")); let resp = test::call_service(&app, req.to_request()).await; assert!(resp.status().is_success()); @@ -149,7 +151,7 @@ async fn server_tests() { // Get Search // Does not test the contents of the results - the index isn't built at this point - let req = build_request_authenticated("search?q=setup", &appstate); + let req = build_request_authenticated("/search?q=setup", &appstate); let resp = test::call_service(&app, req.to_request()).await; assert!(resp.status().is_success()); let body = get_body(resp); @@ -160,7 +162,7 @@ async fn server_tests() { ); } -/// Gets the body from the response as a String. Why doen't actix provide this? +/// Gets the body from the response as a String. Why doesn't actix provide this? fn get_body(resp: ServiceResponse) -> String { let boxbody = resp.into_body(); let bytes = boxbody.try_into_bytes().unwrap(); From ec60fbd0379590f756f9046c676b05c8582758bd Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:29:58 +0200 Subject: [PATCH 06/51] Debug for endpoints --- lib/src/endpoints.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/src/endpoints.rs b/lib/src/endpoints.rs index fd81ba9e7..35f51a160 100644 --- a/lib/src/endpoints.rs +++ b/lib/src/endpoints.rs @@ -58,7 +58,11 @@ pub struct PostEndpoint { impl Endpoint { /// Converts Endpoint to resource. Does not save it. pub fn to_resource(&self, store: &impl Storelike) -> AtomicResult { - let subject = format!("{}{}", store.get_server_url(), self.path); + let subject = store + .get_server_url() + .clone() + .set_path(&self.path) + .to_string(); let mut resource = store.get_resource_new(&subject); resource.set_string(urls::DESCRIPTION.into(), &self.description, store)?; resource.set_string(urls::SHORTNAME.into(), &self.shortname, store)?; @@ -74,6 +78,14 @@ impl Endpoint { } } +impl std::fmt::Debug for Endpoint { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Endpoint") + .field("path", &self.path) + .finish() + } +} + pub fn default_endpoints() -> Vec { vec![ plugins::versioning::version_endpoint(), From 27787b44815c1b8f3d6a1e8e4a162bce892b7f40 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:29:58 +0200 Subject: [PATCH 07/51] Fix URL refactor paths endpoints #502 --- lib/src/urls.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/src/urls.rs b/lib/src/urls.rs index 7519cdbb9..11ba4b0cb 100644 --- a/lib/src/urls.rs +++ b/lib/src/urls.rs @@ -155,12 +155,12 @@ pub const PUBLIC_AGENT: &str = "https://atomicdata.dev/agents/publicAgent"; pub const SUDO_AGENT: &str = "sudo:agent"; // Paths -pub const PATH_IMPORT: &str = "import"; -pub const PATH_FETCH_BOOKMARK: &str = "fetch-bookmark"; -pub const PATH_TPF: &str = "tpf"; -pub const PATH_PATH: &str = "path"; -pub const PATH_COMMITS: &str = "commits"; -pub const PATH_ENDPOINTS: &str = "endpoints"; +pub const PATH_IMPORT: &str = "/import"; +pub const PATH_FETCH_BOOKMARK: &str = "/fetch-bookmark"; +pub const PATH_TPF: &str = "/tpf"; +pub const PATH_PATH: &str = "/path"; +pub const PATH_COMMITS: &str = "/commits"; +pub const PATH_ENDPOINTS: &str = "/endpoints"; pub const PATH_IMPORT: &str = "/import"; pub const PATH_FETCH_BOOKMARK: &str = "/fetch-bookmark"; From 45dc543e7d1f18d99a7945137b01666466002d10 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:30:32 +0200 Subject: [PATCH 08/51] URL refactor --- lib/src/agents.rs | 6 +++++- lib/src/atomic_url.rs | 1 + server/src/handlers/download.rs | 9 ++++++--- server/src/handlers/upload.rs | 26 +++++++++++++++----------- server/src/helpers.rs | 29 +++++++++++++++++++++++++++++ 5 files changed, 56 insertions(+), 15 deletions(-) diff --git a/lib/src/agents.rs b/lib/src/agents.rs index e2d7c0954..927343162 100644 --- a/lib/src/agents.rs +++ b/lib/src/agents.rs @@ -119,7 +119,11 @@ impl Agent { Ok(Agent { private_key: None, public_key: public_key.into(), - subject: format!("{}/agents/{}", store.get_server_url(), public_key), + subject: store + .get_server_url() + .clone() + .set_path(&format!("agents/{}", public_key)) + .to_string(), name: None, created_at: crate::utils::now(), }) diff --git a/lib/src/atomic_url.rs b/lib/src/atomic_url.rs index babdc71a4..f6c7d84da 100644 --- a/lib/src/atomic_url.rs +++ b/lib/src/atomic_url.rs @@ -111,6 +111,7 @@ impl AtomicUrl { Ok(self) } + /// Removes existing path, sets the new one. Escapes special characters pub fn set_path(&mut self, path: &str) -> &Self { self.url.set_path(path); self diff --git a/server/src/handlers/download.rs b/server/src/handlers/download.rs index 71d2a2ca3..67e2d50c1 100644 --- a/server/src/handlers/download.rs +++ b/server/src/handlers/download.rs @@ -25,13 +25,16 @@ pub async fn handle_download( req: actix_web::HttpRequest, ) -> AtomicServerResult { let headers = req.headers(); - let server_url = &appstate.config.server_url; let store = &appstate.store; // We replace `/download` with `/` to get the subject of the Resource. let subject = if let Some(pth) = path { - let subject = format!("{}/{}", server_url, pth); - subject + appstate + .store + .get_server_url() + .clone() + .set_path(pth.as_str()) + .to_string() } else { // There is no end string, so It's the root of the URL, the base URL! return Err("Put `/download` in front of an File URL to download it.".into()); diff --git a/server/src/handlers/upload.rs b/server/src/handlers/upload.rs index 189332f90..c3b821546 100644 --- a/server/src/handlers/upload.rs +++ b/server/src/handlers/upload.rs @@ -7,7 +7,11 @@ use futures::{StreamExt, TryStreamExt}; use image::GenericImageView; use serde::Deserialize; -use crate::{appstate::AppState, errors::AtomicServerResult, helpers::get_client_agent}; +use crate::{ + appstate::AppState, + errors::AtomicServerResult, + helpers::{get_client_agent, get_subject}, +}; #[derive(Deserialize, Debug)] pub struct UploadQuery { @@ -25,19 +29,19 @@ pub async fn upload_handler( appstate: web::Data, query: web::Query, req: actix_web::HttpRequest, + conn: actix_web::dev::ConnectionInfo, ) -> AtomicServerResult { let store = &appstate.store; let parent = store.get_resource(&query.parent)?; - let subject = format!( - "{}{}", - store.get_server_url(), - req.head() - .uri - .path_and_query() - .ok_or("Path must be given")? - ); - let agent = get_client_agent(req.headers(), &appstate, subject)?; - check_write(store, &parent, &agent)?; + let subject = get_subject(&req, &conn, &appstate)?; + if let Some(agent) = get_client_agent(req.headers(), &appstate, subject)? { + check_write(store, &parent, &agent)?; + } else { + return Err(AtomicError::unauthorized( + "No authorization headers present. These are required when uploading files.".into(), + ) + .into()); + } let mut created_resources: Vec = Vec::new(); diff --git a/server/src/helpers.rs b/server/src/helpers.rs index e2476328a..a8bc02e38 100644 --- a/server/src/helpers.rs +++ b/server/src/helpers.rs @@ -297,3 +297,32 @@ mod test { assert_eq!(out.requested_subject, subject); } } + +/// Extracts the full URL from the request, connection and the store. +// You'd think there would be a simpler way of getting the requested URL... +// See https://github.com/actix/actix-web/issues/2895 +pub fn get_subject( + req: &actix_web::HttpRequest, + conn: &actix_web::dev::ConnectionInfo, + appstate: &AppState, +) -> AtomicServerResult { + let domain = &appstate.config.opts.domain; + let host = conn.host(); + let subdomain = if let Some(index) = host.find(domain) { + if index == 0 { + None + } else { + Some(host[0..index - 1].to_string()) + } + } else { + panic!("Wrong domain! A requested URL did not contain the host for this domain. This should not be able to happen."); + }; + + let mut subject_url = appstate.store.get_server_url().clone(); + if let Some(sd) = subdomain { + subject_url.set_subdomain(Some(&sd))?; + } + let server_without_last_slash = subject_url.to_string().trim_end_matches('/').to_string(); + let subject = format!("{}{}", server_without_last_slash, &req.uri().to_string()); + Ok(subject) +} From f4513118f46990d54a40b9a7cbcea34e2ce0fc91 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:30:32 +0200 Subject: [PATCH 09/51] Add rust vscode extension suggestions --- .vscode/extensions.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 2b0f37c7f..4a4b9cace 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -4,6 +4,9 @@ "vadimcn.vscode-lldb", "rust-lang.rust-analyzer", "styled-components.vscode-styled-components", - "svelte.svelte-vscode" + "svelte.svelte-vscode", + "vadimcn.vscode-lldb", + "rust-lang.rust-analyzer", + "serayuzgur.crates" ] } From ef3c650d61a03f80c229a33d70cb7f1fa676c2cd Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:30:32 +0200 Subject: [PATCH 10/51] Fix pnpm tasks --- .vscode/tasks.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 223a84826..17ea49326 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -26,9 +26,9 @@ "group": "test" }, { - "label": "test end-to-end / E2E (npm playwright)", + "label": "test end-to-end / E2E (pnpm playwright)", "type": "shell", - "command": "cd server/e2e_tests/ && npm i && npm run test", + "command": "cd server/e2e_tests/ && pnpm i && pnpm run test", "group": "test" }, { From 37aa7a1b096f6868fb796f32ca7b330b00571feb Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:30:32 +0200 Subject: [PATCH 11/51] Improve register endpoint, WIP --- lib/src/plugins/register.rs | 43 ++++++++++++++++++++++++++----------- lib/src/populate.rs | 4 ++-- 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/lib/src/plugins/register.rs b/lib/src/plugins/register.rs index b097cb045..ce781242c 100644 --- a/lib/src/plugins/register.rs +++ b/lib/src/plugins/register.rs @@ -1,6 +1,13 @@ //! Creates a new Drive and optionally also an Agent. -use crate::{agents::Agent, endpoints::Endpoint, errors::AtomicResult, urls, Resource, Storelike}; +use crate::{ + agents::Agent, + endpoints::Endpoint, + errors::AtomicResult, + urls::{self, PUBLIC_AGENT}, + values::SubResource, + Resource, Storelike, +}; pub fn register_endpoint() -> Endpoint { Endpoint { @@ -42,13 +49,16 @@ pub fn construct_register_redirect( return Err("No name provided".into()); }; - let mut new_agent = None; - let drive_creator_agent: String = if let Some(key) = pub_key { - let new = Agent::new_from_public_key(store, &key)?.subject; - new_agent = Some(new.clone()); - new + let mut new = Agent::new_from_public_key(store, &key)?; + new.name = Some(name.clone()); + let net_agent_subject = new.subject.to_string(); + new.to_resource()?.save(store)?; + net_agent_subject } else if let Some(agent) = for_agent { + if agent == PUBLIC_AGENT { + return Err("No `public-key` provided.".into()); + } agent.to_string() } else { return Err("No `public-key` provided".into()); @@ -57,16 +67,23 @@ pub fn construct_register_redirect( // Create the new Drive let drive = crate::populate::create_drive(store, Some(&name), &drive_creator_agent, false)?; + // Add the drive to the Agent's list of drives + let mut agent = store.get_resource(&drive_creator_agent)?; + agent.push( + urls::DRIVES, + SubResource::Subject(drive.get_subject().into()), + true, + )?; + agent.save_locally(store)?; + // Construct the Redirect Resource, which might provide the Client with a Subject for his Agent. let mut redirect = Resource::new_instance(urls::REDIRECT, store)?; redirect.set_string(urls::DESTINATION.into(), drive.get_subject(), store)?; - if let Some(agent) = new_agent { - redirect.set( - urls::REDIRECT_AGENT.into(), - crate::Value::AtomicUrl(agent), - store, - )?; - } + redirect.set( + urls::REDIRECT_AGENT.into(), + crate::Value::AtomicUrl(drive_creator_agent), + store, + )?; // The front-end requires the @id to be the same as requested redirect.set_subject(requested_subject); Ok(redirect) diff --git a/lib/src/populate.rs b/lib/src/populate.rs index b34dd824c..eff611f73 100644 --- a/lib/src/populate.rs +++ b/lib/src/populate.rs @@ -177,9 +177,9 @@ pub fn create_drive( self_url.to_string() }; - let mut drive = if drive_name.is_some() { + let mut drive = if let Some(drive_name_some) = drive_name { if store.get_resource(&drive_subject).is_ok() { - return Err("Drive URL is already taken".into()); + return Err(format!("Name '{}' is already taken", drive_name_some).into()); } Resource::new(drive_subject) } else { From c4772da05b734e9af36b7f2cdbd533298a11e0ac Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:30:32 +0200 Subject: [PATCH 12/51] Rebased --- lib/src/commit.rs | 1 + lib/src/db.rs | 19 +++++++++---------- lib/src/db/test.rs | 2 +- lib/src/populate.rs | 1 - lib/src/store.rs | 9 ++------- server/src/helpers.rs | 3 ++- 6 files changed, 15 insertions(+), 20 deletions(-) diff --git a/lib/src/commit.rs b/lib/src/commit.rs index f3e79879a..d4583b0e7 100644 --- a/lib/src/commit.rs +++ b/lib/src/commit.rs @@ -6,6 +6,7 @@ use urls::{SET, SIGNER}; use crate::{ agents::{decode_base64, encode_base64}, + atomic_url::Routes, datatype::DataType, errors::AtomicResult, resources::PropVals, diff --git a/lib/src/db.rs b/lib/src/db.rs index 546097a80..bc2350ac3 100644 --- a/lib/src/db.rs +++ b/lib/src/db.rs @@ -16,9 +16,9 @@ use std::{ vec, }; -use tracing::{info, instrument}; +use tracing::info; +use tracing::instrument; use trees::{Method, Operation, Transaction, Tree}; -use url::Url; use crate::{ agents::ForAgent, @@ -267,7 +267,6 @@ impl Db { } fn map_sled_item_to_resource( - &self, item: Result<(sled::IVec, sled::IVec), sled::Error>, self_url: String, include_external: bool, @@ -275,7 +274,10 @@ impl Db { let (subject, resource_bin) = item.expect(DB_CORRUPT_MSG); let subject: String = String::from_utf8_lossy(&subject).to_string(); - if !include_external && self.is_external_subject(&subject).ok()? { + // if !include_external && self.is_external_subject(&subject).ok()? { + // return None; + // } + if !include_external && !subject.starts_with(&self_url) { return None; } @@ -386,10 +388,6 @@ impl Db { } fn query_basic(&self, q: &Query) -> AtomicResult { - let self_url = self - .get_self_url() - .ok_or("No self_url set, required for Queries")?; - let mut subjects: Vec = vec![]; let mut resources: Vec = vec![]; let mut total_count = 0; @@ -829,10 +827,11 @@ impl Storelike for Db { ) -> Box> { let self_url = self .get_self_url() - .expect("No self URL set, is required in DB"); + .expect("No self URL set, is required in DB") + .to_string(); let result = self.resources.into_iter().filter_map(move |item| { - Db::map_sled_item_to_resource(self, item, self_url.to_string(), include_external) + Db::map_sled_item_to_resource(item, self_url.clone(), include_external) }); Box::new(result) diff --git a/lib/src/db/test.rs b/lib/src/db/test.rs index 02dcfe0ac..9438f6172 100644 --- a/lib/src/db/test.rs +++ b/lib/src/db/test.rs @@ -1,4 +1,4 @@ -use crate::{agents::ForAgent, urls, Value}; +use crate::{agents::ForAgent, urls, Value, Routes}; use super::*; use ntest::timeout; diff --git a/lib/src/populate.rs b/lib/src/populate.rs index eff611f73..08d1df169 100644 --- a/lib/src/populate.rs +++ b/lib/src/populate.rs @@ -389,7 +389,6 @@ pub fn populate_all(store: &crate::Db) -> AtomicResult<()> { set_drive_rights(store, true)?; populate_collections(store).map_err(|e| format!("Failed to populate collections. {}", e))?; populate_endpoints(store).map_err(|e| format!("Failed to populate endpoints. {}", e))?; - populate_importer(store).map_err(|e| format!("Failed to populate importer. {}", e))?; populate_sidebar_items(store) .map_err(|e| format!("Failed to populate sidebar items. {}", e))?; Ok(()) diff --git a/lib/src/store.rs b/lib/src/store.rs index 94ad95849..0f74cb1dc 100644 --- a/lib/src/store.rs +++ b/lib/src/store.rs @@ -5,14 +5,9 @@ use url::Url; use crate::agents::Agent; use crate::storelike::QueryResult; -use crate::Value; -use crate::{atoms::Atom, storelike::Storelike}; -use crate::{ - atomic_url::AtomicUrl, - atoms::Atom, - storelike::{ResourceCollection, Storelike}, -}; +use crate::{atomic_url::AtomicUrl, storelike::Storelike}; use crate::{errors::AtomicResult, Resource}; +use crate::{Atom, Value}; use std::{collections::HashMap, sync::Arc, sync::Mutex}; /// The in-memory store of data, containing the Resources, Properties and Classes diff --git a/server/src/helpers.rs b/server/src/helpers.rs index a8bc02e38..994de8593 100644 --- a/server/src/helpers.rs +++ b/server/src/helpers.rs @@ -6,11 +6,12 @@ use actix_web::http::Uri; use atomic_lib::agents::ForAgent; use atomic_lib::authentication::AuthValues; use atomic_lib::AtomicError; +use atomic_lib::Storelike; use percent_encoding::percent_decode_str; use std::str::FromStr; use crate::errors::{AppErrorType, AtomicServerError}; -use crate::{appstate::AppState, content_types::ContentType, errors::AtomicServerResult}; +use crate::{appstate::AppState, errors::AtomicServerResult}; /// Returns the authentication headers from the request #[tracing::instrument(skip_all)] From fd75795b70f26f95c4f58ff529a74d8e2eed2de6 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:30:32 +0200 Subject: [PATCH 13/51] Update changelog --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c8b0b811..b84c500f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -86,9 +86,12 @@ See [STATUS.md](server/STATUS.md) to learn more about which features will remain - Refactor `Endpoint` handlers, uses a Context now #592 - Re-build store + invite when adjusting server url #607 - Use local atomic-server for properties and classes, improves atomic-server #604 +- Add multi-tenancy support. Users can create their own `Drives` on subdomains. #288 +- Add `/register` Endpoint #489 #254 +- Refactor URLs. `store.self_url()` returns an `AtomicUrl`, which provides methods to easily add paths, find subdomains and more. +- Add support for subdomains, use a Wildcard TLS certificate #502 ## [v0.34.1] - 2023-02-11 - - Improve query performance, refactor indexes. The `.tpf` API is deprecated in favor of the more powerful `.query`. #529 - Replace `acme_lib` with `instant-acme`, drop OpenSSL dependency, add DNS verification for TLS option with `--https-dns` #192 - Improved error handling for HTTPS initialization #530 From c1f8de75e7e8135601d72aa90a65c8e6c891f9d6 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:30:32 +0200 Subject: [PATCH 14/51] Custom .env parsing, smtp server opts --- server/src/config.rs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/server/src/config.rs b/server/src/config.rs index 6a0f60c96..1e5b89fd4 100644 --- a/server/src/config.rs +++ b/server/src/config.rs @@ -75,6 +75,7 @@ pub struct Opts { pub data_dir: Option, /// CAUTION: Skip authentication checks, making all data publicly readable. Improves performance. + #[clap(long, env = "ATOMIC_PUBLIC_MODE")] pub public_mode: bool, @@ -94,6 +95,18 @@ pub struct Opts { /// Introduces random delays in the server, to simulate a slow connection. Useful for testing. #[clap(long, env = "ATOMIC_SLOW_MODE")] pub slow_mode: bool, + + /// Add this if you want so send e-mails (e.g. on user registration) + #[clap(long, env = "ATOMIC_SMTP_HOST")] + pub smpt_host: Option, + + /// Useful for debugging e-mails during development, or if your SMTP server has an unconventional port number. + #[clap(long, env = "ATOMIC_SMTP_PORT", default_value = "25")] + pub smpt_port: u16, + + /// If you want to parse options from a specific .env file. By default reads the `./.env` file. + #[clap(long)] + pub env_file: Option, } #[derive(clap::ValueEnum, Clone, Debug)] @@ -200,7 +213,14 @@ pub fn read_opts() -> Opts { dotenv().ok(); // Parse CLI options, .env values, set defaults - Opts::parse() + let mut opts = Opts::parse(); + if let Some(env_path) = &opts.env_file { + dotenv::from_path(env_path) + .unwrap_or_else(|_| panic!("Env file not found: {} ", env_path.display())); + // Parse the opts again so the CLI opts override the .env file + opts = Opts::parse(); + } + opts } /// Creates the server config, reads .env values and sets defaults From ebaad5ac9d3289bf007a3b046b506f1225f6be98 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:33:00 +0200 Subject: [PATCH 15/51] Register endpoint, mail, tokens, #489 #254 #544 --- .vscode/tasks.json | 17 + CHANGELOG.md | 7 +- CONTRIBUTING.md | 1 + Cargo.lock | 624 ++++++++++++++++++++++++++++++++ lib/Cargo.toml | 9 +- lib/defaults/default_store.json | 8 +- lib/src/client/helpers.rs | 2 +- lib/src/db.rs | 69 +++- lib/src/db/migrations.rs | 2 +- lib/src/db/test.rs | 2 +- lib/src/email.rs | 108 ++++++ lib/src/endpoints.rs | 2 + lib/src/lib.rs | 26 +- lib/src/plugins/register.rs | 147 ++++++-- lib/src/populate.rs | 23 +- lib/src/store.rs | 8 +- lib/src/storelike.rs | 3 + lib/src/token.rs | 79 ++++ lib/src/urls.rs | 5 +- server/src/appstate.rs | 12 +- server/src/bin.rs | 6 +- server/src/handlers/upload.rs | 10 +- server/src/helpers.rs | 1 + server/src/serve.rs | 2 +- server/src/tests.rs | 4 +- 25 files changed, 1080 insertions(+), 97 deletions(-) create mode 100644 lib/src/email.rs create mode 100644 lib/src/token.rs diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 17ea49326..1e14f6722 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -15,6 +15,23 @@ "group": "build", "problemMatcher": [] }, + { + "label": "watch atomic-server (cargo watch)", + "type": "shell", + "command": "~/.cargo/bin/cargo-watch", + "args": [ + "--", + "cargo", + "run", + "--bin", + "atomic-server", + "--", + "--env-file", + "server/.env", + ], + "group": "build", + "problemMatcher": [] + }, { "label": "test atomic-server (cargo nextest run)", "type": "shell", diff --git a/CHANGELOG.md b/CHANGELOG.md index b84c500f9..3878a78df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -86,8 +86,8 @@ See [STATUS.md](server/STATUS.md) to learn more about which features will remain - Refactor `Endpoint` handlers, uses a Context now #592 - Re-build store + invite when adjusting server url #607 - Use local atomic-server for properties and classes, improves atomic-server #604 +- New sign up / register flow. Add `/register` Endpoint #489 #254 - Add multi-tenancy support. Users can create their own `Drives` on subdomains. #288 -- Add `/register` Endpoint #489 #254 - Refactor URLs. `store.self_url()` returns an `AtomicUrl`, which provides methods to easily add paths, find subdomains and more. - Add support for subdomains, use a Wildcard TLS certificate #502 @@ -96,6 +96,11 @@ See [STATUS.md](server/STATUS.md) to learn more about which features will remain - Replace `acme_lib` with `instant-acme`, drop OpenSSL dependency, add DNS verification for TLS option with `--https-dns` #192 - Improved error handling for HTTPS initialization #530 - Add `--force` to `atomic-server import` #536 +- Email support. Connect to external SMTP servers. #276 +- Basic plugin support through Endpoints. For now only works if you use `**Atomic**-Lib` as a library. Add your plugins by calling `Db::register_endpoint`. +- Allow parsing `.env` files from custom locations using the `--env-file` flag. +- Plugins support `tokio`, so you can spawn async tasks from plugins. +- Add JWT token support, used for emails and registration #544 - Fix index issue happening when deleting a single property in a sorted collection #545 - Update JS assets & playwright - Fix initial indexing bug #560 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 53fa38035..aae4512ab 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -49,6 +49,7 @@ TL;DR Clone the repo and run `cargo run` from each folder (e.g. `cli` or `server - Go to `browser`, run `pnpm install` (if you haven't already), and run `pnpm dev` to start the browser - Visit your `localhost` in your locally running `atomic-data-browser` instance: (e.g. `http://localhost:5173/app/show?subject=http%3A%2F%2Flocalhost`) - use `cargo watch -- cargo run` to automatically recompile `atomic-server` when you update JS assets in `browser` +- use `cargo watch -- cargo run --bin atomic-server -- --env-file server/.env` to automatically recompile `atomic-server` when you update code or JS assets. ### IDE setup (VSCode) diff --git a/Cargo.lock b/Cargo.lock index 40df6eed8..0082432cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -541,6 +541,15 @@ dependencies = [ "wait-timeout", ] +[[package]] +name = "async-mutex" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e" +dependencies = [ + "event-listener", +] + [[package]] name = "async-trait" version = "0.1.83" @@ -627,15 +636,18 @@ dependencies = [ name = "atomic_lib" version = "0.40.0" dependencies = [ + "async-mutex", "base64 0.21.7", "bincode", "criterion", "directories", "html2md", "iai", + "jwt-simple", "kuchikiki", "lazy_static", "lol_html", + "mail-send", "ntest", "rand 0.8.5", "regex", @@ -646,6 +658,7 @@ dependencies = [ "serde_jcs", "serde_json", "sled", + "tokio", "toml", "tracing", "ulid", @@ -698,6 +711,18 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "base64" version = "0.21.7" @@ -710,6 +735,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + [[package]] name = "bincode" version = "1.3.3" @@ -719,6 +750,12 @@ dependencies = [ "serde", ] +[[package]] +name = "binstring" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e0d60973d9320722cb1206f412740e162a33b8547ea8d6be75d7cff237c7a85" + [[package]] name = "bit_field" version = "0.10.2" @@ -986,6 +1023,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "coarsetime" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b3839cf01bb7960114be3ccf2340f541b6d0c81f8690b007b2b39f750f7e5d" +dependencies = [ + "libc", + "wasix", + "wasm-bindgen", +] + [[package]] name = "color_quant" version = "1.1.0" @@ -1031,6 +1079,18 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "const-oid" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "convert_case" version = "0.4.0" @@ -1180,6 +1240,28 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "crypto-bigint" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -1217,6 +1299,12 @@ dependencies = [ "syn 2.0.79", ] +[[package]] +name = "ct-codecs" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "026ac6ceace6298d2c557ef5ed798894962296469ec7842288ea64674201a2d1" + [[package]] name = "darling" version = "0.20.10" @@ -1252,6 +1340,39 @@ dependencies = [ "syn 2.0.79", ] +[[package]] +name = "der" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" +dependencies = [ + "const-oid 0.7.1", + "crypto-bigint 0.3.2", + "pem-rfc7468 0.3.1", +] + +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid 0.9.6", + "pem-rfc7468 0.6.0", + "zeroize", +] + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid 0.9.6", + "pem-rfc7468 0.7.0", + "zeroize", +] + [[package]] name = "deranged" version = "0.3.11" @@ -1301,7 +1422,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", + "const-oid 0.9.6", "crypto-common", + "subtle", ] [[package]] @@ -1398,6 +1521,30 @@ dependencies = [ "dtoa", ] +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der 0.7.9", + "digest", + "elliptic-curve", + "rfc6979", + "signature 2.2.0", + "spki 0.7.3", +] + +[[package]] +name = "ed25519-compact" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9b3460f44bea8cd47f45a0c70892f1eff856d97cd55358b2f73f663789f6190" +dependencies = [ + "ct-codecs", + "getrandom 0.2.15", +] + [[package]] name = "edit" version = "0.1.5" @@ -1414,6 +1561,27 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint 0.5.5", + "digest", + "ff", + "generic-array", + "group", + "hkdf", + "pem-rfc7468 0.7.0", + "pkcs8 0.10.2", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + [[package]] name = "encode_unicode" version = "0.3.6" @@ -1461,6 +1629,12 @@ dependencies = [ "str-buf", ] +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + [[package]] name = "exr" version = "1.72.0" @@ -1509,6 +1683,16 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "flate2" version = "1.0.34" @@ -1679,6 +1863,27 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", +] + +[[package]] +name = "gethostname" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "gethostname" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +dependencies = [ + "libc", + "windows-targets 0.48.5", ] [[package]] @@ -1727,6 +1932,17 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "h2" version = "0.3.26" @@ -1811,6 +2027,48 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "hmac-sha1-compact" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9d405ec732fa3fcde87264e54a32a84956a377b3e3107de96e59b798c84a7" + +[[package]] +name = "hmac-sha256" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3688e69b38018fec1557254f64c8dc2cc8ec502890182f395dbb0aa997aa5735" +dependencies = [ + "digest", +] + +[[package]] +name = "hmac-sha512" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4ce1f4656bae589a3fab938f9f09bf58645b7ed01a2c5f8a3c238e01a4ef78a" +dependencies = [ + "digest", +] + [[package]] name = "home" version = "0.5.9" @@ -2191,6 +2449,46 @@ dependencies = [ "rayon", ] +[[package]] +name = "jwt-simple" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357892bb32159d763abdea50733fadcb9a8e1c319a9aa77592db8555d05af83e" +dependencies = [ + "anyhow", + "binstring", + "coarsetime", + "ct-codecs", + "ed25519-compact", + "hmac-sha1-compact", + "hmac-sha256", + "hmac-sha512", + "k256", + "p256", + "p384", + "rand 0.8.5", + "rsa 0.7.2", + "serde", + "serde_json", + "spki 0.6.0", + "thiserror", + "zeroize", +] + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2", + "signature 2.2.0", +] + [[package]] name = "kuchikiki" version = "0.8.2" @@ -2215,6 +2513,9 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin 0.9.8", +] [[package]] name = "lazycell" @@ -2365,6 +2666,34 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" +[[package]] +name = "mail-builder" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8390bb0f68168cabc59999706c2199c9b556d7dfd94e65a26e8840fa1d58b238" +dependencies = [ + "gethostname 0.4.3", +] + +[[package]] +name = "mail-send" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17009bb6737cd4b3e2422a0046dc18a07b8ca8f3fa87e59ea3e8408cb0d2e8f" +dependencies = [ + "base64 0.13.1", + "gethostname 0.2.3", + "mail-builder", + "md5", + "rand 0.8.5", + "rsa 0.6.1", + "rustls 0.20.9", + "sha2", + "tokio", + "tokio-rustls 0.23.4", + "webpki-roots 0.22.6", +] + [[package]] name = "markup5ever" version = "0.11.0" @@ -2416,6 +2745,12 @@ dependencies = [ "rayon", ] +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + [[package]] name = "measure_time" version = "0.8.3" @@ -2628,6 +2963,23 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.5", + "smallvec", + "zeroize", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -2654,6 +3006,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-rational" version = "0.4.2" @@ -2826,6 +3189,30 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1584efc0f222d0fc8f31e6c229b8fd93f69280aab398828f0dbce3957178a1ac" +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p384" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + [[package]] name = "parking_lot" version = "0.11.2" @@ -2911,6 +3298,33 @@ dependencies = [ "serde", ] +[[package]] +name = "pem-rfc7468" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01de5d978f34aa4b2296576379fcc416034702fd94117c56ffd8a1a767cefb30" +dependencies = [ + "base64ct", +] + +[[package]] +name = "pem-rfc7468" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d159833a9105500e0398934e205e0773f0b27529557134ecfc51c27646adac" +dependencies = [ + "base64ct", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -3041,6 +3455,60 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs1" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a78f66c04ccc83dd4486fd46c33896f4e17b24a7a3a6400dedc48ed0ddd72320" +dependencies = [ + "der 0.5.1", + "pkcs8 0.8.0", + "zeroize", +] + +[[package]] +name = "pkcs1" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff33bdbdfc54cc98a2eca766ebdec3e1b8fb7387523d5c9c9a2891da856f719" +dependencies = [ + "der 0.6.1", + "pkcs8 0.9.0", + "spki 0.6.0", + "zeroize", +] + +[[package]] +name = "pkcs8" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" +dependencies = [ + "der 0.5.1", + "spki 0.5.4", + "zeroize", +] + +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der 0.6.1", + "spki 0.6.0", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der 0.7.9", + "spki 0.7.3", +] + [[package]] name = "pkg-config" version = "0.3.31" @@ -3136,6 +3604,15 @@ dependencies = [ "termtree", ] +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "proc-macro-crate" version = "3.2.0" @@ -3476,6 +3953,16 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + [[package]] name = "rgb" version = "0.8.50" @@ -3532,6 +4019,47 @@ dependencies = [ "rio_api", ] +[[package]] +name = "rsa" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cf22754c49613d2b3b119f0e5d46e34a2c628a937e3024b8762de4e7d8c710b" +dependencies = [ + "byteorder", + "digest", + "num-bigint-dig", + "num-integer", + "num-iter", + "num-traits", + "pkcs1 0.3.3", + "pkcs8 0.8.0", + "rand_core 0.6.4", + "smallvec", + "subtle", + "zeroize", +] + +[[package]] +name = "rsa" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "094052d5470cbcef561cb848a7209968c9f12dfa6d668f4bca048ac5de51099c" +dependencies = [ + "byteorder", + "digest", + "num-bigint-dig", + "num-integer", + "num-iter", + "num-traits", + "pkcs1 0.4.1", + "pkcs8 0.9.0", + "rand_core 0.6.4", + "signature 1.6.4", + "smallvec", + "subtle", + "zeroize", +] + [[package]] name = "rust-stemmers" version = "1.2.0" @@ -3742,6 +4270,20 @@ dependencies = [ "untrusted 0.9.0", ] +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der 0.7.9", + "generic-array", + "pkcs8 0.10.2", + "subtle", + "zeroize", +] + [[package]] name = "security-framework" version = "2.11.1" @@ -3915,6 +4457,17 @@ dependencies = [ "digest", ] +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -3945,6 +4498,26 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + [[package]] name = "simd-adler32" version = "0.3.7" @@ -4037,6 +4610,36 @@ dependencies = [ "lock_api", ] +[[package]] +name = "spki" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27" +dependencies = [ + "base64ct", + "der 0.5.1", +] + +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der 0.6.1", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der 0.7.9", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -4449,9 +5052,21 @@ dependencies = [ "pin-project-lite", "signal-hook-registry", "socket2", + "tokio-macros", "windows-sys 0.52.0", ] +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.79", +] + [[package]] name = "tokio-rustls" version = "0.23.4" @@ -4843,6 +5458,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasix" +version = "0.12.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1fbb4ef9bbca0c1170e0b00dd28abc9e3b68669821600cad1caaed606583c6d" +dependencies = [ + "wasi 0.11.0+wasi-snapshot-preview1", +] + [[package]] name = "wasm-bindgen" version = "0.2.93" diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 7e88e6588..99f5657d5 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -15,6 +15,9 @@ harness = false name = "benchmarks" # path = "benches/benchmarks.rs" +[package.metadata.docs.rs] +all-features = true + [dependencies] base64 = "0.21" bincode = { version = "1", optional = true } @@ -38,6 +41,10 @@ ureq = "2" url = "2" urlencoding = "2" ulid = "1.1.3" +mail-send = {version= "0.2", optional = true} +async-mutex = {version = "1.4.0", optional = true} +tokio = {version = "1.22.0", optional = true, features = ["full"] } +jwt-simple = {version = "0.11.2", optional = true} [dev-dependencies] criterion = "0.5" @@ -47,6 +54,6 @@ ntest = "0.9" [features] config = ["directories", "toml"] -db = ["sled", "bincode"] html = ["kuchikiki", "lol_html", "html2md"] +db = ["sled", "bincode", "mail-send", "tokio", "async-mutex", "jwt-simple"] rdf = ["rio_api", "rio_turtle"] diff --git a/lib/defaults/default_store.json b/lib/defaults/default_store.json index d2e25c1b1..f2a2305d3 100644 --- a/lib/defaults/default_store.json +++ b/lib/defaults/default_store.json @@ -525,11 +525,17 @@ "@id": "https://atomicdata.dev/properties/localId", "https://atomicdata.dev/properties/datatype": "https://atomicdata.dev/datatypes/string", "https://atomicdata.dev/properties/description": "A `localId` is used to derive the Subject (`@id`, the URL) of an imported Resource. This is useful when some data owner has Atomic Data but does not have the technology to host all Resources at their respective URLs. [See docs](https://docs.atomicdata.dev/create-json-ad.html#preventing-duplication-with-localid).", + "https://atomicdata.dev/properties/shortname": "local-id" + }, + { + "@id": "https://atomicdata.dev/properties/email", + "https://atomicdata.dev/properties/datatype": "https://atomicdata.dev/datatypes/string", + "https://atomicdata.dev/properties/description": "Primary email address ", "https://atomicdata.dev/properties/isA": [ "https://atomicdata.dev/classes/Property" ], "https://atomicdata.dev/properties/parent": "https://atomicdata.dev/properties", - "https://atomicdata.dev/properties/shortname": "local-id" + "https://atomicdata.dev/properties/shortname": "email" }, { "@id": "https://atomicdata.dev/properties/incomplete", diff --git a/lib/src/client/helpers.rs b/lib/src/client/helpers.rs index 5122cf2d3..8e65c0e74 100644 --- a/lib/src/client/helpers.rs +++ b/lib/src/client/helpers.rs @@ -59,7 +59,7 @@ pub fn fetch_body( } let agent = ureq::builder() - .timeout(std::time::Duration::from_secs(2)) + .timeout(std::time::Duration::from_secs(10)) .build(); let resp = agent .get(url) diff --git a/lib/src/db.rs b/lib/src/db.rs index bc2350ac3..3660058f8 100644 --- a/lib/src/db.rs +++ b/lib/src/db.rs @@ -1,5 +1,6 @@ //! Persistent, ACID compliant, threadsafe to-disk store. //! Powered by Sled - an embedded database. +//! See [Db] mod migrations; mod prop_val_sub_index; @@ -16,6 +17,7 @@ use std::{ vec, }; +use mail_send::{Connected, Transport}; use tracing::info; use tracing::instrument; use trees::{Method, Operation, Transaction, Tree}; @@ -24,11 +26,10 @@ use crate::{ agents::ForAgent, atomic_url::AtomicUrl, atoms::IndexAtom, - commit::{CommitOpts, CommitResponse}, - db::{ - query_index::{requires_query_index, NO_VALUE}, - val_prop_sub_index::find_in_val_prop_sub_index, - }, + commit::CommitOpts, + commit::CommitResponse, + db::{query_index::requires_query_index, val_prop_sub_index::find_in_val_prop_sub_index}, + email::{self, MailMessage}, endpoints::{default_endpoints, Endpoint, HandleGetContext}, errors::{AtomicError, AtomicResult}, resources::PropVals, @@ -43,7 +44,7 @@ use self::{ prop_val_sub_index::{add_atom_to_prop_val_sub_index, find_in_prop_val_sub_index}, query_index::{ check_if_atom_matches_watched_query_filters, query_sorted_indexed, should_include_resource, - update_indexed_member, IndexIterator, QueryFilter, + update_indexed_member, IndexIterator, QueryFilter, NO_VALUE, }, val_prop_sub_index::add_atom_to_valpropsub_index, }; @@ -55,14 +56,15 @@ type HandleCommit = Box; /// The String on the left represents a Property URL, and the second one is the set of subjects. pub type PropSubjectMap = HashMap>; -/// The Db is a persistent on-disk Atomic Data store. +/// A persistent on-disk Atomic Data store. /// It's an implementation of [Storelike]. /// It uses [sled::Tree]s as Key Value stores. /// It stores [Resource]s as [PropVals]s by their subject as key. /// It builds a value index for performant [Query]s. /// It keeps track of Queries and updates their index when [crate::Commit]s are applied. -/// You can pass a custom `on_commit` function to run at Commit time. /// `Db` should be easily, cheaply clone-able, as users of this library could have one `Db` per connection. +/// Note that [plugins](crate::plugins) can add their own endpoints to the [Db], +/// and can use [tokio::spawn] to start concurrent tasks. #[derive(Clone)] pub struct Db { /// The Key-Value store that contains all data. @@ -88,6 +90,9 @@ pub struct Db { on_commit: Option>, /// Where the DB is stored on disk. path: std::path::PathBuf, + handle_commit: Option>, + /// Email SMTP client for sending email. + smtp_client: Option>>>, } impl Db { @@ -109,12 +114,14 @@ impl Db { default_agent: Arc::new(Mutex::new(None)), resources, reference_index, + on_commit: None, query_index, prop_val_sub_index, server_url: AtomicUrl::try_from(server_url)?, watched_queries, endpoints: default_endpoints(), - on_commit: None, + handle_commit: None, + smtp_client: None, }; migrate_maybe(&store).map(|e| format!("Error during migration of database: {:?}", e))?; crate::populate::populate_base_models(&store) @@ -218,7 +225,7 @@ impl Db { /// Sets a function that is called whenever a [Commit::apply] is called. /// This can be used to listen to events. pub fn set_handle_commit(&mut self, on_commit: HandleCommit) { - self.on_commit = Some(Arc::new(on_commit)); + self.handle_commit = Some(Arc::new(on_commit)); } /// Finds resource by Subject, return PropVals HashMap @@ -395,14 +402,15 @@ impl Db { let atoms = self.get_index_iterator_for_query(q); for (i, atom_res) in atoms.enumerate() { - let atom = atom_res?; - if !q.include_external && self.is_external_subject(&atom.subject).unwrap() { + total_count += 1; + + if q.offset > i { continue; } - total_count += 1; + let atom = atom_res?; - if q.offset > i { + if !q.include_external && self.is_external_subject(&atom.subject)? { continue; } @@ -477,6 +485,37 @@ impl Db { } Ok(()) } + + /// Adds an [Endpoint] to the store. This means adding a route with custom behavior. + pub fn register_endpoint(&mut self, endpoint: Endpoint) { + self.endpoints.push(endpoint); + } + + /// Registers an SMTP client to the store, allowing the store to send emails. + pub async fn set_smtp_config( + &mut self, + smtp_config: crate::email::SmtpConfig, + ) -> AtomicResult<()> { + self.smtp_client = Some(Arc::new(Mutex::new( + crate::email::get_smtp_client(smtp_config).await?, + ))); + Ok(()) + } + + pub async fn send_email(&self, message: MailMessage) -> AtomicResult<()> { + let mut client = self + .smtp_client + .as_ref() + .ok_or_else(|| { + AtomicError::other_error( + "No SMTP client configured. Please call set_smtp_config first.".into(), + ) + })? + .lock() + .await; + email::send_mail(&mut client, message).await?; + Ok(()) + } } impl Drop for Db { @@ -803,7 +842,7 @@ impl Storelike for Db { } fn handle_commit(&self, commit_response: &CommitResponse) { - if let Some(fun) = &self.on_commit { + if let Some(fun) = &self.handle_commit { fun(commit_response); } } diff --git a/lib/src/db/migrations.rs b/lib/src/db/migrations.rs index 610b0e6c4..a78d04dd8 100644 --- a/lib/src/db/migrations.rs +++ b/lib/src/db/migrations.rs @@ -75,7 +75,7 @@ fn v0_to_v1(store: &Db) -> AtomicResult<()> { /// Add `prop_val_sub` index fn ref_v0_to_v1(store: &Db) -> AtomicResult<()> { - tracing::warn!("Rebuilding indexes..."); + tracing::warn!("Rebuilding indexes due to migrating to new version..."); store.db.drop_tree("reference_index")?; store.build_index(true)?; tracing::warn!("Rebuilding index finished!"); diff --git a/lib/src/db/test.rs b/lib/src/db/test.rs index 9438f6172..0b522c5dc 100644 --- a/lib/src/db/test.rs +++ b/lib/src/db/test.rs @@ -1,4 +1,4 @@ -use crate::{agents::ForAgent, urls, Value, Routes}; +use crate::{agents::ForAgent, atomic_url::Routes, urls, Value}; use super::*; use ntest::timeout; diff --git a/lib/src/email.rs b/lib/src/email.rs new file mode 100644 index 000000000..764fd1b67 --- /dev/null +++ b/lib/src/email.rs @@ -0,0 +1,108 @@ +//! [EmailAddress] with validation, [MailMessage] with sending, and [get_smtp_client] for setting up mail. + +use crate::{errors::AtomicResult, storelike::Query, urls, Storelike}; +use mail_send::{mail_builder::MessageBuilder, Connected, Transport}; +use serde::{Deserialize, Serialize}; +use tracing::info; + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct EmailAddress { + pub address: String, +} + +impl EmailAddress { + pub fn new(address: String) -> AtomicResult { + // TODO: use decent crate for validation, like Lettre + if !address.contains('@') { + return Err(format!("Invalid email address: {}", address).into()); + } + Ok(Self { address }) + } + + /// Throws error if email address is already taken + pub fn check_used(self, store: &impl Storelike) -> AtomicResult { + let mut query = Query::new(); + // TODO: This hits too many resources, as it will also include non-agent resources + query.property = Some(urls::EMAIL.into()); + query.value = Some(crate::Value::String(self.address.clone())); + if store.query(&query)?.count > 0 { + return Err("Email address already used".into()); + } + Ok(self) + } +} + +impl std::fmt::Display for EmailAddress { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.address) + } +} + +pub struct SmtpConfig { + pub host: String, + pub port: u16, +} + +pub async fn get_smtp_client( + config: SmtpConfig, +) -> AtomicResult> { + let full_address = format!("{}:{}", config.host, config.port); + info!("Connecting to mailserver {full_address}"); + let connection = Transport::new(config.host.clone()) + .port(config.port) + .connect() + .await + .map_err(|e| format!("Error connecting to SMTP mail server: at {full_address}. Is it running? Error message: {e}"))?; + Ok(connection) +} + +#[derive(Debug)] +pub struct MailMessage { + pub to: EmailAddress, + pub subject: String, + pub body: String, + pub action: Option, +} + +#[derive(Debug)] +pub struct MailAction { + pub name: String, + pub url: String, +} + +#[tracing::instrument(skip(connection))] +pub async fn send_mail( + connection: &mut mail_send::Transport<'static, Connected>, + message: MailMessage, +) -> AtomicResult<()> { + let html = if let Some(action) = message.action { + format!( + "{}
{}", + message.body, action.url, action.name + ) + } else { + message.body.clone() + }; + + let builder = MessageBuilder::new() + .from(("Atomic Data", "noreply@atomicdata.dev")) + .to(vec![(message.to.to_string())]) + .subject(message.subject) + .html_body(html) + .text_body(message.body); + info!("Sending mail"); + connection.send(builder).await.map_err(|e| e.to_string())?; + Ok(()) +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn create_and_serialize_email() { + EmailAddress::new("invalid email".into()).unwrap_err(); + let valid = EmailAddress::new("valid@email.com".into()).unwrap(); + assert_eq!(valid.to_string(), "valid@email.com"); + } +} diff --git a/lib/src/endpoints.rs b/lib/src/endpoints.rs index 35f51a160..afebb27e1 100644 --- a/lib/src/endpoints.rs +++ b/lib/src/endpoints.rs @@ -32,6 +32,7 @@ pub struct HandlePostContext<'a> { pub body: Vec, } /// An API endpoint at some path which accepts requests and returns some Resource. +/// Add them by calling [Db::register_endpoint] #[derive(Clone)] pub struct Endpoint { /// The part behind the server domain, e.g. '/versions' or '/collections'. Include the slash. @@ -96,6 +97,7 @@ pub fn default_endpoints() -> Vec { plugins::files::download_endpoint(), plugins::export::export_endpoint(), plugins::register::register_endpoint(), + plugins::register::confirm_email_endpoint(), #[cfg(feature = "html")] plugins::bookmark::bookmark_endpoint(), plugins::importer::import_endpoint(), diff --git a/lib/src/lib.rs b/lib/src/lib.rs index f3ab9a290..913c011c6 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -1,25 +1,31 @@ /*! `atomic_lib` helps you to get, store, serialize, parse and validate Atomic Data. +It's primarily used for powering [Atomic-Server](https://github.com/atomicdata-dev/atomic-data-rust). +Many of the features are optional, which helps us keep the default size small. See the [Atomic Data Docs](https://docs.atomicdata.dev) for more information. ## Features - Two stores for Atomic Data: + - **On disk** [Db], powered by Sled. Indexes filtered queries. (requires `db` feature) - **In-memory** [Store] for getting / setting data. Useful for client applications. - - **On disk** [Db], powered by Sled. Useful for applications that persist Atomic Data, such as [`atomic-server`](https://crates.io/crates/atomic-server). -- [serialize] and [parse] tools for [JSON-AD](https://docs.atomicdata.dev/core/json-ad.html), plain JSON, RDF, Turtle, N-Triples and JSON-LD. +- [parse] and import tools for [JSON-AD](https://docs.atomicdata.dev/core/json-ad.html) +- [serialize] tools for JSON-AD, plain JSON, RDF, Turtle, N-Triples and JSON-LD. - [Resource] with getters, setters and a `.save` function that creates Commits. - [Value] converts Atomic Data to Rust native types -- Validate [Atomic Schema](https://docs.atomicdata.dev/schema/intro.html) -- [Commit]s (transactions / delta's / changes / updates / versioning / history). -- [plugins] system (although not very mature) +- [Commit]s (transactions / delta's / changes / updates / versioning / history). Supports many checks, such as Schema, Authorization and more. - [collections] (pagination, sorting, filtering) -- Querying (using triple pattern fragments) (see [storelike::Query]) -- [plugins::invite] for sharing +- Queries (see [storelike::Query]) - [hierarchy] for authorization - [crate::endpoints::Endpoint] for custom API endpoints -- [config::Config] files. +- [config::Config] files. (requires `config` feature) +- [endpoints] which allow easily adding routes with custom features +- [plugins] system basics. Not very mature, as we still need the code in this repo. (all plugins require `db` feature) +- [plugins::invite] for sharing URLs that grant rights +- [plugins::chatroom] for slack-like group chats. +- [plugins::bookmark] for fetching HTML pages, converting them to markdown, and storing them as Atomic Data (requires `html` feature) +- [plugins::versioning] for constructing previous versions of resources, powered by [Commit]s. ## Getting started @@ -71,6 +77,8 @@ pub mod datatype; #[cfg(feature = "db")] pub mod db; #[cfg(feature = "db")] +pub mod email; +#[cfg(feature = "db")] pub mod endpoints; pub mod errors; pub mod hierarchy; @@ -86,6 +94,8 @@ pub mod store; pub mod storelike; #[cfg(test)] mod test_utils; +#[cfg(feature = "db")] +pub mod token; pub mod urls; pub mod utils; pub mod validate; diff --git a/lib/src/plugins/register.rs b/lib/src/plugins/register.rs index ce781242c..945135f27 100644 --- a/lib/src/plugins/register.rs +++ b/lib/src/plugins/register.rs @@ -1,20 +1,23 @@ //! Creates a new Drive and optionally also an Agent. +use serde::{Deserialize, Serialize}; + use crate::{ agents::Agent, - endpoints::Endpoint, + email::{EmailAddress, MailAction, MailMessage}, + endpoints::{Endpoint, HandleGetContext}, errors::AtomicResult, - urls::{self, PUBLIC_AGENT}, + urls::{self}, values::SubResource, - Resource, Storelike, + Resource, Storelike, Value, }; pub fn register_endpoint() -> Endpoint { Endpoint { path: "/register".to_string(), params: [ - urls::INVITE_PUBKEY.to_string(), urls::NAME.to_string(), + urls::EMAIL.to_string(), ].into(), description: "Allows new users to easily, in one request, make both an Agent and a Drive. This drive will be created at the subdomain of `name`.".to_string(), shortname: "register".to_string(), @@ -23,49 +26,125 @@ pub fn register_endpoint() -> Endpoint { } } -#[tracing::instrument(skip(store))] -pub fn construct_register_redirect( - url: url::Url, - store: &impl Storelike, - for_agent: Option<&str>, -) -> AtomicResult { - let requested_subject = url.to_string(); - let mut pub_key = None; +pub fn confirm_email_endpoint() -> Endpoint { + Endpoint { + path: "/confirmEmail".to_string(), + params: [urls::TOKEN.to_string(), urls::INVITE_PUBKEY.to_string()].into(), + description: "Confirm email address and set a key for your Agent.".to_string(), + shortname: "confirm-email".to_string(), + handle: Some(construct_confirm_email_redirect), + handle_post: None, + } +} + +#[derive(Debug, Serialize, Deserialize)] +struct MailConfirmation { + pub email: EmailAddress, + pub name: String, +} + +#[tracing::instrument()] +pub fn construct_register_redirect(context: HandleGetContext) -> AtomicResult { let mut name_option = None; - for (k, v) in url.query_pairs() { + let mut email_option: Option = None; + let store = context.store; + for (k, v) in context.subject.query_pairs() { match k.as_ref() { - "public-key" | urls::INVITE_PUBKEY => pub_key = Some(v.to_string()), "name" | urls::NAME => name_option = Some(v.to_string()), + "email" => email_option = Some(EmailAddress::new(v.to_string())?), _ => {} } } - if pub_key.is_none() && name_option.is_none() { + // by default just return the Endpoint + if name_option.is_none() && email_option.is_none() { return register_endpoint().to_resource(store); - } + }; + + let name = name_option.ok_or("No name provided")?; + let email = email_option.ok_or("No email provided")?.check_used(store)?; + + // send the user an e-mail to confirm sign up + let store_clone = store.clone(); + let confirmation_token_struct = MailConfirmation { + email: email.clone(), + name: name.clone(), + }; + let token = crate::token::sign_claim(store, confirmation_token_struct)?; + let mut confirm_url = store + .get_server_url() + .clone() + .set_path("confirmEmail") + .url(); + confirm_url.set_query(Some(&format!("token={}", token))); + let message = MailMessage { + to: email, + subject: "Confirm your e-mail address".to_string(), + body: format!("Welcome to Atomic Data, {}. Please confirm your e-mail address by clicking the link below", name), + action: Some(MailAction { + name: "Confirm e-mail address".to_string(), + url: confirm_url.into() + }) + }; + // async, because mails are slow + tokio::spawn(async move { + store_clone + .send_email(message) + .await + .unwrap_or_else(|e| tracing::error!("Error sending email: {}", e)); + }); + + // Here we probably want to return some sort of SuccesMessage page. + // Not sure what that should be. + let mut resource = Resource::new_generate_subject(store); + resource.set_string(urls::DESCRIPTION.into(), "success", store)?; + + // resource.set_propval(urls::, value, store) - let name = if let Some(n) = name_option { - n + Ok(resource) +} + +#[tracing::instrument()] +pub fn construct_confirm_email_redirect(context: HandleGetContext) -> AtomicResult { + let url = context.subject; + let store = context.store; + let mut token_opt: Option = None; + let mut pubkey_option = None; + + for (k, v) in url.query_pairs() { + match k.as_ref() { + "token" | urls::TOKEN => token_opt = Some(v.to_string()), + "public-key" | urls::INVITE_PUBKEY => pubkey_option = Some(v.to_string()), + _ => {} + } + } + let token = if let Some(t) = token_opt { + t } else { - return Err("No name provided".into()); + return confirm_email_endpoint().to_resource(store); }; + let pubkey = pubkey_option.ok_or("No public-key provided")?; - let drive_creator_agent: String = if let Some(key) = pub_key { - let mut new = Agent::new_from_public_key(store, &key)?; - new.name = Some(name.clone()); + // Parse and verify the JWT token + let confirmation = crate::token::verify_claim::(store, &token)?.custom; + + // Create the Agent if it doesn't exist yet. + // Note: this happens before the drive is saved, which checks if the name is available. + // We get new agents that just do nothing, but perhaps that's not a problem. + let drive_creator_agent: String = { + let mut new = Agent::new_from_public_key(store, &pubkey)?; + new.name = Some(confirmation.name.clone()); let net_agent_subject = new.subject.to_string(); new.to_resource()?.save(store)?; net_agent_subject - } else if let Some(agent) = for_agent { - if agent == PUBLIC_AGENT { - return Err("No `public-key` provided.".into()); - } - agent.to_string() - } else { - return Err("No `public-key` provided".into()); }; // Create the new Drive - let drive = crate::populate::create_drive(store, Some(&name), &drive_creator_agent, false)?; + let drive = crate::populate::create_drive( + store, + Some(&confirmation.name), + &drive_creator_agent, + false, + )?; // Add the drive to the Agent's list of drives let mut agent = store.get_resource(&drive_creator_agent)?; @@ -74,6 +153,12 @@ pub fn construct_register_redirect( SubResource::Subject(drive.get_subject().into()), true, )?; + // TODO: Make sure this only works if the server sets the email address. + agent.set( + urls::EMAIL.into(), + Value::String(confirmation.email.to_string()), + store, + )?; agent.save_locally(store)?; // Construct the Redirect Resource, which might provide the Client with a Subject for his Agent. @@ -84,7 +169,5 @@ pub fn construct_register_redirect( crate::Value::AtomicUrl(drive_creator_agent), store, )?; - // The front-end requires the @id to be the same as requested - redirect.set_subject(requested_subject); Ok(redirect) } diff --git a/lib/src/populate.rs b/lib/src/populate.rs index 08d1df169..9117e7edc 100644 --- a/lib/src/populate.rs +++ b/lib/src/populate.rs @@ -154,7 +154,7 @@ pub fn populate_base_models(store: &impl Storelike) -> AtomicResult<()> { } /// Creates a Drive resource at the base URL if no name is passed. -#[tracing::instrument(skip(store), level = "info")] +// #[tracing::instrument(skip(store), level = "info")] pub fn create_drive( store: &impl Storelike, drive_name: Option<&str>, @@ -188,11 +188,7 @@ pub fn create_drive( store.get_resource_new(&drive_subject) }; drive.set_class(urls::DRIVE); - drive.set_string( - urls::NAME.into(), - drive_name.unwrap_or_else(|| self_url.host_str().unwrap()), - store, - )?; + drive.set_string(urls::NAME.into(), drive_name.unwrap_or("New Drive"), store)?; drive.save_locally(store)?; Ok(drive) @@ -338,10 +334,13 @@ pub fn populate_endpoints(store: &crate::Db) -> AtomicResult<()> { /// Adds default Endpoints (versioning) to the Db. /// Makes sure they are fetchable pub fn populate_importer(store: &crate::Db) -> AtomicResult<()> { - let base = store + use crate::urls::IMPORTER; + + let mut base = store .get_self_url() - .ok_or("No self URL in this Store - required for populating importer")?; - let mut importer = crate::Resource::new(urls::construct_path_import(&base)); + .ok_or("No self URL in this Store - required for populating importer")? + .clone(); + let mut importer = crate::Resource::new(base.set_path(IMPORTER).to_string()); importer.set_class(urls::IMPORTER); importer.set( urls::PARENT.into(), @@ -359,13 +358,13 @@ pub fn populate_importer(store: &crate::Db) -> AtomicResult<()> { pub fn populate_sidebar_items(store: &crate::Db) -> AtomicResult<()> { let base = store.get_self_url().ok_or("No self_url")?; let mut drive = store.get_resource(base.as_str())?; - let arr = vec![ + let sidebar_items = vec![ base.set_route(crate::atomic_url::Routes::Setup), base.set_route(crate::atomic_url::Routes::Import), base.set_route(crate::atomic_url::Routes::Collections), ]; - for item in arr { - drive.push(urls::SUBRESOURCES, item.into(), true)?; + for item in sidebar_items { + drive.push(urls::SUBRESOURCES, item.to_string().into(), true)?; } drive.save_locally(store)?; Ok(()) diff --git a/lib/src/store.rs b/lib/src/store.rs index 0f74cb1dc..143c5c7c2 100644 --- a/lib/src/store.rs +++ b/lib/src/store.rs @@ -1,8 +1,6 @@ //! In-memory store of Atomic data. //! This provides many methods for finding, changing, serializing and parsing Atomic Data. -use url::Url; - use crate::agents::Agent; use crate::storelike::QueryResult; use crate::{atomic_url::AtomicUrl, storelike::Storelike}; @@ -189,11 +187,7 @@ impl Storelike for Store { if let Some(resource) = self.hashmap.lock().unwrap().get(subject) { return Ok(resource.clone()); } - self.handle_not_found( - subject, - "Not found in HashMap.".into(), - self.get_default_agent().ok().as_ref(), - ) + self.handle_not_found(subject, "Not found in HashMap.".into()) } fn remove_resource(&self, subject: &str) -> AtomicResult<()> { diff --git a/lib/src/storelike.rs b/lib/src/storelike.rs index 8c8e7a131..70b44e3ba 100644 --- a/lib/src/storelike.rs +++ b/lib/src/storelike.rs @@ -1,5 +1,7 @@ //! The Storelike Trait contains many useful methods for maniupulting / retrieving data. +use tracing::info; + use crate::{ agents::{Agent, ForAgent}, atomic_url::AtomicUrl, @@ -254,6 +256,7 @@ pub trait Storelike: Sized { let self_host = self_url.host().ok_or_else(|| { AtomicError::not_found(format!("Self URL has no host: {}", self_url)) })?; + info!("Comparing hosts: {} and {}", subject_host, self_host); if subject_host == self_host { return Ok(false); } diff --git a/lib/src/token.rs b/lib/src/token.rs new file mode 100644 index 000000000..73f4b0e78 --- /dev/null +++ b/lib/src/token.rs @@ -0,0 +1,79 @@ +//! JWT tokens +//! https://github.com/atomicdata-dev/atomic-data-rust/issues/544 + +use jwt_simple::prelude::*; +use serde::de::DeserializeOwned; +use serde::Serialize; + +use crate::errors::AtomicResult; +use crate::Storelike; + +/// Signs a claim as the Default Agent and creates a JWT token. +pub fn sign_claim( + store: &impl Storelike, + custom_claim: CustomClaims, +) -> AtomicResult { + let key = HS256Key::from_bytes( + store + .get_default_agent()? + .private_key + .ok_or("No private key in default agent, can't sign claims")? + .as_bytes(), + ); + let time = Duration::from_hours(1u64); + let claim = Claims::with_custom_claims(custom_claim, time); + let token = key + .authenticate(claim) + .map_err(|e| format!("fail to create token: {}", e))?; + Ok(token) +} + +/// Parses a JWT token, verifies its hash with the Current Agent and returns the Custom Claims. +pub fn verify_claim( + store: &impl Storelike, + token: &str, +) -> AtomicResult> { + let key = HS256Key::from_bytes( + store + .get_default_agent()? + .private_key + .ok_or("No private key in default agent, can't sign claims")? + .as_bytes(), + ); + let verify_opts = VerificationOptions::default(); + let claims = key + .verify_token(token, Some(verify_opts)) + .map_err(|e| format!("fail to verify token: {}", e))?; + Ok(claims) +} + +#[cfg(test)] +mod tests { + use super::*; + use serde::{Deserialize, Serialize}; + + #[derive(Serialize, Deserialize, Debug)] + struct CustomClaims { + pub name: String, + pub email: String, + } + + #[test] + fn test_sign_claim_store() { + let store = crate::test_utils::init_store(); + let customclaim = CustomClaims { + name: "John Doe".to_string(), + email: "awdaw@adiow.com".to_string(), + }; + let token = sign_claim(&store, customclaim).unwrap(); + assert!(token.starts_with("ey")); + let claim = verify_claim::(&store, &token).unwrap(); + assert!(claim.expires_at.is_some()); + assert_eq!(claim.custom.email, "awdaw@adiow.com".to_string()); + + let malicous_agent = store.create_agent(None).unwrap(); + store.set_default_agent(malicous_agent); + let wrong_claim = verify_claim::(&store, &token); + assert!(wrong_claim.is_err()); + } +} diff --git a/lib/src/urls.rs b/lib/src/urls.rs index 11ba4b0cb..f6b5dac61 100644 --- a/lib/src/urls.rs +++ b/lib/src/urls.rs @@ -57,6 +57,7 @@ pub const LAST_COMMIT: &str = "https://atomicdata.dev/properties/lastCommit"; pub const PUBLIC_KEY: &str = "https://atomicdata.dev/properties/publicKey"; pub const NAME: &str = "https://atomicdata.dev/properties/name"; pub const DRIVES: &str = "https://atomicdata.dev/properties/drives"; +pub const EMAIL: &str = "https://atomicdata.dev/properties/email"; // ... for Collections pub const COLLECTION_PROPERTY: &str = "https://atomicdata.dev/properties/collection/property"; pub const COLLECTION_VALUE: &str = "https://atomicdata.dev/properties/collection/value"; @@ -82,6 +83,7 @@ pub const SEARCH_LIMIT: &str = "https://atomicdata.dev/properties/search/limit"; pub const SEARCH_PROPERTY: &str = "https://atomicdata.dev/properties/search/property"; pub const URL: &str = "https://atomicdata.dev/property/url"; pub const PREVIEW: &str = "https://atomicdata.dev/property/preview"; +pub const TOKEN: &str = "https://atomicdata.dev/property/token"; // ... for Bookmarks pub const IMAGE_URL: &str = "https://atomicdata.dev/properties/imageUrl"; // ... for Hierarchy / Drive @@ -161,8 +163,5 @@ pub const PATH_TPF: &str = "/tpf"; pub const PATH_PATH: &str = "/path"; pub const PATH_COMMITS: &str = "/commits"; pub const PATH_ENDPOINTS: &str = "/endpoints"; - -pub const PATH_IMPORT: &str = "/import"; -pub const PATH_FETCH_BOOKMARK: &str = "/fetch-bookmark"; pub const PATH_QUERY: &str = "/query"; pub const PATH_PRUNE_TESTS: &str = "/prunetests"; diff --git a/server/src/appstate.rs b/server/src/appstate.rs index 60ecb4f34..0c157b84e 100644 --- a/server/src/appstate.rs +++ b/server/src/appstate.rs @@ -6,6 +6,7 @@ use atomic_lib::{ agents::{generate_public_key, Agent}, atomic_url::Routes, commit::CommitResponse, + email::SmtpConfig, Storelike, }; @@ -30,7 +31,7 @@ impl AppState { /// Creates the AppState (the server's context available in Handlers). /// Initializes or opens a store on disk. /// Creates a new agent, if necessary. - pub fn init(config: Config) -> AtomicServerResult { + pub async fn init(config: Config) -> AtomicServerResult { tracing::info!("Initializing AppState"); // We warn over here because tracing needs to be initialized first. @@ -73,6 +74,15 @@ impl AppState { }; store.set_handle_commit(Box::new(send_commit)); + if let Some(host) = &config.opts.smpt_host { + store + .set_smtp_config(SmtpConfig { + host: host.clone(), + port: config.opts.smpt_port, + }) + .await?; + }; + // If the user changes their server_url, the drive will not exist. // In this situation, we should re-build a new drive from scratch. if should_init { diff --git a/server/src/bin.rs b/server/src/bin.rs index dd48aa97a..80537bc5a 100644 --- a/server/src/bin.rs +++ b/server/src/bin.rs @@ -1,4 +1,4 @@ -use atomic_lib::{agents::ForAgent, urls, Storelike}; +use atomic_lib::{agents::ForAgent, atomic_url::Routes, Storelike}; use atomic_server_lib::config::Opts; use std::{fs::File, io::Write}; @@ -48,7 +48,7 @@ async fn main_wrapped() -> errors::AtomicServerResult<()> { pt } }; - let appstate = appstate::AppState::init(config.clone())?; + let appstate = appstate::AppState::init(config.clone()).await?; let outstr = appstate.store.export(!e.only_internal)?; std::fs::create_dir_all(path.parent().unwrap()) .map_err(|e| format!("Failed to create directory {:?}. {}", path, e))?; @@ -64,7 +64,7 @@ async fn main_wrapped() -> errors::AtomicServerResult<()> { std::fs::read_to_string(path)? }; - let appstate = appstate::AppState::init(config.clone())?; + let appstate = appstate::AppState::init(config.clone()).await?; let importer_subject = if let Some(i) = &import_opts.parent { i.into() } else { diff --git a/server/src/handlers/upload.rs b/server/src/handlers/upload.rs index c3b821546..475796cb5 100644 --- a/server/src/handlers/upload.rs +++ b/server/src/handlers/upload.rs @@ -34,14 +34,8 @@ pub async fn upload_handler( let store = &appstate.store; let parent = store.get_resource(&query.parent)?; let subject = get_subject(&req, &conn, &appstate)?; - if let Some(agent) = get_client_agent(req.headers(), &appstate, subject)? { - check_write(store, &parent, &agent)?; - } else { - return Err(AtomicError::unauthorized( - "No authorization headers present. These are required when uploading files.".into(), - ) - .into()); - } + let for_agent = get_client_agent(req.headers(), &appstate, subject)?; + check_write(store, &parent, &for_agent)?; let mut created_resources: Vec = Vec::new(); diff --git a/server/src/helpers.rs b/server/src/helpers.rs index 994de8593..ef3de0a70 100644 --- a/server/src/helpers.rs +++ b/server/src/helpers.rs @@ -10,6 +10,7 @@ use atomic_lib::Storelike; use percent_encoding::percent_decode_str; use std::str::FromStr; +use crate::content_types::ContentType; use crate::errors::{AppErrorType, AtomicServerError}; use crate::{appstate::AppState, errors::AtomicServerResult}; diff --git a/server/src/serve.rs b/server/src/serve.rs index 9f7440375..0fddb6613 100644 --- a/server/src/serve.rs +++ b/server/src/serve.rs @@ -38,7 +38,7 @@ pub async fn serve(config: crate::config::Config) -> AtomicServerResult<()> { let tracing_chrome_flush_guard = crate::trace::init_tracing(&config); // Setup the database and more - let appstate = crate::appstate::AppState::init(config.clone())?; + let appstate = crate::appstate::AppState::init(config.clone()).await?; // Start async processes if config.opts.rebuild_indexes { diff --git a/server/src/tests.rs b/server/src/tests.rs index f3a9f9170..cfd3b33dd 100644 --- a/server/src/tests.rs +++ b/server/src/tests.rs @@ -51,7 +51,9 @@ async fn server_tests() { // This prevents folder access issues when running concurrent tests config.search_index_path = format!("./.temp/{}/search_index", unique_string).into(); - let appstate = crate::appstate::AppState::init(config.clone()).expect("failed init appstate"); + let appstate = crate::appstate::AppState::init(config.clone()) + .await + .expect("failed to init appstate"); let data = Data::new(appstate.clone()); let app = test::init_service( App::new() From 48214edbe336c121608448f8cc394b73f1cd68c1 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:33:00 +0200 Subject: [PATCH 16/51] WIP --- lib/src/atomic_url.rs | 2 +- lib/src/plugins/mod.rs | 1 + lib/src/plugins/register.rs | 19 +++++---- lib/src/plugins/reset_pubkey.rs | 70 +++++++++++++++++++++++++++++++++ lib/src/storelike.rs | 17 +++++++- lib/src/urls.rs | 4 ++ server/src/handlers/commit.rs | 2 +- 7 files changed, 104 insertions(+), 11 deletions(-) create mode 100644 lib/src/plugins/reset_pubkey.rs diff --git a/lib/src/atomic_url.rs b/lib/src/atomic_url.rs index f6c7d84da..5afc0b32e 100644 --- a/lib/src/atomic_url.rs +++ b/lib/src/atomic_url.rs @@ -112,7 +112,7 @@ impl AtomicUrl { } /// Removes existing path, sets the new one. Escapes special characters - pub fn set_path(&mut self, path: &str) -> &Self { + pub fn set_path(mut self, path: &str) -> Self { self.url.set_path(path); self } diff --git a/lib/src/plugins/mod.rs b/lib/src/plugins/mod.rs index 02f79c5b4..691a17fef 100644 --- a/lib/src/plugins/mod.rs +++ b/lib/src/plugins/mod.rs @@ -47,5 +47,6 @@ pub mod path; pub mod prunetests; pub mod query; pub mod register; +pub mod reset_pubkey; pub mod search; pub mod versioning; diff --git a/lib/src/plugins/register.rs b/lib/src/plugins/register.rs index 945135f27..57a940a2c 100644 --- a/lib/src/plugins/register.rs +++ b/lib/src/plugins/register.rs @@ -14,7 +14,7 @@ use crate::{ pub fn register_endpoint() -> Endpoint { Endpoint { - path: "/register".to_string(), + path: urls::PATH_REGISTER.to_string(), params: [ urls::NAME.to_string(), urls::EMAIL.to_string(), @@ -28,7 +28,7 @@ pub fn register_endpoint() -> Endpoint { pub fn confirm_email_endpoint() -> Endpoint { Endpoint { - path: "/confirmEmail".to_string(), + path: urls::PATH_CONFIRM_EMAIL.to_string(), params: [urls::TOKEN.to_string(), urls::INVITE_PUBKEY.to_string()].into(), description: "Confirm email address and set a key for your Agent.".to_string(), shortname: "confirm-email".to_string(), @@ -110,6 +110,7 @@ pub fn construct_confirm_email_redirect(context: HandleGetContext) -> AtomicResu let mut token_opt: Option = None; let mut pubkey_option = None; + println!("url: {:?}", url); for (k, v) in url.query_pairs() { match k.as_ref() { "token" | urls::TOKEN => token_opt = Some(v.to_string()), @@ -131,11 +132,15 @@ pub fn construct_confirm_email_redirect(context: HandleGetContext) -> AtomicResu // Note: this happens before the drive is saved, which checks if the name is available. // We get new agents that just do nothing, but perhaps that's not a problem. let drive_creator_agent: String = { - let mut new = Agent::new_from_public_key(store, &pubkey)?; - new.name = Some(confirmation.name.clone()); - let net_agent_subject = new.subject.to_string(); - new.to_resource()?.save(store)?; - net_agent_subject + let mut new_agent = Agent::new_from_public_key(store, &pubkey)?; + new_agent.name = Some(confirmation.name.clone()); + let net_agent_subject = store + .get_server_url() + .clone() + .set_path(&format!("agents/{}", confirmation.name)); + new_agent.subject = net_agent_subject.to_string(); + new_agent.to_resource()?.save(store)?; + net_agent_subject.to_string() }; // Create the new Drive diff --git a/lib/src/plugins/reset_pubkey.rs b/lib/src/plugins/reset_pubkey.rs new file mode 100644 index 000000000..55ae3fad6 --- /dev/null +++ b/lib/src/plugins/reset_pubkey.rs @@ -0,0 +1,70 @@ +/*! +Reset email +*/ + +use crate::{endpoints::Endpoint, errors::AtomicResult, urls, Db, Resource}; + +pub fn request_email_pubkey_reset() -> Endpoint { + Endpoint { + path: urls::PATH_RESET_PUBKEY.to_string(), + params: [urls::TOKEN.to_string(), urls::INVITE_PUBKEY.to_string()].into(), + description: "Requests an email to set a new PublicKey to an Agent.".to_string(), + shortname: "request-pubkey-reset".to_string(), + handle: Some(construct_reset_pubkey), + } +} + +pub fn confirm_pubkey_reset() -> Endpoint { + Endpoint { + path: urls::PATH_CONFIRM_RESET.to_string(), + params: [urls::TOKEN.to_string(), urls::INVITE_PUBKEY.to_string()].into(), + description: "Requests an email to set a new PublicKey to an Agent.".to_string(), + shortname: "request-pubkey-reset".to_string(), + handle: Some(construct_confirm_reset_pubkey), + } +} + +#[tracing::instrument(skip(store))] +pub fn construct_confirm_reset_pubkey( + url: url::Url, + store: &Db, + for_agent: Option<&str>, +) -> AtomicResult { + let mut token_opt: Option = None; + let mut pubkey_option = None; + + println!("url: {:?}", url); + for (k, v) in url.query_pairs() { + match k.as_ref() { + "token" | urls::TOKEN => token_opt = Some(v.to_string()), + "public-key" | urls::INVITE_PUBKEY => pubkey_option = Some(v.to_string()), + _ => {} + } + } + let Some(token) = token_opt else { + return confirm_pubkey_reset().to_resource(store); + }; + let pubkey = pubkey_option.ok_or("No public-key provided")?; + + // Parse and verify the JWT token + let confirmation = crate::token::verify_claim::(store, &token)?.custom; + + // Add the drive to the Agent's list of drives + let mut agent = store.get_resource(&drive_creator_agent)?; + agent.push_propval( + urls::USED_PUBKEYS.into(), + SubResource::Subject(drive.get_subject().into()), + true, + )?; + agent.save_locally(store)?; + + // Construct the Redirect Resource, which might provide the Client with a Subject for his Agent. + let mut redirect = Resource::new_instance(urls::REDIRECT, store)?; + redirect.set_propval_string(urls::DESTINATION.into(), drive.get_subject(), store)?; + redirect.set_propval( + urls::REDIRECT_AGENT.into(), + crate::Value::AtomicUrl(drive_creator_agent), + store, + )?; + Ok(redirect) +} diff --git a/lib/src/storelike.rs b/lib/src/storelike.rs index 70b44e3ba..cf44a62e3 100644 --- a/lib/src/storelike.rs +++ b/lib/src/storelike.rs @@ -248,6 +248,7 @@ pub trait Storelike: Sized { if subject.starts_with(self_url.as_str()) { return Ok(false); } else { + // Is it a subdomain of the self_url? let subject_url = url::Url::parse(subject)?; let subject_host = subject_url.host().ok_or_else(|| { AtomicError::not_found(format!("Subject URL has no host: {}", subject)) @@ -256,8 +257,20 @@ pub trait Storelike: Sized { let self_host = self_url.host().ok_or_else(|| { AtomicError::not_found(format!("Self URL has no host: {}", self_url)) })?; - info!("Comparing hosts: {} and {}", subject_host, self_host); - if subject_host == self_host { + // remove the subdomain from subject, if any. + // The server can have multiple subdomains + let subject_host_string = subject_host.to_string(); + let subject_host_parts = subject_host_string.split('.').collect::>(); + + // Check if the last part of the host is equal + let Some(subject_host_stripped) = subject_host_parts.last() else { + return Ok(false) + }; + info!( + "Comparing hosts: {} and {}", + subject_host_stripped, self_host + ); + if subject_host_stripped == &self_host.to_string() { return Ok(false); } } diff --git a/lib/src/urls.rs b/lib/src/urls.rs index f6b5dac61..1666fe55b 100644 --- a/lib/src/urls.rs +++ b/lib/src/urls.rs @@ -163,5 +163,9 @@ pub const PATH_TPF: &str = "/tpf"; pub const PATH_PATH: &str = "/path"; pub const PATH_COMMITS: &str = "/commits"; pub const PATH_ENDPOINTS: &str = "/endpoints"; +pub const PATH_REGISTER: &str = "/register"; +pub const PATH_CONFIRM_EMAIL: &str = "/confirm-email"; +pub const PATH_RESET_PUBKEY: &str = "/reset-public-key"; +pub const PATH_CONFIRM_RESET: &str = "/confirm-reset-public-key"; pub const PATH_QUERY: &str = "/query"; pub const PATH_PRUNE_TESTS: &str = "/prunetests"; diff --git a/server/src/handlers/commit.rs b/server/src/handlers/commit.rs index 79f53bbd3..10e3a9e77 100644 --- a/server/src/handlers/commit.rs +++ b/server/src/handlers/commit.rs @@ -19,7 +19,7 @@ pub async fn post_commit( let mut builder = HttpResponse::Ok(); let incoming_commit_resource = parse_json_ad_commit_resource(&body, store)?; let incoming_commit = Commit::from_resource(incoming_commit_resource)?; - if store.is_external_subject(&incoming_commit.subject)? { +if store.is_external_subject(&incoming_commit.subject)? { return Err("Subject of commit is external, and should be sent to its origin domain. This store can not own this resource. See https://github.com/atomicdata-dev/atomic-data-rust/issues/509".into()); } let opts = CommitOpts { From 156ab34a9a4a969df2b499e4bf0d7dfdf5a2231f Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:33:00 +0200 Subject: [PATCH 17/51] Public key reset endpoint WIP --- lib/src/agents.rs | 37 ++++++++- lib/src/collections.rs | 6 +- lib/src/db.rs | 11 ++- lib/src/db/query_index.rs | 8 +- lib/src/email.rs | 2 +- lib/src/hierarchy.rs | 2 +- lib/src/lib.rs | 4 +- lib/src/plugins/chatroom.rs | 3 +- lib/src/plugins/mod.rs | 3 + lib/src/plugins/prunetests.rs | 3 +- lib/src/plugins/register.rs | 24 +++--- lib/src/plugins/reset_pubkey.rs | 138 +++++++++++++++++++++++--------- lib/src/plugins/utils.rs | 10 +++ lib/src/plugins/versioning.rs | 3 +- lib/src/populate.rs | 28 +------ lib/src/query.rs | 75 +++++++++++++++++ lib/src/resources.rs | 3 +- lib/src/store.rs | 12 +-- lib/src/storelike.rs | 86 ++------------------ lib/src/urls.rs | 7 ++ 20 files changed, 273 insertions(+), 192 deletions(-) create mode 100644 lib/src/plugins/utils.rs create mode 100644 lib/src/query.rs diff --git a/lib/src/agents.rs b/lib/src/agents.rs index 927343162..a353e4b9d 100644 --- a/lib/src/agents.rs +++ b/lib/src/agents.rs @@ -5,7 +5,7 @@ use base64::{engine::general_purpose, Engine}; use serde_json::from_slice; -use crate::{errors::AtomicResult, urls, Resource, Storelike, Value}; +use crate::{errors::AtomicResult, urls, Query, Resource, Storelike, Value}; /// None represents no right checks will be performed, effectively SUDO mode. #[derive(Clone, Debug, PartialEq)] @@ -81,6 +81,41 @@ impl Agent { Ok(resource) } + pub fn from_email(email: &str, store: &impl Storelike) -> AtomicResult { + let mut query = Query::new(); + query.property = Some(urls::EMAIL.into()); + query.value = Some(Value::String(email.to_string())); + let response = store.query(&query)?; + if response.resources.is_empty() { + return Err(format!("Agent with Email {} not found", email).into()); + } + if response.resources.len() > 1 { + return Err(format!( + "Email {} is not unique, {} agents have this email", + email, response.count + ) + .into()); + } + let resource = response.resources.first().unwrap(); + Agent::from_resource(resource.clone()) + } + + pub fn from_resource(resource: Resource) -> AtomicResult { + let name = if let Ok(name) = resource.get(urls::NAME) { + Some(name.to_string()) + } else { + None + }; + + return Ok(Self { + created_at: resource.get(urls::CREATED_AT)?.to_int()?, + name, + public_key: resource.get(urls::PUBLIC_KEY)?.to_string(), + private_key: None, + subject: resource.get_subject().into(), + }); + } + /// Creates a new Agent, generates a new Keypair. pub fn new(name: Option<&str>, store: &impl Storelike) -> AtomicResult { let keypair = generate_keypair()?; diff --git a/lib/src/collections.rs b/lib/src/collections.rs index 0e767748a..74783b6a1 100644 --- a/lib/src/collections.rs +++ b/lib/src/collections.rs @@ -1,10 +1,8 @@ //! Collections are dynamic resources that refer to multiple resources. //! They are constructed using a [Query] use crate::{ - agents::ForAgent, - errors::AtomicResult, - storelike::{Query, ResourceCollection}, - urls, Resource, Storelike, Value, + agents::ForAgent, errors::AtomicResult, storelike::ResourceCollection, urls, Query, Resource, + Storelike, Value, }; const DEFAULT_PAGE_SIZE: usize = 30; diff --git a/lib/src/db.rs b/lib/src/db.rs index 3660058f8..08b985c5b 100644 --- a/lib/src/db.rs +++ b/lib/src/db.rs @@ -26,17 +26,17 @@ use crate::{ agents::ForAgent, atomic_url::AtomicUrl, atoms::IndexAtom, - commit::CommitOpts, - commit::CommitResponse, + commit::{CommitOpts, CommitResponse}, db::{query_index::requires_query_index, val_prop_sub_index::find_in_val_prop_sub_index}, email::{self, MailMessage}, endpoints::{default_endpoints, Endpoint, HandleGetContext}, errors::{AtomicError, AtomicResult}, + query::QueryResult, resources::PropVals, - storelike::{Query, QueryResult, Storelike}, + storelike::Storelike, urls, values::SortableValue, - Atom, Commit, Resource, + Atom, Commit, Query, Resource, }; use self::{ @@ -511,8 +511,7 @@ impl Db { "No SMTP client configured. Please call set_smtp_config first.".into(), ) })? - .lock() - .await; + .lock()?; email::send_mail(&mut client, message).await?; Ok(()) } diff --git a/lib/src/db/query_index.rs b/lib/src/db/query_index.rs index 0066b0b64..1bab4b761 100644 --- a/lib/src/db/query_index.rs +++ b/lib/src/db/query_index.rs @@ -2,8 +2,8 @@ //! It relies on lexicographic ordering of keys, which Sled utilizes using `scan_prefix` queries. use crate::{ - agents::ForAgent, atoms::IndexAtom, errors::AtomicResult, storelike::Query, - values::SortableValue, Atom, Db, Resource, Storelike, Value, + agents::ForAgent, atoms::IndexAtom, errors::AtomicResult, values::SortableValue, Atom, Db, + Query, Resource, Storelike, Value, }; use serde::{Deserialize, Serialize}; @@ -101,10 +101,6 @@ pub fn query_sorted_indexed( let mut resources: Vec = vec![]; let mut count = 0; - let self_url = store - .get_self_url() - .ok_or("No self_url set, required for Queries")?; - let limit = q.limit.unwrap_or(usize::MAX); for (i, kv) in iter.enumerate() { diff --git a/lib/src/email.rs b/lib/src/email.rs index 764fd1b67..aa2d03149 100644 --- a/lib/src/email.rs +++ b/lib/src/email.rs @@ -1,6 +1,6 @@ //! [EmailAddress] with validation, [MailMessage] with sending, and [get_smtp_client] for setting up mail. -use crate::{errors::AtomicResult, storelike::Query, urls, Storelike}; +use crate::{errors::AtomicResult, urls, Query, Storelike}; use mail_send::{mail_builder::MessageBuilder, Connected, Transport}; use serde::{Deserialize, Serialize}; use tracing::info; diff --git a/lib/src/hierarchy.rs b/lib/src/hierarchy.rs index 0eb1f92d6..42a3b733c 100644 --- a/lib/src/hierarchy.rs +++ b/lib/src/hierarchy.rs @@ -4,7 +4,7 @@ use core::fmt; -use crate::{agents::ForAgent, errors::AtomicResult, storelike::Query, urls, Resource, Storelike}; +use crate::{agents::ForAgent, errors::AtomicResult, urls, Query, Resource, Storelike}; #[derive(Debug)] pub enum Right { diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 913c011c6..d6b3c38f3 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -16,7 +16,7 @@ See the [Atomic Data Docs](https://docs.atomicdata.dev) for more information. - [Value] converts Atomic Data to Rust native types - [Commit]s (transactions / delta's / changes / updates / versioning / history). Supports many checks, such as Schema, Authorization and more. - [collections] (pagination, sorting, filtering) -- Queries (see [storelike::Query]) +- Queries (see [query::Query]) - [hierarchy] for authorization - [crate::endpoints::Endpoint] for custom API endpoints - [config::Config] files. (requires `config` feature) @@ -87,6 +87,7 @@ pub mod parse; #[cfg(feature = "db")] pub mod plugins; pub mod populate; +pub mod query; pub mod resources; pub mod schema; pub mod serialize; @@ -108,6 +109,7 @@ pub use commit::Commit; pub use db::Db; pub use errors::AtomicError; pub use errors::AtomicErrorType; +pub use query::Query; pub use resources::Resource; pub use store::Store; pub use storelike::Storelike; diff --git a/lib/src/plugins/chatroom.rs b/lib/src/plugins/chatroom.rs index 45352abb7..f9aeb4944 100644 --- a/lib/src/plugins/chatroom.rs +++ b/lib/src/plugins/chatroom.rs @@ -8,9 +8,8 @@ use crate::{ agents::ForAgent, commit::{CommitBuilder, CommitOpts}, errors::AtomicResult, - storelike::Query, urls::{self, PARENT}, - utils, Resource, Storelike, Value, + utils, Query, Resource, Storelike, Value, }; // Find the messages for the ChatRoom diff --git a/lib/src/plugins/mod.rs b/lib/src/plugins/mod.rs index 691a17fef..4e54f7351 100644 --- a/lib/src/plugins/mod.rs +++ b/lib/src/plugins/mod.rs @@ -50,3 +50,6 @@ pub mod register; pub mod reset_pubkey; pub mod search; pub mod versioning; + +// Utilities / helpers +mod utils; diff --git a/lib/src/plugins/prunetests.rs b/lib/src/plugins/prunetests.rs index c167d854c..7a4a4f906 100644 --- a/lib/src/plugins/prunetests.rs +++ b/lib/src/plugins/prunetests.rs @@ -1,8 +1,7 @@ use crate::{ endpoints::{Endpoint, HandleGetContext, HandlePostContext}, errors::AtomicResult, - storelike::Query, - urls, Resource, Storelike, Value, + urls, Query, Resource, Storelike, Value, }; pub fn prune_tests_endpoint() -> Endpoint { diff --git a/lib/src/plugins/register.rs b/lib/src/plugins/register.rs index 57a940a2c..403f4ae47 100644 --- a/lib/src/plugins/register.rs +++ b/lib/src/plugins/register.rs @@ -12,6 +12,8 @@ use crate::{ Resource, Storelike, Value, }; +use super::utils::return_success; + pub fn register_endpoint() -> Endpoint { Endpoint { path: urls::PATH_REGISTER.to_string(), @@ -21,8 +23,8 @@ pub fn register_endpoint() -> Endpoint { ].into(), description: "Allows new users to easily, in one request, make both an Agent and a Drive. This drive will be created at the subdomain of `name`.".to_string(), shortname: "register".to_string(), - handle: Some(construct_register_redirect), handle_post: None, + handle: Some(handle_register_name_and_email), } } @@ -43,12 +45,17 @@ struct MailConfirmation { pub name: String, } -#[tracing::instrument()] -pub fn construct_register_redirect(context: HandleGetContext) -> AtomicResult { +#[tracing::instrument] +pub fn handle_register_name_and_email(context: HandleGetContext) -> AtomicResult { + let HandleGetContext { + subject, + store: _, + for_agent: _, + } = context; let mut name_option = None; let mut email_option: Option = None; let store = context.store; - for (k, v) in context.subject.query_pairs() { + for (k, v) in subject.query_pairs() { match k.as_ref() { "name" | urls::NAME => name_option = Some(v.to_string()), "email" => email_option = Some(EmailAddress::new(v.to_string())?), @@ -93,14 +100,7 @@ pub fn construct_register_redirect(context: HandleGetContext) -> AtomicResult Endpoint { +use crate::{ + agents::Agent, + email::{EmailAddress, MailAction, MailMessage}, + endpoints::{Endpoint, HandleGetContext}, + errors::AtomicResult, + plugins::utils::return_success, + urls, Resource, Storelike, +}; + +pub fn request_email_add_pubkey() -> Endpoint { Endpoint { - path: urls::PATH_RESET_PUBKEY.to_string(), + path: urls::PATH_ADD_PUBKEY.to_string(), params: [urls::TOKEN.to_string(), urls::INVITE_PUBKEY.to_string()].into(), - description: "Requests an email to set a new PublicKey to an Agent.".to_string(), - shortname: "request-pubkey-reset".to_string(), - handle: Some(construct_reset_pubkey), + description: "Requests an email to add a new PublicKey to an Agent.".to_string(), + shortname: "pubkey-add".to_string(), + handle: Some(handle_request_email_pubkey), + handle_post: None, } } -pub fn confirm_pubkey_reset() -> Endpoint { +pub fn confirm_add_pubkey() -> Endpoint { Endpoint { - path: urls::PATH_CONFIRM_RESET.to_string(), + path: urls::PATH_CONFIRM_PUBKEY.to_string(), params: [urls::TOKEN.to_string(), urls::INVITE_PUBKEY.to_string()].into(), - description: "Requests an email to set a new PublicKey to an Agent.".to_string(), + description: "Confirms a token to add a new Public Key.".to_string(), shortname: "request-pubkey-reset".to_string(), - handle: Some(construct_confirm_reset_pubkey), + handle: Some(handle_confirm_add_pubkey), + handle_post: None, + } +} + +#[derive(Debug, Serialize, Deserialize)] +struct AddPubkeyToken { + agent: String, +} + +pub fn handle_request_email_pubkey(context: HandleGetContext) -> AtomicResult { + let HandleGetContext { + subject, + store, + for_agent: _, + } = context; + let mut email_option: Option = None; + for (k, v) in subject.query_pairs() { + match k.as_ref() { + "email" => email_option = Some(EmailAddress::new(v.to_string())?), + _ => {} + } } + // by default just return the Endpoint + let Some(email) = email_option else { + return request_email_add_pubkey().to_resource(store); + }; + + // Find the agent by their email + let agent = Agent::from_email(&email.to_string(), store)?; + + // send the user an e-mail to confirm sign up + let store_clone = store.clone(); + let confirmation_token_struct = AddPubkeyToken { + agent: agent.subject, + }; + let token = crate::token::sign_claim(store, confirmation_token_struct)?; + let mut confirm_url = store + .get_server_url() + .clone() + .set_path(urls::PATH_CONFIRM_PUBKEY) + .url(); + confirm_url.set_query(Some(&format!("token={}", token))); + let message = MailMessage { + to: email, + subject: "Add a new Passphrase to your account".to_string(), + body: "You've requested adding a new Passphrase. Click the link below to do so!" + .to_string(), + action: Some(MailAction { + name: "Add new Passphrase to account".to_string(), + url: confirm_url.into(), + }), + }; + // async, because mails are slow + tokio::spawn(async move { + store_clone + .send_email(message) + .await + .unwrap_or_else(|e| tracing::error!("Error sending email: {}", e)); + }); + + return_success() } -#[tracing::instrument(skip(store))] -pub fn construct_confirm_reset_pubkey( - url: url::Url, - store: &Db, - for_agent: Option<&str>, -) -> AtomicResult { +#[tracing::instrument] +pub fn handle_confirm_add_pubkey(context: HandleGetContext) -> AtomicResult { + let HandleGetContext { + subject, + store, + for_agent: _, + } = context; let mut token_opt: Option = None; let mut pubkey_option = None; - println!("url: {:?}", url); - for (k, v) in url.query_pairs() { + for (k, v) in subject.query_pairs() { match k.as_ref() { "token" | urls::TOKEN => token_opt = Some(v.to_string()), "public-key" | urls::INVITE_PUBKEY => pubkey_option = Some(v.to_string()), _ => {} } } + let pubkey = pubkey_option.ok_or("No public-key provided")?; + let Some(token) = token_opt else { - return confirm_pubkey_reset().to_resource(store); + return confirm_add_pubkey().to_resource(store); }; - let pubkey = pubkey_option.ok_or("No public-key provided")?; // Parse and verify the JWT token - let confirmation = crate::token::verify_claim::(store, &token)?.custom; + let confirmation = crate::token::verify_claim::(store, &token)?.custom; - // Add the drive to the Agent's list of drives - let mut agent = store.get_resource(&drive_creator_agent)?; - agent.push_propval( - urls::USED_PUBKEYS.into(), - SubResource::Subject(drive.get_subject().into()), - true, - )?; - agent.save_locally(store)?; + // Add the key to the agent + let mut agent = store.get_resource(&confirmation.agent)?; + agent.push(urls::ACTIVE_KEYS, pubkey.into(), true)?; + agent.save(store)?; - // Construct the Redirect Resource, which might provide the Client with a Subject for his Agent. - let mut redirect = Resource::new_instance(urls::REDIRECT, store)?; - redirect.set_propval_string(urls::DESTINATION.into(), drive.get_subject(), store)?; - redirect.set_propval( - urls::REDIRECT_AGENT.into(), - crate::Value::AtomicUrl(drive_creator_agent), - store, - )?; - Ok(redirect) + return_success() } diff --git a/lib/src/plugins/utils.rs b/lib/src/plugins/utils.rs new file mode 100644 index 000000000..642bbb9af --- /dev/null +++ b/lib/src/plugins/utils.rs @@ -0,0 +1,10 @@ +//! Functions that can be valuable in multiple plugins + +use crate::{errors::AtomicResult, urls, Resource, Value}; + +/// Returns a Resource with a description of "success" +pub fn return_success() -> AtomicResult { + let mut resource = Resource::new("unknown".into()); + resource.set_unsafe(urls::DESCRIPTION.into(), Value::String("success".into())); + Ok(resource) +} diff --git a/lib/src/plugins/versioning.rs b/lib/src/plugins/versioning.rs index 8ee57fd8b..78725e9b4 100644 --- a/lib/src/plugins/versioning.rs +++ b/lib/src/plugins/versioning.rs @@ -5,8 +5,7 @@ use crate::{ collections::CollectionBuilder, endpoints::{Endpoint, HandleGetContext}, errors::AtomicResult, - storelike::Query, - urls, AtomicError, Commit, Resource, Storelike, + urls, AtomicError, Commit, Query, Resource, Storelike, }; pub fn version_endpoint() -> Endpoint { diff --git a/lib/src/populate.rs b/lib/src/populate.rs index 9117e7edc..466dd2d3e 100644 --- a/lib/src/populate.rs +++ b/lib/src/populate.rs @@ -8,8 +8,7 @@ use crate::{ errors::AtomicResult, parse::ParseOpts, schema::{Class, Property}, - storelike::Query, - urls, Resource, Storelike, Value, + urls, Query, Resource, Storelike, Value, }; const DEFAULT_ONTOLOGY_PATH: &str = "defaultOntology"; @@ -303,7 +302,7 @@ pub fn populate_collections(store: &impl Storelike) -> AtomicResult<()> { for subject in result.subjects { let mut collection = - crate::collections::create_collection_resource_for_class(store, &subject)?; + crate::collections::create_collection_resource_for_class(store, &subject.to_string())?; collection.save_locally(store)?; } @@ -330,28 +329,6 @@ pub fn populate_endpoints(store: &crate::Db) -> AtomicResult<()> { Ok(()) } -#[cfg(feature = "db")] -/// Adds default Endpoints (versioning) to the Db. -/// Makes sure they are fetchable -pub fn populate_importer(store: &crate::Db) -> AtomicResult<()> { - use crate::urls::IMPORTER; - - let mut base = store - .get_self_url() - .ok_or("No self URL in this Store - required for populating importer")? - .clone(); - let mut importer = crate::Resource::new(base.set_path(IMPORTER).to_string()); - importer.set_class(urls::IMPORTER); - importer.set( - urls::PARENT.into(), - Value::AtomicUrl(base.to_string()), - store, - )?; - importer.set(urls::NAME.into(), Value::String("Import".into()), store)?; - importer.save_locally(store)?; - Ok(()) -} - #[cfg(feature = "db")] /// Adds items to the SideBar as subresources. /// Useful for helping a new user get started. @@ -360,7 +337,6 @@ pub fn populate_sidebar_items(store: &crate::Db) -> AtomicResult<()> { let mut drive = store.get_resource(base.as_str())?; let sidebar_items = vec![ base.set_route(crate::atomic_url::Routes::Setup), - base.set_route(crate::atomic_url::Routes::Import), base.set_route(crate::atomic_url::Routes::Collections), ]; for item in sidebar_items { diff --git a/lib/src/query.rs b/lib/src/query.rs new file mode 100644 index 000000000..4607f2b83 --- /dev/null +++ b/lib/src/query.rs @@ -0,0 +1,75 @@ +use crate::{agents::ForAgent, urls, Resource, Value}; + +/// Use this to construct a list of Resources +#[derive(Debug)] +pub struct Query { + /// Filter by Property + pub property: Option, + /// Filter by Value + pub value: Option, + /// Maximum of items to return + pub limit: Option, + /// Value at which to begin lexicographically sorting things. + pub start_val: Option, + /// Value at which to stop lexicographically sorting things. + pub end_val: Option, + /// How many items to skip from the first one + pub offset: usize, + /// The Property URL that is used to sort the results + pub sort_by: Option, + /// Sort descending instead of ascending. + pub sort_desc: bool, + /// Whether to include non-server resources + pub include_external: bool, + /// Whether to include full Resources in the result, if not, will add empty vector here. + pub include_nested: bool, + /// For which Agent the query is executed. Pass `None` if you want to skip permission checks. + pub for_agent: ForAgent, +} + +impl Query { + pub fn new() -> Self { + Query { + property: None, + value: None, + limit: None, + start_val: None, + end_val: None, + offset: 0, + sort_by: None, + sort_desc: false, + include_external: false, + include_nested: true, + for_agent: ForAgent::Sudo, + } + } + + /// Search for a property-value combination + pub fn new_prop_val(prop: &str, val: &str) -> Self { + let mut q = Self::new(); + q.property = Some(prop.to_string()); + q.value = Some(Value::String(val.to_string())); + q + } + + /// Search for instances of some Class + pub fn new_class(class: &str) -> Self { + let mut q = Self::new(); + q.property = Some(urls::IS_A.into()); + q.value = Some(Value::AtomicUrl(class.to_string())); + q + } +} + +impl Default for Query { + fn default() -> Self { + Self::new() + } +} + +pub struct QueryResult { + pub subjects: Vec, + pub resources: Vec, + /// The amount of hits that were found, including the ones that were out of bounds or not authorized. + pub count: usize, +} diff --git a/lib/src/resources.rs b/lib/src/resources.rs index 3a711eed3..b0c6e5d44 100644 --- a/lib/src/resources.rs +++ b/lib/src/resources.rs @@ -2,8 +2,6 @@ //! Has methods for saving resources and getting properties inside them. use crate::commit::{CommitOpts, CommitResponse}; -use crate::storelike::Query; -use crate::urls; use crate::utils::random_string; use crate::values::{SubResource, Value}; use crate::{commit::CommitBuilder, errors::AtomicResult}; @@ -12,6 +10,7 @@ use crate::{ schema::{Class, Property}, Atom, Storelike, }; +use crate::{urls, Query}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use tracing::instrument; diff --git a/lib/src/store.rs b/lib/src/store.rs index 143c5c7c2..89310e3b0 100644 --- a/lib/src/store.rs +++ b/lib/src/store.rs @@ -2,10 +2,10 @@ //! This provides many methods for finding, changing, serializing and parsing Atomic Data. use crate::agents::Agent; -use crate::storelike::QueryResult; +use crate::query::QueryResult; use crate::{atomic_url::AtomicUrl, storelike::Storelike}; use crate::{errors::AtomicResult, Resource}; -use crate::{Atom, Value}; +use crate::{Atom, Query, Value}; use std::{collections::HashMap, sync::Arc, sync::Mutex}; /// The in-memory store of data, containing the Resources, Properties and Classes @@ -17,12 +17,8 @@ pub struct Store { default_agent: Arc>>, } -/// The URL used for stores that are not accessible on the web. -// I'd prefer this to a non-HTTP URI, but that causes parsing issues when we combine it with some paths (at least with Commits) -pub const LOCAL_STORE_URL_STR: &str = "http://noresolve.localhost"; - lazy_static::lazy_static! { - static ref LOCAL_STORE_URL: AtomicUrl = AtomicUrl::try_from(LOCAL_STORE_URL_STR).unwrap(); + static ref LOCAL_STORE_URL: AtomicUrl = AtomicUrl::try_from(crate::urls::LOCAL_STORE).unwrap(); } impl Store { @@ -206,7 +202,7 @@ impl Storelike for Store { self.default_agent.lock().unwrap().replace(agent); } - fn query(&self, q: &crate::storelike::Query) -> AtomicResult { + fn query(&self, q: &Query) -> AtomicResult { let atoms = self.tpf( None, q.property.as_deref(), diff --git a/lib/src/storelike.rs b/lib/src/storelike.rs index cf44a62e3..6061d03c7 100644 --- a/lib/src/storelike.rs +++ b/lib/src/storelike.rs @@ -1,4 +1,5 @@ -//! The Storelike Trait contains many useful methods for maniupulting / retrieving data. +//! The Storelike Trait contains many useful methods for manipulating / retrieving data. +//! It is the basis for both the in-memory [Store] and the on-disk [Db] use tracing::info; @@ -8,9 +9,10 @@ use crate::{ commit::CommitResponse, errors::AtomicError, hierarchy, + query::QueryResult, schema::{Class, Property}, - store::LOCAL_STORE_URL_STR, - urls, + urls::LOCAL_STORE, + Query, }; use crate::{errors::AtomicResult, parse::parse_json_ad_string}; use crate::{mapping::Mapping, values::Value, Atom, Resource}; @@ -242,7 +244,7 @@ pub trait Storelike: Sized { /// the answer should always be `true`. fn is_external_subject(&self, subject: &str) -> AtomicResult { if let Some(self_url) = self.get_self_url() { - if self_url.as_str() == LOCAL_STORE_URL_STR { + if self_url.as_str() == LOCAL_STORE { return Ok(true); } if subject.starts_with(self_url.as_str()) { @@ -264,7 +266,7 @@ pub trait Storelike: Sized { // Check if the last part of the host is equal let Some(subject_host_stripped) = subject_host_parts.last() else { - return Ok(false) + return Ok(false); }; info!( "Comparing hosts: {} and {}", @@ -401,77 +403,3 @@ pub trait Storelike: Sized { crate::validate::validate_store(self, false) } } - -/// Use this to construct a list of Resources -#[derive(Debug)] -pub struct Query { - /// Filter by Property - pub property: Option, - /// Filter by Value - pub value: Option, - /// Maximum of items to return, if none returns all items. - pub limit: Option, - /// Value at which to begin lexicographically sorting things. - pub start_val: Option, - /// Value at which to stop lexicographically sorting things. - pub end_val: Option, - /// How many items to skip from the first one - pub offset: usize, - /// The Property URL that is used to sort the results - pub sort_by: Option, - /// Sort descending instead of ascending. - pub sort_desc: bool, - /// Whether to include non-server resources - pub include_external: bool, - /// Whether to include full Resources in the result, if not, will add empty vector here. - pub include_nested: bool, - /// For which Agent the query is executed. Pass `None` if you want to skip permission checks. - pub for_agent: ForAgent, -} - -impl Query { - pub fn new() -> Self { - Query { - property: None, - value: None, - limit: None, - start_val: None, - end_val: None, - offset: 0, - sort_by: None, - sort_desc: false, - include_external: false, - include_nested: true, - for_agent: ForAgent::Sudo, - } - } - - /// Search for a property-value combination - pub fn new_prop_val(prop: &str, val: &str) -> Self { - let mut q = Self::new(); - q.property = Some(prop.to_string()); - q.value = Some(Value::String(val.to_string())); - q - } - - /// Search for instances of some Class - pub fn new_class(class: &str) -> Self { - let mut q = Self::new(); - q.property = Some(urls::IS_A.into()); - q.value = Some(Value::AtomicUrl(class.to_string())); - q - } -} - -impl Default for Query { - fn default() -> Self { - Self::new() - } -} - -pub struct QueryResult { - pub subjects: Vec, - pub resources: Vec, - /// The amount of hits that were found, including the ones that were out of bounds or not authorized. - pub count: usize, -} diff --git a/lib/src/urls.rs b/lib/src/urls.rs index 1666fe55b..f801af7e4 100644 --- a/lib/src/urls.rs +++ b/lib/src/urls.rs @@ -55,6 +55,7 @@ pub const PREVIOUS_COMMIT: &str = "https://atomicdata.dev/properties/previousCom pub const LAST_COMMIT: &str = "https://atomicdata.dev/properties/lastCommit"; // ... for Agents pub const PUBLIC_KEY: &str = "https://atomicdata.dev/properties/publicKey"; +pub const ACTIVE_KEYS: &str = "https://atomicdata.dev/properties/activeKeys"; pub const NAME: &str = "https://atomicdata.dev/properties/name"; pub const DRIVES: &str = "https://atomicdata.dev/properties/drives"; pub const EMAIL: &str = "https://atomicdata.dev/properties/email"; @@ -156,6 +157,10 @@ pub const PUBLIC_AGENT: &str = "https://atomicdata.dev/agents/publicAgent"; // We don't want a user to actually control this URL. pub const SUDO_AGENT: &str = "sudo:agent"; +/// The URL used for stores that are not accessible on the web. +// I'd prefer this to a non-HTTP URI, but that causes parsing issues when we combine it with some paths (at least with Commits) +pub const LOCAL_STORE: &str = "http://noresolve.localhost"; + // Paths pub const PATH_IMPORT: &str = "/import"; pub const PATH_FETCH_BOOKMARK: &str = "/fetch-bookmark"; @@ -166,6 +171,8 @@ pub const PATH_ENDPOINTS: &str = "/endpoints"; pub const PATH_REGISTER: &str = "/register"; pub const PATH_CONFIRM_EMAIL: &str = "/confirm-email"; pub const PATH_RESET_PUBKEY: &str = "/reset-public-key"; +pub const PATH_CONFIRM_PUBKEY: &str = "/confirm-public-key"; +pub const PATH_ADD_PUBKEY: &str = "/add-public-key"; pub const PATH_CONFIRM_RESET: &str = "/confirm-reset-public-key"; pub const PATH_QUERY: &str = "/query"; pub const PATH_PRUNE_TESTS: &str = "/prunetests"; From 3998b108878bacc1246fecb2644cdebd9f814f96 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:33:00 +0200 Subject: [PATCH 18/51] #502 fix path confirm email --- lib/src/plugins/register.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/plugins/register.rs b/lib/src/plugins/register.rs index 403f4ae47..bbff901ef 100644 --- a/lib/src/plugins/register.rs +++ b/lib/src/plugins/register.rs @@ -80,7 +80,7 @@ pub fn handle_register_name_and_email(context: HandleGetContext) -> AtomicResult let mut confirm_url = store .get_server_url() .clone() - .set_path("confirmEmail") + .set_path(urls::PATH_CONFIRM_EMAIL) .url(); confirm_url.set_query(Some(&format!("token={}", token))); let message = MailMessage { From d05e8e6cd820eed5e0c46204d595a008c5a5f924 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:33:00 +0200 Subject: [PATCH 19/51] Check uniqueness username earlier #489 --- lib/src/plugins/register.rs | 32 ++++++++++++++++++++++++++++++-- server/src/helpers.rs | 1 + 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/lib/src/plugins/register.rs b/lib/src/plugins/register.rs index bbff901ef..846fbfedb 100644 --- a/lib/src/plugins/register.rs +++ b/lib/src/plugins/register.rs @@ -45,6 +45,33 @@ struct MailConfirmation { pub name: String, } +#[derive(Debug, Clone)] +struct UserName { + pub name: String, +} + +impl UserName { + /// Throws error if email address is already taken + pub fn check_used(name: &str, store: &impl Storelike) -> AtomicResult { + let mut drive_url = store + .get_self_url() + .ok_or("No self url, cant check name")? + .clone(); + drive_url.set_subdomain(Some(name))?; + + match store.get_resource(&drive_url.to_string()) { + Ok(_) => Err("Name already used".into()), + Err(_) => Ok(Self { name: name.into() }), + } + } +} + +impl std::fmt::Display for UserName { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.name) + } +} + #[tracing::instrument] pub fn handle_register_name_and_email(context: HandleGetContext) -> AtomicResult { let HandleGetContext { @@ -57,7 +84,7 @@ pub fn handle_register_name_and_email(context: HandleGetContext) -> AtomicResult let store = context.store; for (k, v) in subject.query_pairs() { match k.as_ref() { - "name" | urls::NAME => name_option = Some(v.to_string()), + "name" | urls::NAME => name_option = Some(UserName::check_used(&v, store)?), "email" => email_option = Some(EmailAddress::new(v.to_string())?), _ => {} } @@ -68,13 +95,14 @@ pub fn handle_register_name_and_email(context: HandleGetContext) -> AtomicResult }; let name = name_option.ok_or("No name provided")?; + let _validate_name = Value::new(&name.to_string(), &crate::datatype::DataType::Slug)?; let email = email_option.ok_or("No email provided")?.check_used(store)?; // send the user an e-mail to confirm sign up let store_clone = store.clone(); let confirmation_token_struct = MailConfirmation { email: email.clone(), - name: name.clone(), + name: name.to_string(), }; let token = crate::token::sign_claim(store, confirmation_token_struct)?; let mut confirm_url = store diff --git a/server/src/helpers.rs b/server/src/helpers.rs index ef3de0a70..e6e642f49 100644 --- a/server/src/helpers.rs +++ b/server/src/helpers.rs @@ -69,6 +69,7 @@ fn origin(url: &str) -> String { ) } +/// Checks if the origin in the Cookie matches the requested subject. pub fn get_auth_from_cookie( headers: &HeaderMap, requested_subject: &str, From c0f293f54ab621b519d4abee4b492cfcff1a7022 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:33:00 +0200 Subject: [PATCH 20/51] Refactor endpoints, update JSON --- CHANGELOG.md | 1 + lib/defaults/default_base_models.json | 18 ++-- lib/defaults/default_store.json | 12 ++- lib/src/plugins/add_pubkey.rs | 135 ++++++++++++++++++++++++++ lib/src/plugins/mod.rs | 29 +++++- lib/src/plugins/register.rs | 1 + lib/src/populate.rs | 2 +- 7 files changed, 184 insertions(+), 14 deletions(-) create mode 100644 lib/src/plugins/add_pubkey.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 3878a78df..cd60a494c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -87,6 +87,7 @@ See [STATUS.md](server/STATUS.md) to learn more about which features will remain - Re-build store + invite when adjusting server url #607 - Use local atomic-server for properties and classes, improves atomic-server #604 - New sign up / register flow. Add `/register` Endpoint #489 #254 +- New sign up / register flow. Add `/register`, `/confirm-email`, `/add-public-key` endpoints #489 #254 - Add multi-tenancy support. Users can create their own `Drives` on subdomains. #288 - Refactor URLs. `store.self_url()` returns an `AtomicUrl`, which provides methods to easily add paths, find subdomains and more. - Add support for subdomains, use a Wildcard TLS certificate #502 diff --git a/lib/defaults/default_base_models.json b/lib/defaults/default_base_models.json index 4731f33d4..f5262cbef 100644 --- a/lib/defaults/default_base_models.json +++ b/lib/defaults/default_base_models.json @@ -78,17 +78,23 @@ }, { "@id": "https://atomicdata.dev/classes/Property", - "https://atomicdata.dev/properties/description": "A Resource that should redirect the browser to a new location. It can also set a `redirectAgent`, which is used in Invites to create an Agent Resource on the Server from a Public Key that the user posesses. See the [Invite docs](https://docs.atomicdata.dev/invitations.html).", + "https://atomicdata.dev/properties/description": "A Property is a single field in a Class. It's the thing that a property field in an Atom points to. An example is `birthdate`. An instance of Property requires various Properties, most notably a `datatype` (e.g. `string` or `integer`), a human readable `description` (such as the thing you're reading), and a `shortname`.", "https://atomicdata.dev/properties/isA": [ "https://atomicdata.dev/classes/Class" ], - "https://atomicdata.dev/properties/requires": [ - "https://atomicdata.dev/properties/destination" - ], + "https://atomicdata.dev/properties/parent": "https://atomicdata.dev/classes", "https://atomicdata.dev/properties/recommends": [ - "https://atomicdata.dev/properties/invite/redirectAgent" + "https://atomicdata.dev/properties/classtype", + "https://atomicdata.dev/properties/isDynamic", + "https://atomicdata.dev/properties/isLocked", + "https://atomicdata.dev/properties/allowsOnly" + ], + "https://atomicdata.dev/properties/requires": [ + "https://atomicdata.dev/properties/shortname", + "https://atomicdata.dev/properties/datatype", + "https://atomicdata.dev/properties/description" ], - "https://atomicdata.dev/properties/shortname": "redirect" + "https://atomicdata.dev/properties/shortname": "property" }, { "@id": "https://atomicdata.dev/classes/Class", diff --git a/lib/defaults/default_store.json b/lib/defaults/default_store.json index f2a2305d3..341c7e66c 100644 --- a/lib/defaults/default_store.json +++ b/lib/defaults/default_store.json @@ -814,7 +814,6 @@ "https://atomicdata.dev/properties/isA": [ "https://atomicdata.dev/classes/Property" ], - "https://atomicdata.dev/properties/lastCommit": "https://atomicdata.dev/commits/VB3gtWMkysTX5hKjbYjIM1hfVGPywT3pEPL8c7NwaUAJID6RzptGRPzmix8aKKDeb8Pj1WFv0UPV0YVPxcduBg==", "https://atomicdata.dev/properties/parent": "https://atomicdata.dev/properties", "https://atomicdata.dev/properties/shortname": "sub-resources" }, @@ -826,10 +825,19 @@ "https://atomicdata.dev/properties/isA": [ "https://atomicdata.dev/classes/Property" ], - "https://atomicdata.dev/properties/lastCommit": "https://atomicdata.dev/commits/fS0krtm1wDk0lodH0psnUKmBHBMKLuxnjkd7E7QbkzDk/irQ43gNW3lWxkwQj58ZNg6rUAUMDGJrLy1X3cHwBQ==", "https://atomicdata.dev/properties/parent": "https://atomicdata.dev/properties", "https://atomicdata.dev/properties/shortname": "tags" }, + { + "@id": "https://atomicdata.dev/properties/token", + "https://atomicdata.dev/properties/datatype": "https://atomicdata.dev/datatypes/string", + "https://atomicdata.dev/properties/description": "A server-generated string that should not mean anything to the client. It could be a JWT token, or something else.", + "https://atomicdata.dev/properties/isA": [ + "https://atomicdata.dev/classes/Property" + ], + "https://atomicdata.dev/properties/parent": "https://atomicdata.dev/properties", + "https://atomicdata.dev/properties/shortname": "token" + }, { "@id": "https://atomicdata.dev/properties/write", "https://atomicdata.dev/properties/classtype": "https://atomicdata.dev/classes/Agent", diff --git a/lib/src/plugins/add_pubkey.rs b/lib/src/plugins/add_pubkey.rs new file mode 100644 index 000000000..33c8f769c --- /dev/null +++ b/lib/src/plugins/add_pubkey.rs @@ -0,0 +1,135 @@ +/*! +Sends users a link to add a new public key to their account. +Useful when a users loses their private key. +*/ + +use serde::{Deserialize, Serialize}; + +use crate::{ + agents::Agent, + email::{EmailAddress, MailAction, MailMessage}, + endpoints::{Endpoint, HandleGetContext}, + errors::AtomicResult, + plugins::utils::return_success, + urls, Resource, Storelike, +}; + +pub fn request_email_add_pubkey() -> Endpoint { + Endpoint { + path: urls::PATH_ADD_PUBKEY.to_string(), + params: [urls::TOKEN.to_string(), urls::INVITE_PUBKEY.to_string()].into(), + description: "Requests an email to add a new PublicKey to an Agent.".to_string(), + shortname: "request-pubkey-reset".to_string(), + handle: Some(handle_request_email_pubkey), + handle_post: None, + } +} + +pub fn confirm_add_pubkey() -> Endpoint { + Endpoint { + path: urls::PATH_CONFIRM_PUBKEY.to_string(), + params: [urls::TOKEN.to_string(), urls::INVITE_PUBKEY.to_string()].into(), + description: "Confirms a token to add a new Public Key.".to_string(), + shortname: "request-pubkey-reset".to_string(), + handle: Some(handle_confirm_add_pubkey), + handle_post: None, + } +} + +#[derive(Debug, Serialize, Deserialize)] +struct AddPubkeyToken { + agent: String, +} + +pub fn handle_request_email_pubkey(context: HandleGetContext) -> AtomicResult { + let HandleGetContext { + subject, + store, + for_agent: _, + } = context; + let mut email_option: Option = None; + for (k, v) in subject.query_pairs() { + if let "email" = k.as_ref() { + email_option = Some(EmailAddress::new(v.to_string())?) + } + } + // by default just return the Endpoint + let Some(email) = email_option else { + return request_email_add_pubkey().to_resource(store); + }; + + // Find the agent by their email + let agent = match Agent::from_email(&email.to_string(), store) { + Ok(a) => a, + // If we can't find the agent, we should still return a `success` response, + // in order to prevent users to know that the email exists. + Err(_) => return return_success(), + }; + + // send the user an e-mail to confirm sign up + let store_clone = store.clone(); + let confirmation_token_struct = AddPubkeyToken { + agent: agent.subject, + }; + let token = crate::token::sign_claim(store, confirmation_token_struct)?; + let mut confirm_url = store + .get_server_url() + .clone() + .set_path(urls::PATH_CONFIRM_PUBKEY) + .url(); + confirm_url.set_query(Some(&format!("token={}", token))); + let message = MailMessage { + to: email, + subject: "Add a new Passphrase to your account".to_string(), + body: "You've requested adding a new Passphrase. Click the link below to do so!" + .to_string(), + action: Some(MailAction { + name: "Add new Passphrase to account".to_string(), + url: confirm_url.into(), + }), + }; + // async, because mails are slow + tokio::spawn(async move { + store_clone + .send_email(message) + .await + .unwrap_or_else(|e| tracing::error!("Error sending email: {}", e)); + }); + + return_success() +} + +pub fn handle_confirm_add_pubkey(context: HandleGetContext) -> AtomicResult { + let HandleGetContext { + subject, + store, + for_agent: _, + } = context; + + let mut token_opt: Option = None; + let mut pubkey_option = None; + + for (k, v) in subject.query_pairs() { + match k.as_ref() { + "token" | urls::TOKEN => token_opt = Some(v.to_string()), + "public-key" | urls::INVITE_PUBKEY => pubkey_option = Some(v.to_string()), + _ => {} + } + } + + let Some(token) = token_opt else { + return confirm_add_pubkey().to_resource(store); + }; + + let pubkey = pubkey_option.ok_or("No public-key provided")?; + + // Parse and verify the JWT token + let confirmation = crate::token::verify_claim::(store, &token)?.custom; + + // Add the key to the agent + let mut agent = store.get_resource(&confirmation.agent)?; + agent.push_propval(urls::ACTIVE_KEYS, pubkey.into(), true)?; + agent.save(store)?; + + return_success() +} diff --git a/lib/src/plugins/mod.rs b/lib/src/plugins/mod.rs index 4e54f7351..6b7c8025a 100644 --- a/lib/src/plugins/mod.rs +++ b/lib/src/plugins/mod.rs @@ -6,7 +6,7 @@ Plugins can have functions that are called at specific moments by Atomic-Server. For example: -- Before returning a Resource. These are either Endpoints or Class Extenders. +- Before returning a Resource. These are either [Endpoint]s or Class Extenders. - Before applying a Commit. In the long term, these plugins will probably be powered by WASM and can be extended at runtime. @@ -16,13 +16,13 @@ However, they are designed in such a way that they have a limited scope and a cl ## Extending resources There are two ways of extending / modifying a Resource. -Endpoints are great for APIs that have a fixed route, and Class Extenders are great for APIs that don't have a fixed route. +[Endpoint]s are great for APIs that have a fixed route, and Class Extenders are great for APIs that don't have a fixed route. Endpoints are easier to generate from Rust, and will be available the second a server is Running. -### Endpoints +### [Endpoint]s Resources that typically parse query parameters and return a dynamic resource. -When adding an endpoint, add it to the list of endpoints in [lib/src/endpoints.rs] +When adding an endpoint, add it to the list of [default_endpoints] in this file. Endpoints are all instances of the [crate] class. They are presented in the UI as a form. @@ -31,14 +31,18 @@ They are presented in the UI as a form. Similar to Endpoints, Class Extenders can modify their contents before creating a response. Contrary to Endpoints, these can be any type of Class. They are used for performing custom queries, or calculating dynamic attributes. +Add these by registering the handler at [crate::db::Db::get_resource_extended]. */ +use crate::endpoints::Endpoint; + // Class Extenders pub mod chatroom; pub mod importer; pub mod invite; // Endpoints +pub mod add_pubkey; #[cfg(feature = "html")] pub mod bookmark; pub mod export; @@ -47,9 +51,24 @@ pub mod path; pub mod prunetests; pub mod query; pub mod register; -pub mod reset_pubkey; pub mod search; pub mod versioning; // Utilities / helpers mod utils; + +pub fn default_endpoints() -> Vec { + vec![ + versioning::version_endpoint(), + versioning::all_versions_endpoint(), + path::path_endpoint(), + search::search_endpoint(), + files::upload_endpoint(), + register::register_endpoint(), + register::confirm_email_endpoint(), + add_pubkey::request_email_add_pubkey(), + add_pubkey::confirm_add_pubkey(), + #[cfg(feature = "html")] + bookmark::bookmark_endpoint(), + ] +} diff --git a/lib/src/plugins/register.rs b/lib/src/plugins/register.rs index 846fbfedb..2a9aca99e 100644 --- a/lib/src/plugins/register.rs +++ b/lib/src/plugins/register.rs @@ -109,6 +109,7 @@ pub fn handle_register_name_and_email(context: HandleGetContext) -> AtomicResult .get_server_url() .clone() .set_path(urls::PATH_CONFIRM_EMAIL) + // .set_subdomain(Some(&name.to_string()))? .url(); confirm_url.set_query(Some(&format!("token={}", token))); let message = MailMessage { diff --git a/lib/src/populate.rs b/lib/src/populate.rs index 466dd2d3e..05601b78b 100644 --- a/lib/src/populate.rs +++ b/lib/src/populate.rs @@ -315,7 +315,7 @@ pub fn populate_collections(store: &impl Storelike) -> AtomicResult<()> { pub fn populate_endpoints(store: &crate::Db) -> AtomicResult<()> { use crate::atomic_url::Routes; - let endpoints = crate::endpoints::default_endpoints(); + let endpoints = crate::plugins::default_endpoints(); let endpoints_collection = store.get_server_url().set_route(Routes::Endpoints); for endpoint in endpoints { let mut resource = endpoint.to_resource(store)?; From e87ff7a69303839e9332b3f6aa3830d4e2130fcd Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:33:00 +0200 Subject: [PATCH 21/51] Improve check_append error #558 --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd60a494c..7c4216010 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -105,7 +105,6 @@ See [STATUS.md](server/STATUS.md) to learn more about which features will remain - Fix index issue happening when deleting a single property in a sorted collection #545 - Update JS assets & playwright - Fix initial indexing bug #560 -- Fix errors on succesful export / import #565 - Fix envs for store path, change `ATOMIC_STORE_DIR` to `ATOMIC_DATA_DIR` #567 - Refactor static file asset hosting #578 - Meta tags server side #577 @@ -113,6 +112,8 @@ See [STATUS.md](server/STATUS.md) to learn more about which features will remain - Remove feature to index external RDF files and search them #579 - Add staging environment #588 - Add systemd instructions to readme #271 +- Improve check_append error #558 +- Fix errors on successful export / import #565 ## [v0.34.0] - 2022-10-31 From cdebc02d1bb0abe04809bb00608936b0d2c1f282 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:33:00 +0200 Subject: [PATCH 22/51] 556 constrain URLs --- CHANGELOG.md | 2 ++ lib/src/atomic_url.rs | 4 ++-- lib/src/collections.rs | 14 ++++++++++++-- lib/src/commit.rs | 1 + lib/src/db/test.rs | 4 ++-- lib/src/parse.rs | 7 ++++--- lib/src/resources.rs | 4 ++++ lib/test_files/local_id.json | 10 +++++----- server/src/handlers/commit.rs | 3 ++- server/src/tests.rs | 14 +++++++------- 10 files changed, 41 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c4216010..79591ae07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -114,6 +114,8 @@ See [STATUS.md](server/STATUS.md) to learn more about which features will remain - Add systemd instructions to readme #271 - Improve check_append error #558 - Fix errors on successful export / import #565 +- Most Collection routes now live under `/collections`, e.g. `/collections/agents` instead of `/agents`. #556 +- Constrain new URLs. Commits for new Resources are now only valid if their parent is part of the current URL. So it's no longer possible to create `/some-path/new-resource` if `new-resource` is its parent is not in its URL. #556 ## [v0.34.0] - 2022-10-31 diff --git a/lib/src/atomic_url.rs b/lib/src/atomic_url.rs index 5afc0b32e..778c89590 100644 --- a/lib/src/atomic_url.rs +++ b/lib/src/atomic_url.rs @@ -36,9 +36,9 @@ impl AtomicUrl { pub fn set_route(&self, route: Routes) -> Self { let path = match route { Routes::AllVersions => "/all-versions".to_string(), - Routes::Agents => "/agents".to_string(), + Routes::Agents => "/collections/agents".to_string(), Routes::Collections => "/collections".to_string(), - Routes::Commits => "/commits".to_string(), + Routes::Commits => "/collections/commits".to_string(), Routes::CommitsUnsigned => "/commits-unsigned".to_string(), Routes::Endpoints => "/endpoints".to_string(), Routes::Import => "/import".to_string(), diff --git a/lib/src/collections.rs b/lib/src/collections.rs index 74783b6a1..64e654cd0 100644 --- a/lib/src/collections.rs +++ b/lib/src/collections.rs @@ -385,6 +385,10 @@ pub fn create_collection_resource_for_class( ) -> AtomicResult { let class = store.get_class(class_subject)?; + // We use the `Collections` collection as the parent for all collections. + // This also influences their URLs. + let is_collections_collection = class.subject == urls::COLLECTION; + // Pluralize the shortname let pluralized = match class.shortname.as_ref() { "class" => "classes".to_string(), @@ -392,7 +396,13 @@ pub fn create_collection_resource_for_class( other => format!("{}s", other), }; - let mut collection = CollectionBuilder::class_collection(&class.subject, &pluralized, store); + let path = if is_collections_collection { + "/collections".to_string() + } else { + format!("/collections/{}", pluralized) + }; + + let mut collection = CollectionBuilder::class_collection(&class.subject, &path, store); collection.sort_by = match class_subject { urls::COMMIT => Some(urls::CREATED_AT.to_string()), @@ -413,7 +423,7 @@ pub fn create_collection_resource_for_class( .ok_or("No self_url present in store, can't populate collections")?; // Let the Collections collection be the top level item - let parent = if class.subject == urls::COLLECTION { + let parent = if is_collections_collection { drive.to_string() } else { drive diff --git a/lib/src/commit.rs b/lib/src/commit.rs index d4583b0e7..a2874e4f0 100644 --- a/lib/src/commit.rs +++ b/lib/src/commit.rs @@ -679,6 +679,7 @@ mod test { validate_previous_commit: true, validate_rights: false, validate_for_agent: None, + validate_subject_url_parent: true, update_index: true, }; } diff --git a/lib/src/db/test.rs b/lib/src/db/test.rs index 0b522c5dc..481009550 100644 --- a/lib/src/db/test.rs +++ b/lib/src/db/test.rs @@ -200,7 +200,7 @@ fn destroy_resource_and_check_collection_and_commits() { fn get_extended_resource_pagination() { let store = Db::init_temp("get_extended_resource_pagination").unwrap(); let subject = format!( - "{}commits?current_page=2&page_size=99999", + "{}collections/commits?current_page=2&page_size=99999", store.get_server_url() ); let for_agent = &ForAgent::Public; @@ -220,7 +220,7 @@ fn get_extended_resource_pagination() { .unwrap() .to_int() .unwrap(); - assert_eq!(cur_page, 2); + assert_eq!(cur_page, num); assert_eq!(resource.get_subject(), &subject_with_page_size); } diff --git a/lib/src/parse.rs b/lib/src/parse.rs index 0d5497c28..d65f38051 100644 --- a/lib/src/parse.rs +++ b/lib/src/parse.rs @@ -418,7 +418,8 @@ fn parse_json_ad_map_to_resource( validate_timestamp: false, validate_rights: parse_opts.for_agent != ForAgent::Sudo, validate_previous_commit: false, - validate_for_agent: Some(parse_opts.for_agent.to_string()), + validate_for_agent: parse_opts.for_agent.clone(), + validate_subject_url_parent: true, update_index: true, }; @@ -644,8 +645,8 @@ mod test { .import(include_str!("../test_files/local_id.json"), &parse_opts) .unwrap(); - let reference_subject = generate_id_from_local_id(&importer, "reference"); - let my_subject = generate_id_from_local_id(&importer, "my-local-id"); + let reference_subject = generate_id_from_local_id(&importer, "parent"); + let my_subject = generate_id_from_local_id(&importer, "parent/my-local-id"); let found = store.get_resource(&my_subject).unwrap(); let found_ref = store.get_resource(&reference_subject).unwrap(); diff --git a/lib/src/resources.rs b/lib/src/resources.rs index b0c6e5d44..f143566d1 100644 --- a/lib/src/resources.rs +++ b/lib/src/resources.rs @@ -357,6 +357,7 @@ impl Resource { validate_for_agent: agent.subject.into(), // TODO: auto-merge should work before we enable this https://github.com/atomicdata-dev/atomic-server/issues/412 validate_previous_commit: false, + validate_subject_url_parent: true, update_index: true, }; let commit_response = store.apply_commit(commit, &opts)?; @@ -385,6 +386,8 @@ impl Resource { validate_for_agent: agent.subject.into(), // https://github.com/atomicdata-dev/atomic-server/issues/412 validate_previous_commit: false, + // This is contentious: https://github.com/atomicdata-dev/atomic-data-rust/issues/556 + validate_subject_url_parent: false, update_index: true, }; let commit_response = store.apply_commit(commit, &opts)?; @@ -695,6 +698,7 @@ mod test { validate_signature: true, validate_timestamp: true, validate_rights: false, + validate_subject_url_parent: true, validate_previous_commit: true, validate_for_agent: None, update_index: true, diff --git a/lib/test_files/local_id.json b/lib/test_files/local_id.json index 8773e399b..d9610a5f5 100644 --- a/lib/test_files/local_id.json +++ b/lib/test_files/local_id.json @@ -1,17 +1,17 @@ [ { - "https://atomicdata.dev/properties/localId": "reference", + "https://atomicdata.dev/properties/localId": "parent", "https://atomicdata.dev/properties/write": [ - "my-local-id" + "parent/my-local-id" ], "https://atomicdata.dev/properties/name": "My referenced resource" }, { - "https://atomicdata.dev/properties/localId": "my-local-id", + "https://atomicdata.dev/properties/localId": "parent/my-local-id", "https://atomicdata.dev/properties/name": "My resource that refers", - "https://atomicdata.dev/properties/parent": "reference", + "https://atomicdata.dev/properties/parent": "parent", "https://atomicdata.dev/properties/write": [ - "reference" + "parent" ] } ] diff --git a/server/src/handlers/commit.rs b/server/src/handlers/commit.rs index 10e3a9e77..4ae271ffb 100644 --- a/server/src/handlers/commit.rs +++ b/server/src/handlers/commit.rs @@ -19,7 +19,7 @@ pub async fn post_commit( let mut builder = HttpResponse::Ok(); let incoming_commit_resource = parse_json_ad_commit_resource(&body, store)?; let incoming_commit = Commit::from_resource(incoming_commit_resource)?; -if store.is_external_subject(&incoming_commit.subject)? { + if store.is_external_subject(&incoming_commit.subject)? { return Err("Subject of commit is external, and should be sent to its origin domain. This store can not own this resource. See https://github.com/atomicdata-dev/atomic-data-rust/issues/509".into()); } let opts = CommitOpts { @@ -30,6 +30,7 @@ if store.is_external_subject(&incoming_commit.subject)? { // https://github.com/atomicdata-dev/atomic-server/issues/412 validate_previous_commit: false, validate_for_agent: Some(incoming_commit.signer.to_string()), + validate_subject_url_parent: true, update_index: true, }; let commit_response = store.apply_commit(incoming_commit, &opts)?; diff --git a/server/src/tests.rs b/server/src/tests.rs index cfd3b33dd..eccd472f7 100644 --- a/server/src/tests.rs +++ b/server/src/tests.rs @@ -79,8 +79,8 @@ async fn server_tests() { assert!(body.as_str().contains("html")); // Should 200 (public) - let req = - test::TestRequest::with_uri("/properties").insert_header(("Accept", "application/ad+json")); + let req = test::TestRequest::with_uri("/collections/properties") + .insert_header(("Accept", "application/ad+json")); let resp = test::call_service(&app, req.to_request()).await; assert_eq!( resp.status().as_u16(), @@ -109,8 +109,8 @@ async fn server_tests() { drive.save(store).unwrap(); // Should 401 (Unauthorized) - let req = - test::TestRequest::with_uri("/properties").insert_header(("Accept", "application/ad+json")); + let req = test::TestRequest::with_uri("/collections/properties") + .insert_header(("Accept", "application/ad+json")); let resp = test::call_service(&app, req.to_request()).await; assert_eq!( resp.status().as_u16(), @@ -119,7 +119,7 @@ async fn server_tests() { ); // Get JSON-AD - let req = build_request_authenticated("/properties", &appstate); + let req = build_request_authenticated("/collections/properties", &appstate); let resp = test::call_service(&app, req.to_request()).await; let body = get_body(resp); println!("DEBUG: {:?}", body); @@ -130,7 +130,7 @@ async fn server_tests() { ); // Get JSON-LD - let req = build_request_authenticated("/properties", &appstate) + let req = build_request_authenticated("/collections/properties", &appstate) .insert_header(("Accept", "application/ld+json")); let resp = test::call_service(&app, req.to_request()).await; assert!(resp.status().is_success(), "setup not returning JSON-LD"); @@ -141,7 +141,7 @@ async fn server_tests() { ); // Get turtle - let req = build_request_authenticated("/properties", &appstate) + let req = build_request_authenticated("/collections/properties", &appstate) .insert_header(("Accept", "text/turtle")); let resp = test::call_service(&app, req.to_request()).await; assert!(resp.status().is_success()); From a63f406d23de3cc66d41aaa7e40edf2a52cabf54 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:33:00 +0200 Subject: [PATCH 23/51] Store.tpf public for tests --- lib/src/store.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/store.rs b/lib/src/store.rs index 89310e3b0..4d32b86ca 100644 --- a/lib/src/store.rs +++ b/lib/src/store.rs @@ -38,7 +38,7 @@ impl Store { /// Returns an empty array if nothing is found. // Very costly, slow implementation. // Does not assume any indexing. - fn tpf( + pub fn tpf( &self, q_subject: Option<&str>, q_property: Option<&str>, From 412aa9e54ae3893a28e1fef3e57e93c75595862a Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:33:00 +0200 Subject: [PATCH 24/51] import_file test failing on macos #564 --- server/src/appstate.rs | 2 +- server/src/bin.rs | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/server/src/appstate.rs b/server/src/appstate.rs index 0c157b84e..d65def82a 100644 --- a/server/src/appstate.rs +++ b/server/src/appstate.rs @@ -7,7 +7,7 @@ use atomic_lib::{ atomic_url::Routes, commit::CommitResponse, email::SmtpConfig, - Storelike, + Db, Storelike, }; /// The AppState contains all the relevant Context for the server. diff --git a/server/src/bin.rs b/server/src/bin.rs index 80537bc5a..155c9697b 100644 --- a/server/src/bin.rs +++ b/server/src/bin.rs @@ -68,8 +68,7 @@ async fn main_wrapped() -> errors::AtomicServerResult<()> { let importer_subject = if let Some(i) = &import_opts.parent { i.into() } else { - appstate - .store + store .get_self_url() .expect("No self URL") .set_route(Routes::Import) @@ -84,11 +83,10 @@ async fn main_wrapped() -> errors::AtomicServerResult<()> { } else { atomic_lib::parse::SaveOpts::Commit }, - signer: Some(appstate.store.get_default_agent()?), + signer: Some(store.get_default_agent()?), }; println!("Importing..."); appstate.store.import(&readstring, &parse_opts)?; - appstate.search_state.add_all_resources(&appstate.store)?; println!("Successfully imported {:?} to store.", import_opts.file); println!("WARNING: Your search index is not yet updated with these imported items. Run `--rebuild-index` to fix that."); Ok(()) From 61c74152bd1a3cc3b3c3f63df30224cb5f75183d Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:33:00 +0200 Subject: [PATCH 25/51] Throw error on trailing slash server URL #566 --- server/src/config.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/src/config.rs b/server/src/config.rs index 1e5b89fd4..d29e9ea9f 100644 --- a/server/src/config.rs +++ b/server/src/config.rs @@ -280,7 +280,11 @@ pub fn build_config(opts: Opts) -> AtomicServerResult { // This logic could be a bit too complicated, but I'm not sure on how to make this simpler. let server_url = if let Some(addr) = opts.server_url.clone() { - addr + if addr.ends_with('/') { + return Err("The Server URL should not end with a trailing slash.".into()); + } else { + addr + } } else if opts.https && opts.port_https == 443 || !opts.https && opts.port == 80 { format!("{}://{}", schema, opts.domain) } else { From b87fc2263dc4757e9ddcfd95f44c64f6fa154762 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:33:00 +0200 Subject: [PATCH 26/51] Fix cookies subject in search --- server/src/handlers/search.rs | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/server/src/handlers/search.rs b/server/src/handlers/search.rs index 2ab6ee672..58bbbd2bb 100644 --- a/server/src/handlers/search.rs +++ b/server/src/handlers/search.rs @@ -5,6 +5,7 @@ use crate::{ appstate::AppState, errors::{AtomicServerError, AtomicServerResult}, + helpers::get_subject, search::{resource_to_facet, Fields}, }; use actix_web::{web, HttpResponse}; @@ -54,6 +55,7 @@ pub async fn search_query( appstate: web::Data, params: web::Query, req: actix_web::HttpRequest, + conn: actix_web::dev::ConnectionInfo, ) -> AtomicServerResult { let mut timer = Timer::new(); let store = &appstate.store; @@ -82,19 +84,7 @@ pub async fn search_query( let subjects = docs_to_subjects(top_docs, &fields, &searcher)?; // Create a valid atomic data resource. - // You'd think there would be a simpler way of getting the requested URL... - // See https://github.com/actix/actix-web/issues/2895 - let subject: String = store - .get_self_url() - .ok_or("No base URL set")? - .url() - .join( - req.uri() - .path_and_query() - .ok_or("Add a query param")? - .as_str(), - )? - .to_string(); + let subject: String = get_subject(&req, &conn, &appstate)?; let mut results_resource = atomic_lib::plugins::search::search_endpoint().to_resource(store)?; results_resource.set_subject(subject.clone()); From 2e43ea0527b9952805a282c8e6d154d7b3cc80e3 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:33:00 +0200 Subject: [PATCH 27/51] Set max chars for meta tags --- server/src/handlers/single_page_app.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/server/src/handlers/single_page_app.rs b/server/src/handlers/single_page_app.rs index c5bb89a37..543d18741 100644 --- a/server/src/handlers/single_page_app.rs +++ b/server/src/handlers/single_page_app.rs @@ -100,7 +100,10 @@ impl Default for MetaTags { impl Display for MetaTags { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let description = escape_html(&self.description); + let description = escape_html(&self.description) + .chars() + .take(250) + .collect::(); let image = &self.image; let title = escape_html(&self.title); From 107ab3ae10cadbc04c79b939dc1659f5a4fd8a49 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:33:00 +0200 Subject: [PATCH 28/51] Cleanup, imports --- server/src/config.rs | 4 ++-- server/src/helpers.rs | 18 ------------------ 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/server/src/config.rs b/server/src/config.rs index d29e9ea9f..e0dfb2729 100644 --- a/server/src/config.rs +++ b/server/src/config.rs @@ -96,11 +96,11 @@ pub struct Opts { #[clap(long, env = "ATOMIC_SLOW_MODE")] pub slow_mode: bool, - /// Add this if you want so send e-mails (e.g. on user registration) + /// Host address of an SMTP server. Add if you want so send e-mails (e.g. for user registration). Also set the port. #[clap(long, env = "ATOMIC_SMTP_HOST")] pub smpt_host: Option, - /// Useful for debugging e-mails during development, or if your SMTP server has an unconventional port number. + /// Port of your SMTP server. #[clap(long, env = "ATOMIC_SMTP_PORT", default_value = "25")] pub smpt_port: u16, diff --git a/server/src/helpers.rs b/server/src/helpers.rs index e6e642f49..d4056c2c5 100644 --- a/server/src/helpers.rs +++ b/server/src/helpers.rs @@ -187,24 +187,6 @@ pub fn get_client_agent( Ok(for_agent) } -/// Finds the extension -pub fn try_extension(path: &str) -> Option<(ContentType, &str)> { - let items: Vec<&str> = path.split('.').collect(); - if items.len() == 2 { - let path = items[0]; - let content_type = match items[1] { - "json" => ContentType::Json, - "jsonld" => ContentType::JsonLd, - "jsonad" => ContentType::JsonAd, - "html" => ContentType::Html, - "ttl" => ContentType::Turtle, - _ => return None, - }; - return Some((content_type, path)); - } - None -} - fn session_cookies_from_header(header: &HeaderValue) -> AtomicServerResult> { let cookies: Vec<&str> = header .to_str() From a2c68f51eb4c6367fabf0d62525ffaf46a02a3c4 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:33:01 +0200 Subject: [PATCH 29/51] Fix rebase issues --- lib/src/plugins/reset_pubkey.rs | 130 -------------------------------- server/src/helpers.rs | 18 +++++ 2 files changed, 18 insertions(+), 130 deletions(-) delete mode 100644 lib/src/plugins/reset_pubkey.rs diff --git a/lib/src/plugins/reset_pubkey.rs b/lib/src/plugins/reset_pubkey.rs deleted file mode 100644 index b0ccc1c1b..000000000 --- a/lib/src/plugins/reset_pubkey.rs +++ /dev/null @@ -1,130 +0,0 @@ -/*! -Sends users a link to add a new public key to their account. -Useful when a users loses their private key. -*/ - -use serde::{Deserialize, Serialize}; - -use crate::{ - agents::Agent, - email::{EmailAddress, MailAction, MailMessage}, - endpoints::{Endpoint, HandleGetContext}, - errors::AtomicResult, - plugins::utils::return_success, - urls, Resource, Storelike, -}; - -pub fn request_email_add_pubkey() -> Endpoint { - Endpoint { - path: urls::PATH_ADD_PUBKEY.to_string(), - params: [urls::TOKEN.to_string(), urls::INVITE_PUBKEY.to_string()].into(), - description: "Requests an email to add a new PublicKey to an Agent.".to_string(), - shortname: "pubkey-add".to_string(), - handle: Some(handle_request_email_pubkey), - handle_post: None, - } -} - -pub fn confirm_add_pubkey() -> Endpoint { - Endpoint { - path: urls::PATH_CONFIRM_PUBKEY.to_string(), - params: [urls::TOKEN.to_string(), urls::INVITE_PUBKEY.to_string()].into(), - description: "Confirms a token to add a new Public Key.".to_string(), - shortname: "request-pubkey-reset".to_string(), - handle: Some(handle_confirm_add_pubkey), - handle_post: None, - } -} - -#[derive(Debug, Serialize, Deserialize)] -struct AddPubkeyToken { - agent: String, -} - -pub fn handle_request_email_pubkey(context: HandleGetContext) -> AtomicResult { - let HandleGetContext { - subject, - store, - for_agent: _, - } = context; - let mut email_option: Option = None; - for (k, v) in subject.query_pairs() { - match k.as_ref() { - "email" => email_option = Some(EmailAddress::new(v.to_string())?), - _ => {} - } - } - // by default just return the Endpoint - let Some(email) = email_option else { - return request_email_add_pubkey().to_resource(store); - }; - - // Find the agent by their email - let agent = Agent::from_email(&email.to_string(), store)?; - - // send the user an e-mail to confirm sign up - let store_clone = store.clone(); - let confirmation_token_struct = AddPubkeyToken { - agent: agent.subject, - }; - let token = crate::token::sign_claim(store, confirmation_token_struct)?; - let mut confirm_url = store - .get_server_url() - .clone() - .set_path(urls::PATH_CONFIRM_PUBKEY) - .url(); - confirm_url.set_query(Some(&format!("token={}", token))); - let message = MailMessage { - to: email, - subject: "Add a new Passphrase to your account".to_string(), - body: "You've requested adding a new Passphrase. Click the link below to do so!" - .to_string(), - action: Some(MailAction { - name: "Add new Passphrase to account".to_string(), - url: confirm_url.into(), - }), - }; - // async, because mails are slow - tokio::spawn(async move { - store_clone - .send_email(message) - .await - .unwrap_or_else(|e| tracing::error!("Error sending email: {}", e)); - }); - - return_success() -} - -#[tracing::instrument] -pub fn handle_confirm_add_pubkey(context: HandleGetContext) -> AtomicResult { - let HandleGetContext { - subject, - store, - for_agent: _, - } = context; - let mut token_opt: Option = None; - let mut pubkey_option = None; - - for (k, v) in subject.query_pairs() { - match k.as_ref() { - "token" | urls::TOKEN => token_opt = Some(v.to_string()), - "public-key" | urls::INVITE_PUBKEY => pubkey_option = Some(v.to_string()), - _ => {} - } - } - let pubkey = pubkey_option.ok_or("No public-key provided")?; - - let Some(token) = token_opt else { - return confirm_add_pubkey().to_resource(store); - }; - - // Parse and verify the JWT token - let confirmation = crate::token::verify_claim::(store, &token)?.custom; - - // Add the key to the agent - let mut agent = store.get_resource(&confirmation.agent)?; - agent.push(urls::ACTIVE_KEYS, pubkey.into(), true)?; - agent.save(store)?; - - return_success() -} diff --git a/server/src/helpers.rs b/server/src/helpers.rs index d4056c2c5..d7e745dbf 100644 --- a/server/src/helpers.rs +++ b/server/src/helpers.rs @@ -311,3 +311,21 @@ pub fn get_subject( let subject = format!("{}{}", server_without_last_slash, &req.uri().to_string()); Ok(subject) } + +/// Finds the extension +pub fn try_extension(path: &str) -> Option<(ContentType, &str)> { + let items: Vec<&str> = path.split('.').collect(); + if items.len() == 2 { + let path = items[0]; + let content_type = match items[1] { + "json" => ContentType::Json, + "jsonld" => ContentType::JsonLd, + "jsonad" => ContentType::JsonAd, + "html" => ContentType::Html, + "ttl" => ContentType::Turtle, + _ => return None, + }; + return Some((content_type, path)); + } + None +} From fc275240669655cc06140c65f96b4e7f11cfa313 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:33:01 +0200 Subject: [PATCH 30/51] Use get_subject --- lib/src/parse.rs | 2 +- server/src/handlers/get_resource.rs | 33 ++------------ server/src/handlers/post_resource.rs | 33 ++------------ server/src/handlers/search.rs | 2 +- server/src/handlers/single_page_app.rs | 5 +- server/src/handlers/upload.rs | 2 +- server/src/helpers.rs | 63 ++++++++++++++++++-------- 7 files changed, 58 insertions(+), 82 deletions(-) diff --git a/lib/src/parse.rs b/lib/src/parse.rs index d65f38051..eca1137cf 100644 --- a/lib/src/parse.rs +++ b/lib/src/parse.rs @@ -418,7 +418,7 @@ fn parse_json_ad_map_to_resource( validate_timestamp: false, validate_rights: parse_opts.for_agent != ForAgent::Sudo, validate_previous_commit: false, - validate_for_agent: parse_opts.for_agent.clone(), + validate_for_agent: Some(parse_opts.for_agent.to_string()), validate_subject_url_parent: true, update_index: true, }; diff --git a/server/src/handlers/get_resource.rs b/server/src/handlers/get_resource.rs index 1ef8f199c..184543f88 100644 --- a/server/src/handlers/get_resource.rs +++ b/server/src/handlers/get_resource.rs @@ -1,9 +1,8 @@ use crate::{ appstate::AppState, - content_types::get_accept, content_types::ContentType, errors::AtomicServerResult, - helpers::{get_client_agent, try_extension}, + helpers::{get_client_agent, get_subject}, }; use actix_web::{web, HttpResponse}; use atomic_lib::Storelike; @@ -16,39 +15,13 @@ pub async fn handle_get_resource( path: Option>, appstate: web::Data, req: actix_web::HttpRequest, + conn: actix_web::dev::ConnectionInfo, ) -> AtomicServerResult { let mut timer = Timer::new(); let headers = req.headers(); - let mut content_type = get_accept(headers); - let server_url = &appstate.config.server_url; // Get the subject from the path, or return the home URL - let subject = if let Some(subj_end) = path { - let mut subj_end_string = subj_end.as_str(); - // If the request is for the root, return the home URL - if subj_end_string.is_empty() { - server_url.to_string() - } else { - if content_type == ContentType::Html { - if let Some((ext, path)) = try_extension(subj_end_string) { - content_type = ext; - subj_end_string = path; - } - } - // Check extensions and set datatype. Harder than it looks to get right... - // This might not be the best way of creating the subject. But I can't access the full URL from any actix stuff! - let querystring = if req.query_string().is_empty() { - "".to_string() - } else { - format!("?{}", req.query_string()) - }; - let subject = format!("{}/{}{}", server_url, subj_end_string, querystring); - subject - } - } else { - // There is no end string, so It's the root of the URL, the base URL! - String::from(server_url) - }; + let (subject, content_type) = get_subject(&req, &conn, &appstate)?; let store = &appstate.store; timer.add("parse_headers"); diff --git a/server/src/handlers/post_resource.rs b/server/src/handlers/post_resource.rs index 881467751..5c676e612 100644 --- a/server/src/handlers/post_resource.rs +++ b/server/src/handlers/post_resource.rs @@ -1,9 +1,8 @@ use crate::{ appstate::AppState, - content_types::get_accept, content_types::ContentType, errors::AtomicServerResult, - helpers::{get_client_agent, try_extension}, + helpers::{get_client_agent, get_subject}, }; use actix_web::{web, HttpResponse}; use atomic_lib::Storelike; @@ -16,39 +15,13 @@ pub async fn handle_post_resource( appstate: web::Data, req: actix_web::HttpRequest, body: web::Bytes, + conn: actix_web::dev::ConnectionInfo, ) -> AtomicServerResult { let mut timer = Timer::new(); let headers = req.headers(); - let mut content_type = get_accept(headers); - let server_url = &appstate.config.server_url; // Get the subject from the path, or return the home URL - let subject = if let Some(subj_end) = path { - let mut subj_end_string = subj_end.as_str(); - // If the request is for the root, return the home URL - if subj_end_string.is_empty() { - server_url.to_string() - } else { - if content_type == ContentType::Html { - if let Some((ext, path)) = try_extension(subj_end_string) { - content_type = ext; - subj_end_string = path; - } - } - // Check extensions and set datatype. Harder than it looks to get right... - // This might not be the best way of creating the subject. But I can't access the full URL from any actix stuff! - let querystring = if req.query_string().is_empty() { - "".to_string() - } else { - format!("?{}", req.query_string()) - }; - let subject = format!("{}/{}{}", server_url, subj_end_string, querystring); - subject - } - } else { - // There is no end string, so It's the root of the URL, the base URL! - String::from(server_url) - }; + let (subject, content_type) = get_subject(&req, &conn, &appstate)?; let store = &appstate.store; timer.add("parse_headers"); diff --git a/server/src/handlers/search.rs b/server/src/handlers/search.rs index 58bbbd2bb..b8fd340fe 100644 --- a/server/src/handlers/search.rs +++ b/server/src/handlers/search.rs @@ -84,7 +84,7 @@ pub async fn search_query( let subjects = docs_to_subjects(top_docs, &fields, &searcher)?; // Create a valid atomic data resource. - let subject: String = get_subject(&req, &conn, &appstate)?; + let (subject, _) = get_subject(&req, &conn, &appstate)?; let mut results_resource = atomic_lib::plugins::search::search_endpoint().to_resource(store)?; results_resource.set_subject(subject.clone()); diff --git a/server/src/handlers/single_page_app.rs b/server/src/handlers/single_page_app.rs index 543d18741..766f1495c 100644 --- a/server/src/handlers/single_page_app.rs +++ b/server/src/handlers/single_page_app.rs @@ -1,6 +1,7 @@ use std::fmt::Display; use std::fmt::Formatter; +use crate::helpers::get_subject; use crate::{appstate::AppState, errors::AtomicServerResult}; use actix_web::HttpResponse; @@ -9,9 +10,11 @@ use actix_web::HttpResponse; pub async fn single_page( appstate: actix_web::web::Data, path: actix_web::web::Path, + req: actix_web::HttpRequest, + conn: actix_web::dev::ConnectionInfo, ) -> AtomicServerResult { let template = include_str!("../../assets_tmp/index.html"); - let subject = format!("{}/{}", appstate.store.get_server_url(), path); + let (subject, _content_type) = get_subject(&req, &conn, &appstate)?; let meta_tags: MetaTags = if let Ok(resource) = appstate .store diff --git a/server/src/handlers/upload.rs b/server/src/handlers/upload.rs index 475796cb5..7eb1adcd0 100644 --- a/server/src/handlers/upload.rs +++ b/server/src/handlers/upload.rs @@ -33,7 +33,7 @@ pub async fn upload_handler( ) -> AtomicServerResult { let store = &appstate.store; let parent = store.get_resource(&query.parent)?; - let subject = get_subject(&req, &conn, &appstate)?; + let (subject, _) = get_subject(&req, &conn, &appstate)?; let for_agent = get_client_agent(req.headers(), &appstate, subject)?; check_write(store, &parent, &for_agent)?; diff --git a/server/src/helpers.rs b/server/src/helpers.rs index d7e745dbf..db255d67b 100644 --- a/server/src/helpers.rs +++ b/server/src/helpers.rs @@ -10,7 +10,7 @@ use atomic_lib::Storelike; use percent_encoding::percent_decode_str; use std::str::FromStr; -use crate::content_types::ContentType; +use crate::content_types::{get_accept, ContentType}; use crate::errors::{AppErrorType, AtomicServerError}; use crate::{appstate::AppState, errors::AtomicServerResult}; @@ -290,7 +290,9 @@ pub fn get_subject( req: &actix_web::HttpRequest, conn: &actix_web::dev::ConnectionInfo, appstate: &AppState, -) -> AtomicServerResult { +) -> AtomicServerResult<(String, ContentType)> { + let content_type = get_accept(req.headers()); + let domain = &appstate.config.opts.domain; let host = conn.host(); let subdomain = if let Some(index) = host.find(domain) { @@ -309,23 +311,48 @@ pub fn get_subject( } let server_without_last_slash = subject_url.to_string().trim_end_matches('/').to_string(); let subject = format!("{}{}", server_without_last_slash, &req.uri().to_string()); - Ok(subject) + // if let Some((ct, path)) = try_extension(req.path()) { + // content_type = ct; + // return Ok((path.to_string(), content_type)); + // } + Ok((subject, content_type)) } -/// Finds the extension -pub fn try_extension(path: &str) -> Option<(ContentType, &str)> { - let items: Vec<&str> = path.split('.').collect(); - if items.len() == 2 { - let path = items[0]; - let content_type = match items[1] { - "json" => ContentType::Json, - "jsonld" => ContentType::JsonLd, - "jsonad" => ContentType::JsonAd, - "html" => ContentType::Html, - "ttl" => ContentType::Turtle, - _ => return None, - }; - return Some((content_type, path)); +/// Finds the extension of a supported serialization format. +/// Not used right now, see: https://github.com/atomicdata-dev/atomic-data-rust/issues/601 +#[allow(dead_code)] +fn try_extension(path: &str) -> Option<(ContentType, &str)> { + // Check if path ends with one of the folliwing extensions + let extensions = [ + ".json", + ".jsonld", + ".jsonad", + ".html", + ".ttl", + ".nt", + ".nq", + ".ntriples", + ".nt", + ]; + let mut found = None; + for ext in extensions.iter() { + if path.ends_with(ext) { + println!("Found extension: {}", ext); + let path = &path[0..path.len() - ext.len()]; + let content_type = match *ext { + ".json" => Some(ContentType::Json), + ".jsonld" => Some(ContentType::JsonLd), + ".jsonad" => Some(ContentType::JsonAd), + ".html" => Some(ContentType::Html), + ".ttl" => Some(ContentType::Turtle), + ".nt" => Some(ContentType::NTriples), + ".ntriples" => Some(ContentType::NTriples), + _ => None, + }; + if let Some(ct) = content_type { + found = Some((ct, path)); + } + } } - None + found } From 070265de5df6f68f69aaab8954bea5c42af5f591 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:33:01 +0200 Subject: [PATCH 31/51] Do not add `register` endpoint if there is no SMTP server --- lib/src/db.rs | 42 ++++++++++++++++++++++++++++++----- lib/src/endpoints.rs | 2 +- lib/src/plugins/add_pubkey.rs | 2 +- lib/src/plugins/mod.rs | 18 --------------- lib/src/populate.rs | 3 +-- server/src/appstate.rs | 17 ++++++++++---- 6 files changed, 53 insertions(+), 31 deletions(-) diff --git a/lib/src/db.rs b/lib/src/db.rs index 08b985c5b..31f0291be 100644 --- a/lib/src/db.rs +++ b/lib/src/db.rs @@ -24,19 +24,20 @@ use trees::{Method, Operation, Transaction, Tree}; use crate::{ agents::ForAgent, - atomic_url::AtomicUrl, + atomic_url::{AtomicUrl, Routes}, atoms::IndexAtom, commit::{CommitOpts, CommitResponse}, db::{query_index::requires_query_index, val_prop_sub_index::find_in_val_prop_sub_index}, email::{self, MailMessage}, - endpoints::{default_endpoints, Endpoint, HandleGetContext}, + endpoints::{build_default_endpoints, Endpoint, HandleGetContext}, errors::{AtomicError, AtomicResult}, + plugins, query::QueryResult, resources::PropVals, storelike::Storelike, urls, values::SortableValue, - Atom, Commit, Query, Resource, + Atom, Commit, Query, Resource, Value, }; use self::{ @@ -119,7 +120,7 @@ impl Db { prop_val_sub_index, server_url: AtomicUrl::try_from(server_url)?, watched_queries, - endpoints: default_endpoints(), + endpoints: Vec::new(), handle_commit: None, smtp_client: None, }; @@ -294,6 +295,28 @@ impl Db { Some(Resource::from_propvals(propvals, subject)) } + pub fn register_default_endpoints(&mut self) -> AtomicResult<()> { + // First we delete all existing endpoint resources, as they might not be there in this new run + let found_endpoints = self.query(&Query::new_class(urls::ENDPOINT))?.resources; + + for mut found in found_endpoints { + found.destroy(self)?; + } + + let mut endpoints = build_default_endpoints(); + + if self.smtp_client.is_some() { + endpoints.push(plugins::register::register_endpoint()); + endpoints.push(plugins::register::confirm_email_endpoint()); + } + + for endpoint in endpoints { + self.register_endpoint(endpoint)?; + } + + Ok(()) + } + fn build_index_for_atom( &self, atom: &IndexAtom, @@ -487,8 +510,17 @@ impl Db { } /// Adds an [Endpoint] to the store. This means adding a route with custom behavior. - pub fn register_endpoint(&mut self, endpoint: Endpoint) { + pub fn register_endpoint(&mut self, endpoint: Endpoint) -> AtomicResult<()> { + let mut resource = endpoint.to_resource(self)?; + let endpoints_collection = self.get_server_url().set_route(Routes::Endpoints); + resource.set( + urls::PARENT.into(), + Value::AtomicUrl(endpoints_collection.to_string()), + self, + )?; + resource.save_locally(self)?; self.endpoints.push(endpoint); + Ok(()) } /// Registers an SMTP client to the store, allowing the store to send emails. diff --git a/lib/src/endpoints.rs b/lib/src/endpoints.rs index afebb27e1..0391b9eeb 100644 --- a/lib/src/endpoints.rs +++ b/lib/src/endpoints.rs @@ -87,7 +87,7 @@ impl std::fmt::Debug for Endpoint { } } -pub fn default_endpoints() -> Vec { +pub fn build_default_endpoints() -> Vec { vec![ plugins::versioning::version_endpoint(), plugins::versioning::all_versions_endpoint(), diff --git a/lib/src/plugins/add_pubkey.rs b/lib/src/plugins/add_pubkey.rs index 33c8f769c..2d230cdf2 100644 --- a/lib/src/plugins/add_pubkey.rs +++ b/lib/src/plugins/add_pubkey.rs @@ -128,7 +128,7 @@ pub fn handle_confirm_add_pubkey(context: HandleGetContext) -> AtomicResult Vec { - vec![ - versioning::version_endpoint(), - versioning::all_versions_endpoint(), - path::path_endpoint(), - search::search_endpoint(), - files::upload_endpoint(), - register::register_endpoint(), - register::confirm_email_endpoint(), - add_pubkey::request_email_add_pubkey(), - add_pubkey::confirm_add_pubkey(), - #[cfg(feature = "html")] - bookmark::bookmark_endpoint(), - ] -} diff --git a/lib/src/populate.rs b/lib/src/populate.rs index 05601b78b..42fc4df70 100644 --- a/lib/src/populate.rs +++ b/lib/src/populate.rs @@ -315,7 +315,7 @@ pub fn populate_collections(store: &impl Storelike) -> AtomicResult<()> { pub fn populate_endpoints(store: &crate::Db) -> AtomicResult<()> { use crate::atomic_url::Routes; - let endpoints = crate::plugins::default_endpoints(); + let endpoints = crate::endpoints::build_default_endpoints(); let endpoints_collection = store.get_server_url().set_route(Routes::Endpoints); for endpoint in endpoints { let mut resource = endpoint.to_resource(store)?; @@ -363,7 +363,6 @@ pub fn populate_all(store: &crate::Db) -> AtomicResult<()> { .map_err(|e| format!("Failed to create default ontology. {}", e))?; set_drive_rights(store, true)?; populate_collections(store).map_err(|e| format!("Failed to populate collections. {}", e))?; - populate_endpoints(store).map_err(|e| format!("Failed to populate endpoints. {}", e))?; populate_sidebar_items(store) .map_err(|e| format!("Failed to populate sidebar items. {}", e))?; Ok(()) diff --git a/server/src/appstate.rs b/server/src/appstate.rs index d65def82a..b94960df2 100644 --- a/server/src/appstate.rs +++ b/server/src/appstate.rs @@ -27,6 +27,17 @@ pub struct AppState { pub search_state: SearchState, } +/// Initializes the Store and sets the default agent. +pub fn init_store(config: &Config) -> AtomicServerResult { + let mut store = atomic_lib::Db::init(&config.store_path, &config.server_url)?; + + tracing::info!("Setting default agent"); + set_default_agent(config, &store)?; + store.register_default_endpoints()?; + + Ok(store) +} + impl AppState { /// Creates the AppState (the server's context available in Handlers). /// Initializes or opens a store on disk. @@ -42,7 +53,7 @@ impl AppState { tracing::warn!("Development mode is enabled. This will use staging environments for services like LetsEncrypt."); } - let mut store = atomic_lib::Db::init(&config.store_path, config.server_url.clone())?; + let mut store = atomic_lib::Db::init(&config.store_path, &config.server_url.clone())?; let no_server_resource = store.get_resource(&config.server_url).is_err(); if no_server_resource { tracing::warn!("Server URL resource not found. This is likely because the server URL has changed. Initializing a new database..."); @@ -54,8 +65,6 @@ impl AppState { .map_err(|e| format!("Failed to populate default store. {}", e))?; } - set_default_agent(&config, &store)?; - // Initialize search constructs let search_state = SearchState::new(&config) .map_err(|e| format!("Failed to start search service: {}", e))?; @@ -183,8 +192,8 @@ fn set_default_agent(config: &Config, store: &impl Storelike) -> AtomicServerRes created_at: 0, name: None, }; - tracing::info!("Default Agent is set: {}", &agent.subject); store.set_default_agent(agent); + tracing::info!("Default Agent is set: {}", &agent.subject); Ok(()) } From d09b138b8a67931c9fcb99d2ed3f1fb996c83176 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:33:01 +0200 Subject: [PATCH 32/51] Fix issues? --- lib/src/populate.rs | 3 +-- lib/src/resources.rs | 2 ++ lib/src/urls.rs | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/src/populate.rs b/lib/src/populate.rs index 42fc4df70..f53ec23a4 100644 --- a/lib/src/populate.rs +++ b/lib/src/populate.rs @@ -315,9 +315,8 @@ pub fn populate_collections(store: &impl Storelike) -> AtomicResult<()> { pub fn populate_endpoints(store: &crate::Db) -> AtomicResult<()> { use crate::atomic_url::Routes; - let endpoints = crate::endpoints::build_default_endpoints(); let endpoints_collection = store.get_server_url().set_route(Routes::Endpoints); - for endpoint in endpoints { + for endpoint in crate::endpoints::build_default_endpoints() { let mut resource = endpoint.to_resource(store)?; resource.set( urls::PARENT.into(), diff --git a/lib/src/resources.rs b/lib/src/resources.rs index f143566d1..3585bc823 100644 --- a/lib/src/resources.rs +++ b/lib/src/resources.rs @@ -2,8 +2,10 @@ //! Has methods for saving resources and getting properties inside them. use crate::commit::{CommitOpts, CommitResponse}; +use crate::urls; use crate::utils::random_string; use crate::values::{SubResource, Value}; +use crate::Query; use crate::{commit::CommitBuilder, errors::AtomicResult}; use crate::{ mapping::is_url, diff --git a/lib/src/urls.rs b/lib/src/urls.rs index f801af7e4..c53f07406 100644 --- a/lib/src/urls.rs +++ b/lib/src/urls.rs @@ -173,6 +173,7 @@ pub const PATH_CONFIRM_EMAIL: &str = "/confirm-email"; pub const PATH_RESET_PUBKEY: &str = "/reset-public-key"; pub const PATH_CONFIRM_PUBKEY: &str = "/confirm-public-key"; pub const PATH_ADD_PUBKEY: &str = "/add-public-key"; +pub const PATH_CONFIRM_PUBKEY: &str = "/confirm-add-public-key"; pub const PATH_CONFIRM_RESET: &str = "/confirm-reset-public-key"; pub const PATH_QUERY: &str = "/query"; pub const PATH_PRUNE_TESTS: &str = "/prunetests"; From dcaa9d725415d1e66c2706baec93ee39fee6156a Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:33:01 +0200 Subject: [PATCH 33/51] Add register form + email confirm #640 #544 #489 #276 --- CONTRIBUTING.md | 1 + browser/CHANGELOG.md | 13 + .../data-browser/src/components/CodeBlock.tsx | 15 +- .../src/components/Dialog/useDialog.tsx | 10 +- .../data-browser/src/components/ErrorLook.tsx | 20 +- browser/data-browser/src/components/Guard.tsx | 17 + .../src/components/RegisterSignIn.tsx | 299 +++++++ .../src/components/SettingsAgent.tsx | 198 +++++ .../src/components/SideBar/DriveSwitcher.tsx | 12 +- .../forms/FileDropzone/FileDropzoneInput.tsx | 2 +- .../CustomForms/NewBookmarkDialog.tsx | 58 +- .../src/components/forms/ResourceSelector.tsx | 206 +++++ .../data-browser/src/helpers/AppSettings.tsx | 3 +- .../data-browser/src/hooks/useSavedDrives.ts | 6 +- browser/data-browser/src/hooks/useUpload.ts | 3 +- .../data-browser/src/routes/ConfirmEmail.tsx | 108 +++ .../src/routes/NewResource/NewRoute.tsx | 10 +- browser/data-browser/src/routes/Routes.tsx | 9 +- .../data-browser/src/routes/SearchRoute.tsx | 2 +- .../data-browser/src/routes/SettingsAgent.tsx | 272 +----- browser/data-browser/src/routes/paths.tsx | 1 + .../data-browser/src/views/ChatRoomPage.tsx | 42 +- .../data-browser/src/views/CollectionPage.tsx | 4 +- browser/data-browser/src/views/CrashPage.tsx | 29 +- browser/data-browser/src/views/DrivePage.tsx | 60 +- browser/data-browser/src/views/ErrorPage.tsx | 17 +- browser/data-browser/tests/e2e.spec.ts | 841 ++++++++++++++++++ browser/lib/src/authentication.ts | 174 +++- browser/lib/src/store.ts | 12 + browser/lib/src/urls.ts | 4 + browser/react/src/index.ts | 1 + browser/react/src/useServerSupports.ts | 20 + lib/src/commit.rs | 1 - lib/src/db.rs | 11 +- lib/src/db/test.rs | 1 + lib/src/resources.rs | 1 - lib/src/urls.rs | 1 - server/build.rs | 2 +- 38 files changed, 2109 insertions(+), 377 deletions(-) create mode 100644 browser/data-browser/src/components/Guard.tsx create mode 100644 browser/data-browser/src/components/RegisterSignIn.tsx create mode 100644 browser/data-browser/src/components/SettingsAgent.tsx create mode 100644 browser/data-browser/src/components/forms/ResourceSelector.tsx create mode 100644 browser/data-browser/src/routes/ConfirmEmail.tsx create mode 100644 browser/data-browser/tests/e2e.spec.ts create mode 100644 browser/react/src/useServerSupports.ts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index aae4512ab..ce41983eb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -50,6 +50,7 @@ TL;DR Clone the repo and run `cargo run` from each folder (e.g. `cli` or `server - Visit your `localhost` in your locally running `atomic-data-browser` instance: (e.g. `http://localhost:5173/app/show?subject=http%3A%2F%2Flocalhost`) - use `cargo watch -- cargo run` to automatically recompile `atomic-server` when you update JS assets in `browser` - use `cargo watch -- cargo run --bin atomic-server -- --env-file server/.env` to automatically recompile `atomic-server` when you update code or JS assets. +- If you want to debug emails: `brew install mailhog` => `mailhog` => `http://localhost:8025` and add `ATOMIC_SMTP_HOST=localhost` `ATOMIC_SMTP_PORT=1025` to your `.env`. ### IDE setup (VSCode) diff --git a/browser/CHANGELOG.md b/browser/CHANGELOG.md index 41b45e0f5..7204e5cfe 100644 --- a/browser/CHANGELOG.md +++ b/browser/CHANGELOG.md @@ -243,11 +243,18 @@ This changelog covers all five packages, as they are (for now) updated as a whol - Add `store.getResourceAncestry` method, which returns the ancestry of a resource, including the resource itself. - Add `resource.title` property, which returns the name of a resource, or the first property that is can be used to name the resource. - `store.createSubject` now accepts a `parent` argument, which allows creating nested subjects. +- Add `store.getServerSupports` to know which features a Server supports + +### @tomic/react + +- Add `useServerSupports` hook to see supported features of the server ## v0.35.0 ### @tomic/browser +- Let users register using e-mail address, improve sign-up UX. +- Add `Store.parseMetaTags` to load JSON-AD objects stored in the DOM. Speeds up initial page load by allowing server to set JSON-AD objects in the initial HTML response. - Move static assets around, align build with server and fix PWA #292 - Add `useChildren` hook and `Store.getChildren` method - Add new file preview UI for images, audio, text and PDF files. @@ -255,8 +262,14 @@ This changelog covers all five packages, as they are (for now) updated as a whol - Fix Dialogue form #308 - Refactor search, escape query strings for Tantivy - Add `import` context menu, allows importing anywhere +- Let users register using e-mail address, improve sign-up UX. ### @tomic/react +- `store.createSubject` allows creating nested paths +- `store.createSubject` allows creating nested paths +- Add `useChildren` hook and `Store.getChildren` method +- Add `Store.postToServer` method, add `endpoints`, `import_json_ad_string` +- Add `store.preloadClassesAndProperties` and remove `urls.properties.getAll` and `urls.classes.getAll`. This enables using `atomic-data-browser` without relying on `atomicdata.dev` being available. - Add more options to `useSearch` diff --git a/browser/data-browser/src/components/CodeBlock.tsx b/browser/data-browser/src/components/CodeBlock.tsx index aafe0fa56..fc6b85e68 100644 --- a/browser/data-browser/src/components/CodeBlock.tsx +++ b/browser/data-browser/src/components/CodeBlock.tsx @@ -7,9 +7,11 @@ import { Button } from './Button'; interface CodeBlockProps { content?: string; loading?: boolean; + wrapContent?: boolean; } -export function CodeBlock({ content, loading }: CodeBlockProps) { +/** Codeblock with copy feature */ +export function CodeBlock({ content, loading, wrapContent }: CodeBlockProps) { const [isCopied, setIsCopied] = useState(undefined); function copyToClipboard() { @@ -19,7 +21,7 @@ export function CodeBlock({ content, loading }: CodeBlockProps) { } return ( - + {loading ? ( 'loading...' ) : ( @@ -46,7 +48,12 @@ export function CodeBlock({ content, loading }: CodeBlockProps) { ); } -export const CodeBlockStyled = styled.pre` +interface Props { + /** Renders all in a single line */ + wrapContent?: boolean; +} + +export const CodeBlockStyled = styled.pre` position: relative; background-color: ${p => p.theme.colors.bg1}; border-radius: ${p => p.theme.radius}; @@ -55,4 +62,6 @@ export const CodeBlockStyled = styled.pre` font-family: monospace; width: 100%; overflow-x: auto; + word-wrap: ${p => (p.wrapContent ? 'break-word' : 'initial')}; + white-space: ${p => (p.wrapContent ? 'pre-wrap' : 'pre')}; `; diff --git a/browser/data-browser/src/components/Dialog/useDialog.tsx b/browser/data-browser/src/components/Dialog/useDialog.tsx index 7ef7d5740..ffbd9510a 100644 --- a/browser/data-browser/src/components/Dialog/useDialog.tsx +++ b/browser/data-browser/src/components/Dialog/useDialog.tsx @@ -1,16 +1,16 @@ import { useCallback, useMemo, useState } from 'react'; import { InternalDialogProps } from './index'; -export type UseDialogReturnType = [ +export type UseDialogReturnType = { /** Props meant to pass to a {@link Dialog} component */ - dialogProps: InternalDialogProps, + dialogProps: InternalDialogProps; /** Function to show the dialog */ - show: () => void, + show: () => void; /** Function to close the dialog */ close: (success?: boolean) => void, /** Boolean indicating wether the dialog is currently open */ - isOpen: boolean, -]; + isOpen: boolean; +}; export type UseDialogOptions = { bindShow?: React.Dispatch; diff --git a/browser/data-browser/src/components/ErrorLook.tsx b/browser/data-browser/src/components/ErrorLook.tsx index 71c1469dd..d6ba24275 100644 --- a/browser/data-browser/src/components/ErrorLook.tsx +++ b/browser/data-browser/src/components/ErrorLook.tsx @@ -2,6 +2,7 @@ import { lighten } from 'polished'; import { styled, css } from 'styled-components'; import { FaExclamationTriangle } from 'react-icons/fa'; +import { Column } from './Row'; export const errorLookStyle = css` color: ${props => props.theme.colors.alert}; @@ -25,18 +26,21 @@ export function ErrorBlock({ error, showTrace }: ErrorBlockProps): JSX.Element { Something went wrong -
-        {error.message}
+      
+        {error.message}
         {showTrace && (
           <>
-            
-
- Stack trace: -
- {error.stack} + Stack trace: + + {error.stack} + )} -
+ ); } diff --git a/browser/data-browser/src/components/Guard.tsx b/browser/data-browser/src/components/Guard.tsx new file mode 100644 index 000000000..60554e845 --- /dev/null +++ b/browser/data-browser/src/components/Guard.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { useSettings } from '../helpers/AppSettings'; +import { RegisterSignIn } from './RegisterSignIn'; + +/** + * The Guard can be wrapped around a Component that depends on a user being logged in. + * If the user is not logged in, it will show a button to sign up / sign in. + * Show to users after a new Agent has been created. + * Instructs them to save their secret somewhere safe + */ +export function Guard({ children }: React.PropsWithChildren): JSX.Element { + const { agent } = useSettings(); + + if (agent) { + return <>{children}; + } else return ; +} diff --git a/browser/data-browser/src/components/RegisterSignIn.tsx b/browser/data-browser/src/components/RegisterSignIn.tsx new file mode 100644 index 000000000..e893114cc --- /dev/null +++ b/browser/data-browser/src/components/RegisterSignIn.tsx @@ -0,0 +1,299 @@ +import { + Dialog, + DialogActions, + DialogContent, + DialogTitle, + useDialog, +} from './Dialog'; +import React, { FormEvent, useCallback, useEffect, useState } from 'react'; +import { useSettings } from '../helpers/AppSettings'; +import { Button } from './Button'; +import { + addPublicKey, + nameRegex, + register as createRegistration, + useServerSupports, + useServerURL, + useStore, +} from '@tomic/react'; +import Field from './forms/Field'; +import { InputWrapper, InputStyled } from './forms/InputStyles'; +import { Row } from './Row'; +import { ErrorLook } from './ErrorLook'; +import { SettingsAgent } from './SettingsAgent'; + +interface RegisterSignInProps { + // URL where to send the user to after successful registration + redirect?: string; +} + +/** What is currently showing */ +enum PageStateOpts { + none, + signIn, + register, + reset, + mailSentRegistration, + mailSentAddPubkey, +} + +/** + * Two buttons: Register / Sign in. + * Opens a Dialog / Modal with the appropriate form. + */ +export function RegisterSignIn({ + children, +}: React.PropsWithChildren): JSX.Element { + const { dialogProps, show, close } = useDialog(); + const { agent } = useSettings(); + const [pageState, setPageState] = useState(PageStateOpts.none); + const [email, setEmail] = useState(''); + const { emailRegister } = useServerSupports(); + + if (agent) { + return <>{children}; + } else if (!emailRegister) { + return ; + } + + return ( + <> + + + + + + {pageState === PageStateOpts.register && ( + + )} + {pageState === PageStateOpts.signIn && ( + + )} + {pageState === PageStateOpts.reset && ( + + )} + {pageState === PageStateOpts.mailSentRegistration && ( + + )} + {pageState === PageStateOpts.mailSentAddPubkey && ( + + )} + + + ); +} + +function Reset({ email, setEmail, setPageState }) { + const store = useStore(); + const [err, setErr] = useState(undefined); + + const handleRequestReset = useCallback(async () => { + try { + await addPublicKey(store, email); + setPageState(PageStateOpts.mailSentAddPubkey); + } catch (e) { + setErr(e); + } + }, [email]); + + return ( + <> + +

Reset your PassKey

+
+ +

+ { + "Lost it? No worries, we'll send a link that let's you create a new one." + } +

+ { + setErr(undefined); + setEmail(e); + }} + /> + {err && {err.message}} +
+ + + + + ); +} + +function MailSentConfirm({ email, close, message }) { + return ( + <> + +

Go to your email inbox

+
+ +

+ {"We've sent a confirmation link to "} + {email} + {'.'} +

+

{message}

+
+ + + + + ); +} + +function Register({ setPageState, email, setEmail }) { + const [name, setName] = useState(''); + const [serverUrlStr] = useServerURL(); + const [nameErr, setErr] = useState(undefined); + const store = useStore(); + + const serverUrl = new URL(serverUrlStr); + serverUrl.host = `${name}.${serverUrl.host}`; + + useEffect(() => { + // check regex of name, set error + if (!name.match(nameRegex)) { + setErr(new Error('Name must be lowercase and only contain numbers')); + } else { + setErr(undefined); + } + }, [name, email]); + + const handleSubmit = useCallback( + async (event: FormEvent) => { + event.preventDefault(); + + if (!name) { + setErr(new Error('Name is required')); + + return; + } + + try { + await createRegistration(store, name, email); + setPageState(PageStateOpts.mailSentRegistration); + } catch (er) { + setErr(er); + } + }, + [name, email], + ); + + return ( + <> + +

Register

+
+ +
+ + + { + setName(e.target.value); + }} + /> + + + + {name && nameErr && {nameErr.message}} + +
+ + + + + + ); +} + +function SignIn({ setPageState }) { + return ( + <> + +

Sign in

+
+ + + + + + + + + ); +} + +function EmailField({ setEmail, email }) { + return ( + + + { + setEmail(e.target.value); + }} + /> + + + ); +} diff --git a/browser/data-browser/src/components/SettingsAgent.tsx b/browser/data-browser/src/components/SettingsAgent.tsx new file mode 100644 index 000000000..edda04afa --- /dev/null +++ b/browser/data-browser/src/components/SettingsAgent.tsx @@ -0,0 +1,198 @@ +import { Agent } from '@tomic/react'; +import React, { useState } from 'react'; +import { FaCog, FaEye, FaEyeSlash } from 'react-icons/fa'; +import { useSettings } from '../helpers/AppSettings'; +import { ButtonInput } from './Button'; +import Field from './forms/Field'; +import { InputStyled, InputWrapper } from './forms/InputStyles'; + +/** Form where users can post their Private Key, or edit their Agent */ +export const SettingsAgent: React.FunctionComponent = () => { + const { agent, setAgent } = useSettings(); + const [subject, setSubject] = useState(undefined); + const [privateKey, setPrivateKey] = useState(undefined); + const [error, setError] = useState(undefined); + const [showPrivateKey, setShowPrivateKey] = useState(false); + const [advanced, setAdvanced] = useState(false); + const [secret, setSecret] = useState(undefined); + + // When there is an agent, set the advanced values + // Otherwise, reset the secret value + React.useEffect(() => { + if (agent !== undefined) { + fillAdvanced(); + } else { + setSecret(''); + } + }, [agent]); + + // When the key or subject changes, update the secret + React.useEffect(() => { + renewSecret(); + }, [subject, privateKey]); + + function renewSecret() { + if (agent) { + setSecret(agent.buildSecret()); + } + } + + function fillAdvanced() { + try { + if (!agent) { + throw new Error('No agent set'); + } + + setSubject(agent.subject); + setPrivateKey(agent.privateKey); + } catch (e) { + const err = new Error('Cannot fill subject and privatekey fields.' + e); + setError(err); + setSubject(''); + } + } + + function setAgentIfChanged(oldAgent: Agent | undefined, newAgent: Agent) { + if (JSON.stringify(oldAgent) !== JSON.stringify(newAgent)) { + setAgent(newAgent); + } + } + + /** Called when the secret or the subject is updated manually */ + async function handleUpdateSubjectAndKey() { + renewSecret(); + setError(undefined); + + try { + const newAgent = new Agent(privateKey!, subject); + await newAgent.getPublicKey(); + await newAgent.verifyPublicKeyWithServer(); + + setAgentIfChanged(agent, newAgent); + } catch (e) { + const err = new Error('Invalid Agent' + e); + setError(err); + } + } + + function handleCopy() { + secret && navigator.clipboard.writeText(secret); + } + + /** When the Secret updates, parse it and try if the */ + async function handleUpdateSecret(updateSecret: string) { + setSecret(updateSecret); + + if (updateSecret === '') { + setSecret(''); + setError(undefined); + + return; + } + + setError(undefined); + + try { + const newAgent = Agent.fromSecret(updateSecret); + setAgentIfChanged(agent, newAgent); + setPrivateKey(newAgent.privateKey); + setSubject(newAgent.subject); + // This will fail and throw if the agent is not public, which is by default + // await newAgent.checkPublicKey(); + } catch (e) { + const err = new Error('Invalid secret. ' + e); + setError(err); + } + } + + return ( +
+ + + handleUpdateSecret(e.target.value)} + type={showPrivateKey ? 'text' : 'password'} + disabled={agent !== undefined} + name='secret' + id='current-password' + autoComplete='current-password' + spellCheck='false' + placeholder='Paste your Passphrase' + /> + setShowPrivateKey(!showPrivateKey)} + > + {showPrivateKey ? : } + + setAdvanced(!advanced)} + > + + + {agent && ( + + copy + + )} + + + {advanced ? ( + + + + { + setSubject(e.target.value); + handleUpdateSubjectAndKey(); + }} + /> + + + + + { + setPrivateKey(e.target.value); + handleUpdateSubjectAndKey(); + }} + /> + setShowPrivateKey(!showPrivateKey)} + > + {showPrivateKey ? : } + + + + + ) : null} +
+ ); +}; diff --git a/browser/data-browser/src/components/SideBar/DriveSwitcher.tsx b/browser/data-browser/src/components/SideBar/DriveSwitcher.tsx index 6d4ba73c7..27089ecae 100644 --- a/browser/data-browser/src/components/SideBar/DriveSwitcher.tsx +++ b/browser/data-browser/src/components/SideBar/DriveSwitcher.tsx @@ -1,4 +1,10 @@ -import { Resource, core, server, useResources } from '@tomic/react'; +import { + Resource, + server, + truncateUrl, + urls, + useResources, +} from '@tomic/react'; import { useMemo } from 'react'; import { FaCog, @@ -21,7 +27,8 @@ const Trigger = buildDefaultTrigger(, 'Open Drive Settings'); function getTitle(resource: Resource): string { return ( - (resource.get(core.properties.name) as string) ?? resource.getSubject() + (resource.get(urls.properties.name) as string) ?? + truncateUrl(resource.getSubject(), 20) ); } @@ -45,6 +52,7 @@ export function DriveSwitcher() { }; const createNewResource = useNewResourceUI(); + // const createNewDrive = useDefaultNewInstanceHandler(classes.drive); const items = useMemo( () => [ diff --git a/browser/data-browser/src/components/forms/FileDropzone/FileDropzoneInput.tsx b/browser/data-browser/src/components/forms/FileDropzone/FileDropzoneInput.tsx index 25e5a2f48..3d542b842 100644 --- a/browser/data-browser/src/components/forms/FileDropzone/FileDropzoneInput.tsx +++ b/browser/data-browser/src/components/forms/FileDropzone/FileDropzoneInput.tsx @@ -8,11 +8,11 @@ import { useUpload } from '../../../hooks/useUpload'; export interface FileDropzoneInputProps { parentResource: Resource; - onFilesUploaded?: (files: string[]) => void; text?: string; maxFiles?: number; className?: string; accept?: string[]; + onFilesUploaded?: (fileSubjects: string[]) => void; } /** diff --git a/browser/data-browser/src/components/forms/NewForm/CustomCreateActions/CustomForms/NewBookmarkDialog.tsx b/browser/data-browser/src/components/forms/NewForm/CustomCreateActions/CustomForms/NewBookmarkDialog.tsx index f2b7b4811..482f56bbf 100644 --- a/browser/data-browser/src/components/forms/NewForm/CustomCreateActions/CustomForms/NewBookmarkDialog.tsx +++ b/browser/data-browser/src/components/forms/NewForm/CustomCreateActions/CustomForms/NewBookmarkDialog.tsx @@ -27,7 +27,7 @@ export const NewBookmarkDialog: FC = ({ }) => { const [url, setUrl] = useState(''); - const [dialogProps, show, hide] = useDialog({ onCancel: onClose }); + const { dialogProps, show, close } = useDialog({ onCancel: onClose }); const createResourceAndNavigate = useCreateAndNavigate(); @@ -58,32 +58,34 @@ export const NewBookmarkDialog: FC = ({ }, []); return ( - - -

New Bookmark

-
- -
- - - setUrl(e.target.value)} - /> - - -
-
- - - - -
+ <> + + +

New Bookmark

+
+ +
+ + + setUrl(e.target.value)} + /> + + +
+
+ + + + +
+ ); }; diff --git a/browser/data-browser/src/components/forms/ResourceSelector.tsx b/browser/data-browser/src/components/forms/ResourceSelector.tsx new file mode 100644 index 000000000..558b76299 --- /dev/null +++ b/browser/data-browser/src/components/forms/ResourceSelector.tsx @@ -0,0 +1,206 @@ +import { + ArrayError, + urls, + useArray, + useResource, + useStore, + useTitle, +} from '@tomic/react'; +import React, { + Dispatch, + SetStateAction, + useContext, + useState, + useCallback, +} from 'react'; +import { ErrMessage, InputWrapper } from './InputStyles'; +import { DropdownInput } from './DropdownInput'; +import { Dialog, useDialog } from '../Dialog'; +import { DialogTreeContext } from '../Dialog/dialogContext'; +import { useSettings } from '../../helpers/AppSettings'; +import styled from 'styled-components'; +import { NewFormDialog } from './NewForm/NewFormDialog'; + +interface ResourceSelectorProps { + /** + * Whether a certain type of Class is required here. Pass the URL of the + * class. Is used for constructing a list of options. + */ + classType?: string; + /** If true, the form will show an error if it is left empty. */ + required?: boolean; + /** + * This callback is called when the Subject Changes. You can pass an Error + * Handler as the second argument to set an error message. Take the second + * argument of a `useString` hook and pass the setString part to this property + */ + setSubject: ( + subject: string | undefined, + errHandler?: Dispatch>, + ) => void; + /** The value (URL of the Resource that is selected) */ + value?: string; + /** A function to remove this item. Only relevant in arrays. */ + handleRemove?: () => void; + /** Only pass an error if it is applicable to this specific field */ + error?: Error; + /** + * Set an ArrayError. A special type, because the parent needs to know where + * in the Array the error occurred + */ + setError?: Dispatch>; + disabled?: boolean; + autoFocus?: boolean; + /** Is used when a new item is created using the ResourceSelector */ + parent?: string; +} + +/** + * Form field for selecting a single resource. Needs external subject & + * setSubject properties + */ +export const ResourceSelector = React.memo(function ResourceSelector({ + required, + setSubject, + value, + handleRemove, + error, + setError, + classType, + disabled, + parent, + ...props +}: ResourceSelectorProps): JSX.Element { + // TODO: This list should use the user's Pod instead of a hardcoded collection; + const classesCollection = useResource(getCollectionURL(classType)); + let [options] = useArray( + classesCollection, + urls.properties.collection.members, + ); + const requiredClass = useResource(classType); + const [classTypeTitle] = useTitle(requiredClass); + const store = useStore(); + const { + dialogProps, + show: showDialog, + close: closeDialog, + isOpen: isDialogOpen, + } = useDialog(); + const { drive } = useSettings(); + + const [ + /** The value of the input underneath, updated through a callback */ + inputValue, + setInputValue, + ] = useState(value || ''); + + const handleUpdate = useCallback( + (newval?: string) => { + // Pass the error setter for validation purposes + // Pass the Error handler to its parent, so validation errors appear locally + setSubject(newval, setError); + // Reset the error every time anything changes + // setError(null); + }, + [setSubject], + ); + + const handleBlur = useCallback(() => { + value === undefined && handleUpdate(inputValue); + }, [inputValue, value]); + + const isInDialogTree = useContext(DialogTreeContext); + + if (options.length === 0) { + options = store.getAllSubjects(); + } + + let placeholder = 'Enter an Atomic URL...'; + + if (classType && classTypeTitle?.length > 0) { + placeholder = `Select a ${classTypeTitle} or enter a ${classTypeTitle} URL...`; + } + + if (classType && !requiredClass.isReady()) { + placeholder = 'Loading Class...'; + } + + return ( + + + {value && value !== '' && error && ( + {error?.message} + )} + {!isInDialogTree && ( + + {isDialogOpen && ( + + )} + + )} + {required && value === '' && Required} + + ); +}); + +/** For a given class URL, this tries to return a URL of a Collection containing these. */ +// TODO: Scope to current store / make adjustable https://github.com/atomicdata-dev/atomic-data-browser/issues/295 +export function getCollectionURL(classtypeUrl?: string): string | undefined { + switch (classtypeUrl) { + case urls.classes.property: + return 'https://atomicdata.dev/properties/?page_size=999'; + case urls.classes.class: + return 'https://atomicdata.dev/classes/?page_size=999'; + case urls.classes.agent: + return 'https://atomicdata.dev/agents/'; + case urls.classes.commit: + return 'https://atomicdata.dev/commits'; + case urls.classes.datatype: + return 'https://atomicdata.dev/datatypes'; + default: + return undefined; + } +} + +const Wrapper = styled.div` + flex: 1; + --radius: ${props => props.theme.radius}; + ${InputWrapper} { + border-radius: 0; + } + + &:first-of-type ${InputWrapper} { + border-top-left-radius: var(--radius); + border-top-right-radius: var(--radius); + } + + &:last-of-type ${InputWrapper} { + border-bottom-left-radius: var(--radius); + border-bottom-right-radius: var(--radius); + } + + &:not(:last-of-type) ${InputWrapper} { + border-bottom: none; + } +`; diff --git a/browser/data-browser/src/helpers/AppSettings.tsx b/browser/data-browser/src/helpers/AppSettings.tsx index fb52ca4eb..9248bba7e 100644 --- a/browser/data-browser/src/helpers/AppSettings.tsx +++ b/browser/data-browser/src/helpers/AppSettings.tsx @@ -47,7 +47,8 @@ export const AppSettingsContextProvider = ( const [agent, setAgent] = useCurrentAgent(); const [baseURL, setBaseURL] = useServerURL(); - const [drive, innerSetDrive] = useLocalStorage('drive', baseURL); + // By default, we want to use the current URL's origin with a trailing slash. + const [drive, innerSetDrive] = useLocalStorage('drive', baseURL + '/'); const setDrive = useCallback( (newDrive: string) => { diff --git a/browser/data-browser/src/hooks/useSavedDrives.ts b/browser/data-browser/src/hooks/useSavedDrives.ts index a58e1b2eb..44c25ab88 100644 --- a/browser/data-browser/src/hooks/useSavedDrives.ts +++ b/browser/data-browser/src/hooks/useSavedDrives.ts @@ -4,9 +4,9 @@ import { isDev } from '../config'; import { useSettings } from '../helpers/AppSettings'; const rootDrives = [ - window.location.origin, - 'https://atomicdata.dev', - ...(isDev() ? ['http://localhost:9883'] : []), + window.location.origin + '/', + 'https://atomicdata.dev/', + ...(isDev() ? ['http://localhost:9883/'] : []), ]; const arrayOpts = { diff --git a/browser/data-browser/src/hooks/useUpload.ts b/browser/data-browser/src/hooks/useUpload.ts index c0b673436..49eadcc91 100644 --- a/browser/data-browser/src/hooks/useUpload.ts +++ b/browser/data-browser/src/hooks/useUpload.ts @@ -40,7 +40,8 @@ export function useUpload(parentResource: Resource): UseUploadResult { ); const allUploaded = [...netUploaded]; setIsUploading(false); - setSubResources([...subResources, ...allUploaded]); + await setSubResources([...subResources, ...allUploaded]); + await parentResource.save(store); return allUploaded; } catch (e) { diff --git a/browser/data-browser/src/routes/ConfirmEmail.tsx b/browser/data-browser/src/routes/ConfirmEmail.tsx new file mode 100644 index 000000000..23ca32e7f --- /dev/null +++ b/browser/data-browser/src/routes/ConfirmEmail.tsx @@ -0,0 +1,108 @@ +import { confirmEmail, useStore } from '@tomic/react'; +import * as React from 'react'; +import { useState } from 'react'; +import toast from 'react-hot-toast'; +import { Button } from '../components/Button'; +import { CodeBlockStyled } from '../components/CodeBlock'; +import { ContainerNarrow } from '../components/Containers'; +import { isDev } from '../config'; +import { useSettings } from '../helpers/AppSettings'; +import { + useCurrentSubject, + useSubjectParam, +} from '../helpers/useCurrentSubject'; +import { paths } from './paths'; + +/** Route that connects to `/confirm-email`, which confirms an email and creates a secret key. */ +const ConfirmEmail: React.FunctionComponent = () => { + // Value shown in navbar, after Submitting + const [subject] = useCurrentSubject(); + const [secret, setSecret] = useState(''); + const store = useStore(); + const [token] = useSubjectParam('token'); + const { setAgent } = useSettings(); + const [destinationToGo, setDestination] = useState(); + const [err, setErr] = useState(undefined); + const [triedConfirm, setTriedConfirm] = useState(false); + + const handleConfirm = React.useCallback(async () => { + setTriedConfirm(true); + let tokenUrl = subject as string; + + if (isDev()) { + const url = new URL(store.getServerUrl()); + url.pathname = paths.confirmEmail; + url.searchParams.set('token', token as string); + tokenUrl = url.href; + } + + try { + const { agent: newAgent, destination } = await confirmEmail( + store, + tokenUrl, + ); + setSecret(newAgent.buildSecret()); + setDestination(destination); + setAgent(newAgent); + toast.success('Email confirmed!'); + } catch (e) { + setErr(e); + } + }, [subject]); + + if (!triedConfirm && subject) { + handleConfirm(); + } + + if (err) { + if (err.message.includes('expired')) { + return ( + + The link has expired. Request a new one by Registering again. + + ); + } + + return {err?.message}; + } + + if (secret) { + return ; + } + + return Verifying token...; +}; + +function SavePassphrase({ secret, destination }) { + const [copied, setCopied] = useState(false); + + function copyToClipboard() { + setCopied(secret); + navigator.clipboard.writeText(secret || ''); + toast.success('Copied to clipboard'); + } + + return ( + +

Mail confirmed, please save your passphrase

+

+ Your Passphrase is like your password. Never share it with anyone. Use a + password manager like{' '} + + BitWarden + {' '} + to store it securely. +

+ {secret} + {copied ? ( + + {"I've saved my PassPhrase, open my new Drive!"} + + ) : ( + + )} +
+ ); +} + +export default ConfirmEmail; diff --git a/browser/data-browser/src/routes/NewResource/NewRoute.tsx b/browser/data-browser/src/routes/NewResource/NewRoute.tsx index edb9e307c..b4b27a1fc 100644 --- a/browser/data-browser/src/routes/NewResource/NewRoute.tsx +++ b/browser/data-browser/src/routes/NewResource/NewRoute.tsx @@ -62,11 +62,13 @@ function NewResourceSelector() { } const onUploadComplete = useCallback( - (files: string[]) => { - toast.success(`Uploaded ${files.length} files.`); + (fileSubjects: string[]) => { + toast.success(`Uploaded ${fileSubjects.length} files.`); - if (calculatedParent) { - navigate(constructOpenURL(calculatedParent)); + if (fileSubjects.length > 1 && parentSubject) { + navigate(constructOpenURL(parentSubject)); + } else { + navigate(constructOpenURL(fileSubjects[0])); } }, [calculatedParent, navigate], diff --git a/browser/data-browser/src/routes/Routes.tsx b/browser/data-browser/src/routes/Routes.tsx index 3c4cc7d40..c1e11f927 100644 --- a/browser/data-browser/src/routes/Routes.tsx +++ b/browser/data-browser/src/routes/Routes.tsx @@ -11,7 +11,7 @@ import Data from './DataRoute'; import { Shortcuts } from './ShortcutsRoute'; import { About as About } from './AboutRoute'; import Local from './LocalRoute'; -import SettingsAgent from './SettingsAgent'; +import { SettingsAgentRoute } from './SettingsAgent'; import { SettingsServer } from './SettingsServer'; import { paths } from './paths'; import ResourcePage from '../views/ResourcePage'; @@ -21,8 +21,10 @@ import { TokenRoute } from './TokenRoute'; import { ImporterPage } from '../views/ImporterPage'; import { History } from './History'; import { PruneTestsRoute } from './PruneTestsRoute'; +import ConfirmEmail from './ConfirmEmail'; -const homeURL = window.location.origin; +/** Server URLs should have a `/` at the end */ +const homeURL = window.location.origin + '/'; const isDev = import.meta.env.MODE === 'development'; @@ -37,7 +39,7 @@ export function AppRoutes(): JSX.Element { } /> } /> - } /> + } /> } /> } /> } /> @@ -51,6 +53,7 @@ export function AppRoutes(): JSX.Element { } /> {isDev && } />} {isDev && } />} + } /> } /> } /> diff --git a/browser/data-browser/src/routes/SearchRoute.tsx b/browser/data-browser/src/routes/SearchRoute.tsx index b39175f96..64835019c 100644 --- a/browser/data-browser/src/routes/SearchRoute.tsx +++ b/browser/data-browser/src/routes/SearchRoute.tsx @@ -95,7 +95,7 @@ export function Search(): JSX.Element { } if (loading) { - message = 'Loading results...'; + message = 'Loading results for'; } if (results.length > 0) { diff --git a/browser/data-browser/src/routes/SettingsAgent.tsx b/browser/data-browser/src/routes/SettingsAgent.tsx index 44dc5ed41..c646027d5 100644 --- a/browser/data-browser/src/routes/SettingsAgent.tsx +++ b/browser/data-browser/src/routes/SettingsAgent.tsx @@ -1,72 +1,19 @@ import * as React from 'react'; -import { useState } from 'react'; -import { Agent } from '@tomic/react'; -import { FaCog, FaEye, FaEyeSlash, FaUser } from 'react-icons/fa'; import { useSettings } from '../helpers/AppSettings'; -import { - InputStyled, - InputWrapper, - LabelStyled, -} from '../components/forms/InputStyles'; -import { ButtonInput, Button } from '../components/Button'; +import { Button } from '../components/Button'; import { Margin } from '../components/Card'; -import Field from '../components/forms/Field'; import { ResourceInline } from '../views/ResourceInline'; import { ContainerNarrow } from '../components/Containers'; -import { AtomicLink } from '../components/AtomicLink'; import { editURL } from '../helpers/navigation'; +import { Guard } from '../components/Guard'; import { useNavigate } from 'react-router'; -import { Main } from '../components/Main'; -import { Column } from '../components/Row'; -import { WarningBlock } from '../components/WarningBlock'; +import { SettingsAgent } from '../components/SettingsAgent'; -const SettingsAgent: React.FunctionComponent = () => { +export function SettingsAgentRoute() { const { agent, setAgent } = useSettings(); - const [subject, setSubject] = useState(undefined); - const [privateKey, setPrivateKey] = useState(undefined); - const [error, setError] = useState(undefined); - const [showPrivateKey, setShowPrivateKey] = useState(false); - const [advanced, setAdvanced] = useState(false); - const [secret, setSecret] = useState(undefined); const navigate = useNavigate(); - // When there is an agent, set the advanced values - // Otherwise, reset the secret value - React.useEffect(() => { - if (agent !== undefined) { - fillAdvanced(); - } else { - setSecret(''); - } - }, [agent]); - - // When the key or subject changes, update the secret - React.useEffect(() => { - renewSecret(); - }, [subject, privateKey]); - - function renewSecret() { - if (agent) { - setSecret(agent.buildSecret()); - } - } - - function fillAdvanced() { - try { - if (!agent) { - throw new Error('No agent set'); - } - - setSubject(agent.subject); - setPrivateKey(agent.privateKey); - } catch (e) { - const err = new Error('Cannot fill subject and privatekey fields.' + e); - setError(err); - setSubject(''); - } - } - function handleSignOut() { if ( window.confirm( @@ -74,196 +21,27 @@ const SettingsAgent: React.FunctionComponent = () => { ) ) { setAgent(undefined); - setError(undefined); - setSubject(''); - setPrivateKey(''); - } - } - - function setAgentIfChanged(oldAgent: Agent | undefined, newAgent: Agent) { - if (JSON.stringify(oldAgent) !== JSON.stringify(newAgent)) { - setAgent(newAgent); - } - } - - /** Called when the secret or the subject is updated manually */ - async function handleUpdateSubjectAndKey() { - renewSecret(); - setError(undefined); - - try { - const newAgent = new Agent(privateKey!, subject); - await newAgent.getPublicKey(); - await newAgent.verifyPublicKeyWithServer(); - - setAgentIfChanged(agent, newAgent); - } catch (e) { - const err = new Error('Invalid Agent' + e); - setError(err); - } - } - - function handleCopy() { - secret && navigator.clipboard.writeText(secret); - } - - /** When the Secret updates, parse it and try if the */ - async function handleUpdateSecret(updateSecret: string) { - setSecret(updateSecret); - - if (updateSecret === '') { - setSecret(''); - setError(undefined); - - return; - } - - setError(undefined); - - try { - const newAgent = Agent.fromSecret(updateSecret); - setAgentIfChanged(agent, newAgent); - setPrivateKey(newAgent.privateKey); - setSubject(newAgent.subject); - // This will fail and throw if the agent is not public, which is by default - // await newAgent.checkPublicKey(); - } catch (e) { - const err = new Error('Invalid secret. ' + e); - setError(err); } } return ( -
- -
-

User Settings

-

- An Agent is a user, consisting of a Subject (its URL) and Private - Key. Together, these can be used to edit data and sign Commits. -

- {agent ? ( - - {agent.subject?.startsWith('http://localhost') && ( - - Warning: - { - "You're using a local Agent, which cannot authenticate on other domains, because its URL does not resolve." - } - - )} -
- - You{"'"}re signed in as - - -
- - -
- ) : ( + +

User Settings

+

+ An Agent is a user, consisting of a Subject (its URL) and Private Key. + Together, these can be used to edit data and sign Commits. +

+ + {agent && ( + <>

- You can create your own Agent by hosting an{' '} - - atomic-server - - . Alternatively, you can use{' '} - - an Invite - {' '} - to get a guest Agent on someone else{"'s"} Atomic Server. +

- )} - - - handleUpdateSecret(e.target.value)} - type={showPrivateKey ? 'text' : 'password'} - disabled={agent !== undefined} - name='secret' - id='current-password' - autoComplete='current-password' - spellCheck='false' - /> - setShowPrivateKey(!showPrivateKey)} - > - {showPrivateKey ? : } - - setAdvanced(!advanced)} - > - - - {agent && ( - - copy - - )} - - - {advanced ? ( - <> - - - { - setSubject(e.target.value); - handleUpdateSubjectAndKey(); - }} - /> - - - - - { - setPrivateKey(e.target.value); - handleUpdateSubjectAndKey(); - }} - /> - setShowPrivateKey(!showPrivateKey)} - > - {showPrivateKey ? : } - - - - - ) : null} - {agent && ( + + + - )} - -
-
+ + )} + + ); -}; - -export default SettingsAgent; +} diff --git a/browser/data-browser/src/routes/paths.tsx b/browser/data-browser/src/routes/paths.tsx index 66531d2a7..bd6b27b3e 100644 --- a/browser/data-browser/src/routes/paths.tsx +++ b/browser/data-browser/src/routes/paths.tsx @@ -15,6 +15,7 @@ export const paths = { history: '/app/history', allVersions: '/all-versions', sandbox: '/sandbox', + confirmEmail: '/confirm-email', fetchBookmark: '/fetch-bookmark', pruneTests: '/prunetests', }; diff --git a/browser/data-browser/src/views/ChatRoomPage.tsx b/browser/data-browser/src/views/ChatRoomPage.tsx index ebd8eb281..562e30011 100644 --- a/browser/data-browser/src/views/ChatRoomPage.tsx +++ b/browser/data-browser/src/views/ChatRoomPage.tsx @@ -22,6 +22,7 @@ import { CommitDetail } from '../components/CommitDetail'; import Markdown from '../components/datatypes/Markdown'; import { Detail } from '../components/Detail'; import { EditableTitle } from '../components/EditableTitle'; +import { Guard } from '../components/Guard'; import { NavBarSpacer } from '../components/NavBarSpacer'; import { editURL } from '../helpers/navigation'; import { ResourceInline } from './ResourceInline'; @@ -147,25 +148,28 @@ export function ChatRoomPage({ resource }: ResourcePageProps) { )} - - - - Send - - + + + + + Send + + + + ); diff --git a/browser/data-browser/src/views/CollectionPage.tsx b/browser/data-browser/src/views/CollectionPage.tsx index e467a39c6..a83d7a85c 100644 --- a/browser/data-browser/src/views/CollectionPage.tsx +++ b/browser/data-browser/src/views/CollectionPage.tsx @@ -13,6 +13,7 @@ import { FaArrowLeft, FaArrowRight, FaInfo, + FaPlus, FaTable, FaThLarge, } from 'react-icons/fa'; @@ -171,8 +172,9 @@ function Collection({ resource }: ResourcePageProps): JSX.Element { {isClass && ( diff --git a/browser/data-browser/src/views/CrashPage.tsx b/browser/data-browser/src/views/CrashPage.tsx index 520f82586..f5d559913 100644 --- a/browser/data-browser/src/views/CrashPage.tsx +++ b/browser/data-browser/src/views/CrashPage.tsx @@ -14,6 +14,32 @@ type ErrorPageProps = { clearError: () => void; }; +const githubIssueTemplate = ( + message, + stack, +) => `**Describe what you did to produce the bug** + +## Error message +\`\`\` +${message} +\`\`\` + +## Stack trace +\`\`\` +${stack} +\`\`\` +`; + +function createGithubIssueLink(error: Error): string { + const url = new URL( + 'https://github.com/atomicdata-dev/atomic-data-browser/issues/new', + ); + url.searchParams.set('body', githubIssueTemplate(error.message, error.stack)); + url.searchParams.set('labels', 'bug'); + + return url.href; +} + /** If the entire app crashes, show this page */ function CrashPage({ resource, @@ -26,6 +52,7 @@ function CrashPage({ {children ? children : } + Create Github issue {clearError && } diff --git a/browser/data-browser/src/views/DrivePage.tsx b/browser/data-browser/src/views/DrivePage.tsx index 66843c550..6f75235df 100644 --- a/browser/data-browser/src/views/DrivePage.tsx +++ b/browser/data-browser/src/views/DrivePage.tsx @@ -5,7 +5,7 @@ import { Button } from '../components/Button'; import { useSettings } from '../helpers/AppSettings'; import { ResourcePageProps } from './ResourcePage'; import { EditableTitle } from '../components/EditableTitle'; -import { Column, Row } from '../components/Row'; +import { Row } from '../components/Row'; import { styled } from 'styled-components'; import InputSwitcher from '../components/forms/InputSwitcher'; import { WarningBlock } from '../components/WarningBlock'; @@ -23,38 +23,36 @@ function DrivePage({ resource }: ResourcePageProps): JSX.Element { return ( - - - - {baseURL !== resource.subject && ( - - )} - - + + {baseURL !== resource.subject && ( + + )} + + +
+ Default Ontology + -
- Default Ontology - -
- {baseURL.startsWith('http://localhost') && ( - - You are running Atomic-Server on `localhost`, which means that it - will not be available from any other machine than your current local - device. If you want your Atomic-Server to be available from the web, - you should set this up at a Domain on a server. - - )} - +
+ {baseURL.startsWith('http://localhost') && ( + + You are running Atomic-Server on `localhost`, which means that it will + not be available from any other machine than your current local + device. If you want your Atomic-Server to be available from the web, + you should set this up at a Domain on a server. + + )}
); } diff --git a/browser/data-browser/src/views/ErrorPage.tsx b/browser/data-browser/src/views/ErrorPage.tsx index 4be11ec79..95a708ace 100644 --- a/browser/data-browser/src/views/ErrorPage.tsx +++ b/browser/data-browser/src/views/ErrorPage.tsx @@ -3,12 +3,12 @@ import { isUnauthorized, useStore } from '@tomic/react'; import { ContainerWide } from '../components/Containers'; import { ErrorBlock } from '../components/ErrorLook'; import { Button } from '../components/Button'; -import { SignInButton } from '../components/SignInButton'; import { useSettings } from '../helpers/AppSettings'; import { ResourcePageProps } from './ResourcePage'; import { Column, Row } from '../components/Row'; import CrashPage from './CrashPage'; import { clearAllLocalData } from '../helpers/clearData'; +import { Guard } from '../components/Guard'; /** * A View for Resource Errors. Not to be confused with the CrashPage, which is @@ -19,13 +19,26 @@ function ErrorPage({ resource }: ResourcePageProps): JSX.Element { const store = useStore(); const subject = resource.getSubject(); + React.useEffect(() => { + // Try again when agent changes + store.fetchResourceFromServer(subject); + }, [agent]); + if (isUnauthorized(resource.error)) { + // This might be a bit too aggressive, but it fixes 'Unauthorized' messages after signing in to a new drive. + store.fetchResourceFromServer(subject); + return (

Unauthorized

{agent ? ( <> +

+ { + "You don't have access to this. Try asking for access, or sign in with a different account." + } +

- + {/* {pageState === PageStateOpts.register && ( )} - + */} ); } diff --git a/browser/data-browser/src/views/ErrorPage.tsx b/browser/data-browser/src/views/ErrorPage.tsx index 95a708ace..43e26f358 100644 --- a/browser/data-browser/src/views/ErrorPage.tsx +++ b/browser/data-browser/src/views/ErrorPage.tsx @@ -17,7 +17,7 @@ import { Guard } from '../components/Guard'; function ErrorPage({ resource }: ResourcePageProps): JSX.Element { const { agent } = useSettings(); const store = useStore(); - const subject = resource.getSubject(); + const subject = resource.subject; React.useEffect(() => { // Try again when agent changes @@ -60,8 +60,8 @@ function ErrorPage({ resource }: ResourcePageProps): JSX.Element { return ( -

Could not open {resource.getSubject()}

- +

Could not open {resource.subject}

+ + ); +} + export function ErrorBlock({ error, showTrace }: ErrorBlockProps): JSX.Element { return ( @@ -48,14 +86,6 @@ const ErrorLookBig = styled.div` background-color: ${p => p.theme.colors.bg}; `; -const Pre = styled.pre` - white-space: pre-wrap; - border-radius: ${p => p.theme.radius}; - padding: ${p => p.theme.margin}rem; - background-color: ${p => p.theme.colors.bg}; - font-size: 0.9rem; -`; - const BiggerText = styled.p` color: ${p => p.theme.colors.alert}; font-size: 1.3rem; diff --git a/browser/data-browser/src/routes/DataRoute.tsx b/browser/data-browser/src/routes/DataRoute.tsx index 1aa41f022..3134e6fba 100644 --- a/browser/data-browser/src/routes/DataRoute.tsx +++ b/browser/data-browser/src/routes/DataRoute.tsx @@ -59,7 +59,7 @@ function Data(): JSX.Element { setTextResponseLoading(true); try { - const resp = await window.fetch(subject!, { headers: headers }); + const resp = await fetch(subject!, { headers: headers }); const body = await resp.text(); setTextResponseLoading(false); setTextResponse(body); diff --git a/browser/data-browser/src/views/CrashPage.tsx b/browser/data-browser/src/views/CrashPage.tsx index f5d559913..e8e88fa04 100644 --- a/browser/data-browser/src/views/CrashPage.tsx +++ b/browser/data-browser/src/views/CrashPage.tsx @@ -2,7 +2,11 @@ import * as React from 'react'; import { Resource } from '@tomic/react'; import { ContainerWide } from '../components/Containers'; -import { ErrorBlock } from '../components/ErrorLook'; +import { + createGithubIssueLink, + ErrorBlock, + GitHubIssueButton, +} from '../components/ErrorLook'; import { Button } from '../components/Button'; import { Column, Row } from '../components/Row'; @@ -14,32 +18,6 @@ type ErrorPageProps = { clearError: () => void; }; -const githubIssueTemplate = ( - message, - stack, -) => `**Describe what you did to produce the bug** - -## Error message -\`\`\` -${message} -\`\`\` - -## Stack trace -\`\`\` -${stack} -\`\`\` -`; - -function createGithubIssueLink(error: Error): string { - const url = new URL( - 'https://github.com/atomicdata-dev/atomic-data-browser/issues/new', - ); - url.searchParams.set('body', githubIssueTemplate(error.message, error.stack)); - url.searchParams.set('labels', 'bug'); - - return url.href; -} - /** If the entire app crashes, show this page */ function CrashPage({ resource, @@ -53,6 +31,7 @@ function CrashPage({ {children ? children : } Create Github issue + {clearError && } - {/* + {pageState === PageStateOpts.register && ( )} - */} + ); } @@ -135,7 +128,7 @@ function Reset({ email, setEmail, setPageState }) { return ( <> -

Reset your PassKey

+

Reset your Secret

@@ -145,7 +138,7 @@ function Reset({ email, setEmail, setPageState }) {

{ + setEmail={(e: unknown) => { setErr(undefined); setEmail(e); }} @@ -277,7 +270,7 @@ function SignIn({ setPageState }) { Register diff --git a/browser/data-browser/src/components/SettingsAgent.tsx b/browser/data-browser/src/components/SettingsAgent.tsx index edda04afa..90b2b9671 100644 --- a/browser/data-browser/src/components/SettingsAgent.tsx +++ b/browser/data-browser/src/components/SettingsAgent.tsx @@ -108,9 +108,9 @@ export const SettingsAgent: React.FunctionComponent = () => { return (
@@ -124,7 +124,7 @@ export const SettingsAgent: React.FunctionComponent = () => { id='current-password' autoComplete='current-password' spellCheck='false' - placeholder='Paste your Passphrase' + placeholder='Paste your Secret' /> = ({ onClose(); }, [name, createResourceAndNavigate, onClose, parent]); - const [dialogProps, show, hide] = useDialog({ onSuccess, onCancel: onClose }); + const { dialogProps, show, close } = useDialog({ + onSuccess, + onCancel: onClose, + }); const onNameChange = (e: React.ChangeEvent) => { setName(e.target.value); @@ -69,7 +72,7 @@ export const NewArticleDialog: FC = ({ { e.preventDefault(); - hide(true); + close(true); }} > @@ -89,10 +92,10 @@ export const NewArticleDialog: FC = ({
- - diff --git a/browser/data-browser/src/components/forms/NewForm/CustomCreateActions/CustomForms/NewCollectionDialog.tsx b/browser/data-browser/src/components/forms/NewForm/CustomCreateActions/CustomForms/NewCollectionDialog.tsx index 349866c84..09f2ccd54 100644 --- a/browser/data-browser/src/components/forms/NewForm/CustomCreateActions/CustomForms/NewCollectionDialog.tsx +++ b/browser/data-browser/src/components/forms/NewForm/CustomCreateActions/CustomForms/NewCollectionDialog.tsx @@ -22,7 +22,7 @@ export const NewCollectionDialog: FC = ({ const [valueFilter, setValue] = useState(); const [propertyFilter, setProperty] = useState(); - const [dialogProps, show, hide] = useDialog({ onCancel: onClose }); + const { dialogProps, show, close } = useDialog({ onCancel: onClose }); const createResourceAndNavigate = useCreateAndNavigate(); @@ -91,7 +91,7 @@ export const NewCollectionDialog: FC = ({ - + )} ); diff --git a/browser/data-browser/src/routes/History/HistoryMobileView.tsx b/browser/data-browser/src/routes/History/HistoryMobileView.tsx index b69ac8f6b..df0b57466 100644 --- a/browser/data-browser/src/routes/History/HistoryMobileView.tsx +++ b/browser/data-browser/src/routes/History/HistoryMobileView.tsx @@ -23,7 +23,7 @@ export function HistoryMobileView({ onSelectVersion, onVersionAccept, }: HistoryViewProps) { - const [dialogProps, showDialog, closeDialog] = useDialog(); + const { dialogProps, show: showDialog, close: closeDialog } = useDialog(); const handleVersionSelect = useCallback((version: Version) => { onSelectVersion(version); diff --git a/browser/data-browser/src/routes/Sandbox.tsx b/browser/data-browser/src/routes/Sandbox.tsx index 8685fc42f..f7ffb6e45 100644 --- a/browser/data-browser/src/routes/Sandbox.tsx +++ b/browser/data-browser/src/routes/Sandbox.tsx @@ -1,6 +1,15 @@ +import { Button } from '../components/Button'; import { ContainerFull } from '../components/Containers'; +import { + Dialog, + DialogContent, + DialogTitle, + useDialog, +} from '../components/Dialog'; export function Sandbox(): JSX.Element { + const { dialogProps, show, isOpen } = useDialog(); + return (
@@ -9,6 +18,12 @@ export function Sandbox(): JSX.Element { Welcome to the sandbox. This is a place to test components in isolation.

+

{isOpen ? 'TRUE' : 'FALSE'}

+ + + Title + Content +
); diff --git a/browser/data-browser/src/routes/SettingsAgent.tsx b/browser/data-browser/src/routes/SettingsAgent.tsx index c646027d5..61d7f7e0c 100644 --- a/browser/data-browser/src/routes/SettingsAgent.tsx +++ b/browser/data-browser/src/routes/SettingsAgent.tsx @@ -1,5 +1,3 @@ -import * as React from 'react'; - import { useSettings } from '../helpers/AppSettings'; import { Button } from '../components/Button'; import { Margin } from '../components/Card'; diff --git a/browser/data-browser/src/views/CodeUsage/ResourceCodeUsageDialog.tsx b/browser/data-browser/src/views/CodeUsage/ResourceCodeUsageDialog.tsx index a00b091ce..f903aba5c 100644 --- a/browser/data-browser/src/views/CodeUsage/ResourceCodeUsageDialog.tsx +++ b/browser/data-browser/src/views/CodeUsage/ResourceCodeUsageDialog.tsx @@ -21,7 +21,7 @@ export function ResourceCodeUsageDialog({ bindShow, }: ResourceCodeUsageDialogProps): React.JSX.Element { const resource = useResource(subject); - const [dialogProps, show, hide, isOpen] = useDialog({ bindShow }); + const { dialogProps, show, close: hide, isOpen } = useDialog({ bindShow }); useEffect(() => { if (open) { diff --git a/browser/data-browser/src/views/OntologyPage/CreateInstanceButton.tsx b/browser/data-browser/src/views/OntologyPage/CreateInstanceButton.tsx index d639a2bd0..9744b434f 100644 --- a/browser/data-browser/src/views/OntologyPage/CreateInstanceButton.tsx +++ b/browser/data-browser/src/views/OntologyPage/CreateInstanceButton.tsx @@ -13,7 +13,7 @@ interface CreateInstanceButtonProps { export function CreateInstanceButton({ ontology }: CreateInstanceButtonProps) { const [classSelectorActive, setClassSelectorActive] = useState(false); const [classSubject, setClassSubject] = useState(); - const [dialogProps, show, close, isOpen] = useDialog({ + const { dialogProps, show, close, isOpen } = useDialog({ onSuccess: () => { setClassSubject(undefined); ontology.save(); diff --git a/browser/data-browser/src/views/OntologyPage/NewClassButton.tsx b/browser/data-browser/src/views/OntologyPage/NewClassButton.tsx index 52b16ecc9..64912723b 100644 --- a/browser/data-browser/src/views/OntologyPage/NewClassButton.tsx +++ b/browser/data-browser/src/views/OntologyPage/NewClassButton.tsx @@ -29,7 +29,12 @@ export function NewClassButton({ resource }: NewClassButtonProps): JSX.Element { const subject = subjectForClass(resource, inputValue); - const [dialogProps, show, hide, isOpen] = useDialog({ + const { + dialogProps, + show, + close: hide, + isOpen, + } = useDialog({ onSuccess: async () => { const createdClass = await newClass(inputValue, resource, store); const id = toAnchorId(createdClass); diff --git a/browser/data-browser/src/views/OntologyPage/Property/PropertyLineWrite.tsx b/browser/data-browser/src/views/OntologyPage/Property/PropertyLineWrite.tsx index 4d07b3fee..67da2d318 100644 --- a/browser/data-browser/src/views/OntologyPage/Property/PropertyLineWrite.tsx +++ b/browser/data-browser/src/views/OntologyPage/Property/PropertyLineWrite.tsx @@ -25,7 +25,7 @@ export function PropertyLineWrite({ const resource = useResource(subject); const shortnameProp = useProperty(core.properties.shortname); const descriptionProp = useProperty(core.properties.description); - const [dialogProps, show, hide] = useDialog(); + const { dialogProps, show, close: hide } = useDialog(); const [canEdit] = useCanWrite(resource); const { hasProperty } = useOntologyContext(); diff --git a/browser/data-browser/src/views/OntologyPage/Property/PropertyWriteDialog.tsx b/browser/data-browser/src/views/OntologyPage/Property/PropertyWriteDialog.tsx index 0d143c6cf..396983f45 100644 --- a/browser/data-browser/src/views/OntologyPage/Property/PropertyWriteDialog.tsx +++ b/browser/data-browser/src/views/OntologyPage/Property/PropertyWriteDialog.tsx @@ -25,7 +25,7 @@ export function PropertyWriteDialog({ return ( - {dialogProps.show && ( + {dialogProps.isVisible && ( <> { if (showDialog) { diff --git a/browser/data-browser/src/views/TablePage/PropertyForm/ExternalPropertyDialog.tsx b/browser/data-browser/src/views/TablePage/PropertyForm/ExternalPropertyDialog.tsx index ab1e62480..52f8bb4a3 100644 --- a/browser/data-browser/src/views/TablePage/PropertyForm/ExternalPropertyDialog.tsx +++ b/browser/data-browser/src/views/TablePage/PropertyForm/ExternalPropertyDialog.tsx @@ -30,7 +30,7 @@ export function ExternalPropertyDialog({ urls.properties.recommends, { commit: true }, ); - const [dialogProps, show, hide] = useDialog({ bindShow }); + const { dialogProps, show, close: hide } = useDialog({ bindShow }); const onAddClick = () => { if (subject) { diff --git a/browser/data-browser/src/views/TablePage/PropertyForm/NewPropertyDialog.tsx b/browser/data-browser/src/views/TablePage/PropertyForm/NewPropertyDialog.tsx index da9dde3a2..d1538cd71 100644 --- a/browser/data-browser/src/views/TablePage/PropertyForm/NewPropertyDialog.tsx +++ b/browser/data-browser/src/views/TablePage/PropertyForm/NewPropertyDialog.tsx @@ -136,7 +136,11 @@ export function NewPropertyDialog({ setResource(null); }, [resource, store, tableClassResource, pushProp]); - const [dialogProps, show, hide] = useDialog({ + const { + dialogProps, + show, + close: hide, + } = useDialog({ bindShow, onCancel: handleUserCancelAction, onSuccess: handleUserSuccessAction, diff --git a/browser/e2e/tests/e2e.spec.ts b/browser/e2e/tests/e2e.spec.ts index df9646aac..e6e8846d2 100644 --- a/browser/e2e/tests/e2e.spec.ts +++ b/browser/e2e/tests/e2e.spec.ts @@ -65,9 +65,9 @@ test.describe('data-browser', async () => { // Sign out await openAgentPage(page); await page.click('[data-test="sign-out"]'); - await expect(page.locator('text=Enter your Agent secret')).toBeVisible(); + await expect(page.locator('text=Enter your Secret')).toBeVisible(); await page.reload(); - await expect(page.locator('text=Enter your Agent secret')).toBeVisible(); + await expect(page.locator('text=Enter your Secret')).toBeVisible(); }); test('sign up and edit document atomicdata.dev', async ({ page }) => { diff --git a/browser/lib/src/authentication.ts b/browser/lib/src/authentication.ts index 5380188c6..84c47b6bd 100644 --- a/browser/lib/src/authentication.ts +++ b/browser/lib/src/authentication.ts @@ -1,8 +1,9 @@ import { Agent } from './agent.js'; import type { HeadersObject } from './client.js'; import { generateKeyPair, getTimestampNow, signToBase64 } from './commit.js'; -import { core } from './ontologies/core.js'; import type { Store } from './store.js'; +import type { Resource } from './resource.js'; +import { core } from './ontologies/core.js'; /** Returns a JSON-AD resource of an Authentication */ export async function createAuthentication(subject: string, agent: Agent) { @@ -122,7 +123,7 @@ export const nameRegex = '^[a-z0-9_-]+'; export async function serverSupportsRegister(store: Store) { const url = new URL('/register', store.getServerUrl()); - const resource = await store.getResourceAsync(url.toString()); + const resource = await store.getResource(url.toString()); if (!resource) { return false; @@ -135,6 +136,23 @@ export async function serverSupportsRegister(store: Store) { return true; } +/** Run this after making a call to an endpoint. Throws if something went wrong. */ +function checkResourceSuccess(resource?: Resource) { + if (!resource) { + throw new Error('No resource received'); + } + + if (resource.error) { + throw resource.error; + } + + const respName = resource.get(core.properties.name) as string; + + if (!respName.includes('Success')) { + throw new Error('Expected a `success` message, did not receive one'); + } +} + /** Asks the server to create an Agent + a Drive. * Sends the confirmation email to the user. * Throws if the name is not available or the email is invalid. @@ -148,20 +166,7 @@ export async function register( url.searchParams.set('name', name); url.searchParams.set('email', email); const resource = await store.getResourceAsync(url.toString()); - - if (!resource) { - throw new Error('No resource received'); - } - - if (resource.error) { - throw resource.error; - } - - const description = resource.get(core.properties.description) as string; - - if (!description.includes('success')) { - throw new Error('Expected a `success` message, did not receive one'); - } + checkResourceSuccess(resource); return; } @@ -175,20 +180,7 @@ export async function addPublicKey(store: Store, email: string): Promise { const url = new URL('/add-public-key', store.getServerUrl()); url.searchParams.set('email', email); const resource = await store.getResourceAsync(url.toString()); - - if (!resource) { - throw new Error('No resource received'); - } - - if (resource.error) { - throw resource.error; - } - - const description = resource.get(core.properties.description) as string; - - if (!description.includes('success')) { - throw new Error('Expected a `success` message, did not receive one'); - } + checkResourceSuccess(resource); return; } @@ -231,7 +223,7 @@ export async function confirmEmail( } url.searchParams.set('public-key', await agent.getPublicKey()); - const resource = await store.getResourceAsync(url.toString()); + const resource = await store.getResource(url.toString()); if (!resource) { throw new Error('no resource!'); @@ -254,7 +246,7 @@ export async function confirmEmail( return { agent, destination }; } -function parseJwt(token) { +function parseJwt(token: string) { try { const base64Url = token.split('.')[1]; const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/'); diff --git a/browser/react/src/useServerSupports.ts b/browser/react/src/useServerSupports.ts index 15301ea7f..ad2695e13 100644 --- a/browser/react/src/useServerSupports.ts +++ b/browser/react/src/useServerSupports.ts @@ -5,16 +5,16 @@ export function useServerSupports(): ServerSupports { const store = useStore(); const serverURL = useServerURL(); const [supports, setSupports] = useState({ - emailRegister: false, + emailRegister: true, }); useEffect(() => { - async function check() { - const res = await store.getServerSupports(); - setSupports(res); - } - - check(); + console.log('useEffect'); + // async function check() { + // const res = await store.getServerSupports(); + // setSupports(res); + // } + // check(); }, [store, serverURL]); return supports; diff --git a/cli/src/commit.rs b/cli/src/commit.rs index 1251a0721..88bf8ee13 100644 --- a/cli/src/commit.rs +++ b/cli/src/commit.rs @@ -8,7 +8,7 @@ pub fn set(context: &Context, subject: &str, property: &str, value: &str) -> Ato Ok(r) => r, Err(_) => atomic_lib::Resource::new(subject.into()), }; - resource.set_shortname(&property, &value, &context.store)?; + resource.set_shortname(property, value, &context.store)?; resource.save(&context.store)?; Ok(()) } @@ -17,19 +17,19 @@ pub fn set(context: &Context, subject: &str, property: &str, value: &str) -> Ato #[cfg(feature = "native")] pub fn edit(context: &Context, subject: &str, prop: &str) -> AtomicResult<()> { // If the resource is not found, create it - let mut resource = match context.store.get_resource(&subject) { + let mut resource = match context.store.get_resource(subject) { Ok(r) => r, Err(_) => atomic_lib::Resource::new(subject.into()), }; // If the prop is not found, create it - let current_val = match resource.get_shortname(&prop, &context.store) { + let current_val = match resource.get_shortname(prop, &context.store) { Ok(val) => val.to_string(), Err(_) => "".to_string(), }; let edited = edit::edit(current_val)?; // Remove newline - or else I can's save shortnames or numbers using vim; let trimmed = edited.trim_end_matches('\n'); - resource.set_shortname(&prop, trimmed, &context.store)?; + resource.set_shortname(prop, trimmed, &context.store)?; resource.save(&context.store)?; Ok(()) } @@ -37,7 +37,7 @@ pub fn edit(context: &Context, subject: &str, prop: &str) -> AtomicResult<()> { /// Apply a Commit using the Remove method - removes a property from a resource pub fn remove(context: &Context, subject: &str, prop: &str) -> AtomicResult<()> { let mut resource = context.store.get_resource(subject)?; - resource.remove_propval_shortname(&prop, &context.store)?; + resource.remove_propval_shortname(prop, &context.store)?; resource.save(&context.store)?; Ok(()) } diff --git a/cli/src/main.rs b/cli/src/main.rs index f10ad2acb..7e2b15982 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,5 +1,4 @@ use atomic_lib::serialize::Format; -use atomic_lib::atomic_url::Routes; use atomic_lib::{agents::generate_public_key, mapping::Mapping}; use atomic_lib::{agents::Agent, config::Config}; use atomic_lib::{errors::AtomicResult, Storelike}; @@ -114,9 +113,9 @@ pub enum SerializeOptions { NTriples, } -impl Into for SerializeOptions { - fn into(self) -> Format { - match self { +impl From<&SerializeOptions> for Format { + fn from(val: &SerializeOptions) -> Self { + match val { SerializeOptions::Pretty => Format::Pretty, SerializeOptions::Json => Format::Json, SerializeOptions::NTriples => Format::NTriples, diff --git a/cli/src/path.rs b/cli/src/path.rs index 9d7a5fdce..145677a7b 100644 --- a/cli/src/path.rs +++ b/cli/src/path.rs @@ -4,7 +4,7 @@ use atomic_lib::{agents::ForAgent, errors::AtomicResult, serialize, storelike, A /// Resolves an Atomic Path query pub fn get_path( context: &mut Context, - path_vec: &Vec, + path_vec: &[String], serialize: &SerializeOptions, ) -> AtomicResult<()> { // let subcommand_matches = context.matches.subcommand_matches("get").unwrap(); diff --git a/cli/src/print.rs b/cli/src/print.rs index a05f55276..83a568f13 100644 --- a/cli/src/print.rs +++ b/cli/src/print.rs @@ -7,7 +7,7 @@ use colored::*; use crate::{Context, SerializeOptions}; -/// Prints a resource for the terminal with readble formatting and colors +/// Prints a resource for the terminal with readable formatting and colors pub fn pretty_print_resource(resource: &Resource, store: &impl Storelike) -> AtomicResult { let mut output = String::new(); output.push_str(&format!( @@ -32,7 +32,7 @@ pub fn print_resource( resource: &Resource, serialize: &SerializeOptions, ) -> AtomicResult<()> { - let format: Format = serialize.clone().into(); + let format: Format = serialize.into(); let out = match format { Format::Json => resource.to_json(&context.store)?, Format::JsonLd => resource.to_json_ld(&context.store)?, diff --git a/docs/src/react/useStore.md b/docs/src/react/useStore.md index f14ce5150..9db6812ad 100644 --- a/docs/src/react/useStore.md +++ b/docs/src/react/useStore.md @@ -23,10 +23,10 @@ export const Login = () => { return (
); } @@ -148,8 +150,7 @@ export function ResourceCardDefault({ export default ResourceCard; const DescriptionWrapper = styled.div` - max-height: 10rem; - overflow: hidden; + overflow: auto; `; const ClassName = styled.span` From e0033db3a8b7a1512385b9edc608c439293e1897 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:36:05 +0200 Subject: [PATCH 44/51] Fix history page --- browser/lib/src/resource.ts | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/browser/lib/src/resource.ts b/browser/lib/src/resource.ts index 5b1602957..679f65b74 100644 --- a/browser/lib/src/resource.ts +++ b/browser/lib/src/resource.ts @@ -1,3 +1,4 @@ +import { commits } from './ontologies/commits.js'; import { EventManager } from './EventManager.js'; import type { Agent } from './agent.js'; import { Client } from './client.js'; @@ -415,19 +416,25 @@ export class Resource { public async getHistory( progressCallback?: (percentage: number) => void, ): Promise { - const commitsCollection = await this.store.fetchResourceFromServer( - this.getCommitsCollectionSubject(), - ); - const commits = commitsCollection.get( - properties.collection.members, - ) as string[]; + const commitsCollection = await new CollectionBuilder(this.store) + .setPageSize(9999) + .setProperty(commits.properties.subject) + .setValue(this.subject) + .setSortBy(commits.properties.createdAt) + .buildAndFetch(); + + const commitSubjects = await commitsCollection.getAllMembers(); const builtVersions: Version[] = []; + if (!commitSubjects) { + return builtVersions; + } + let previousResource = new Resource(this.subject); - for (let i = 0; i < commits.length; i++) { - const commitResource = await this.store.getResource(commits[i]); + for (let i = 0; i < commitSubjects.length; i++) { + const commitResource = await this.store.getResource(commitSubjects[i]); const parsedCommit = parseCommitResource(commitResource); const builtResource = applyCommitToResource( previousResource.clone(), @@ -441,7 +448,7 @@ export class Resource { // Every 30 cycles we report the progress if (progressCallback && i % 30 === 0) { - progressCallback(Math.round((i / commits.length) * 100)); + progressCallback(Math.round((i / commitSubjects.length) * 100)); await WaitForImmediate(); } } From 3a006f8b8b0c5d73ad42093d14984fe2b88b6a4a Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 22 Aug 2024 10:36:05 +0200 Subject: [PATCH 45/51] Improve server FAQ --- docs/src/atomicserver/faq.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/src/atomicserver/faq.md b/docs/src/atomicserver/faq.md index a2467a372..e2e0b1e61 100644 --- a/docs/src/atomicserver/faq.md +++ b/docs/src/atomicserver/faq.md @@ -8,12 +8,14 @@ No, AtomicServer has its own HTTPS support. Just pass a `--https` flag! -## Can / should I create backups? +## Can I create backups? -You should. -Run `atomic-server export` to create a JSON-AD backup in your `~/.config/atomic/backups` folder. +There are two ways you can create backups: + +1. **Export the JSON-AD**. Run `atomic-server export` to create a JSON-AD backup in your `~/.config/atomic/backups` folder. Import them using `atomic-server import -p ~/.config/atomic/backups/${date}.json`.' You could also copy all folders `atomic-server` uses. To see what these are, see `atomic-server show-config`. +1. **Backup the database file**. use `atomic-server show-config` to find the `store_path` and copy the path to some place where you store the backup. ## I lost the key / secret to my Root Agent, and the `/setup` invite is no longer usable! What now? @@ -26,7 +28,7 @@ This could especially be helpful if you're running at `localhost:9883` and want ## How do I reset my database? -`atomic-server reset` +`atomic-server reset`. This deletes all of your data. Be careful! ## How do I make my data private, yet available online? From ad8896658ba7a701f16e6a1dbe3f4d410c0a2d46 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Fri, 23 Aug 2024 13:59:25 +0200 Subject: [PATCH 46/51] Clippy fixes --- lib/src/db/migrations.rs | 34 ++++++++++++++++++++++++--------- server/src/handlers/download.rs | 5 +++-- server/src/handlers/export.rs | 9 ++++----- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/lib/src/db/migrations.rs b/lib/src/db/migrations.rs index 647a64a56..92bdabdab 100644 --- a/lib/src/db/migrations.rs +++ b/lib/src/db/migrations.rs @@ -89,17 +89,30 @@ fn res_v1_to_v2(store: &Db) -> AtomicResult<()> { let old_key = RESOURCE_TREE_V1; let old = store.db.open_tree(old_key)?; let mut count = 0; + let mut failed_subjects: Vec = Vec::new(); - fn migrate_subject(subject: &str) -> String { - let url = url::Url::parse(subject).expect("Unable to parse subject URL"); - if subject != url.to_string() { - println!("Migrating: {} -> {}", subject, url) + for item in old.into_iter() { + let mut migrate_subject = |subject: &mut str| { + let url = match url::Url::parse(subject) { + Ok(url) => url, + Err(e) => { + tracing::error!( + "Unable to parse subject URL of '{}', skipping: {}", + subject, + e + ); + failed_subjects.push(subject.to_string()); + return subject.to_string(); + } + }; + + if subject != &url.to_string() { + println!("Migrating: {} -> {}", subject, url); + }; + url.to_string() }; - url.to_string() - } - for item in old.into_iter() { - let (subject, resource_bin) = item.expect("Unable to convert into iterable"); + let (subject, resource_bin) = item.expect("Unable to convert intos iterable"); let subject: String = String::from_utf8_lossy(&subject).to_string(); let mut propvals: PropVals = bincode::deserialize(&resource_bin)?; @@ -124,7 +137,10 @@ fn res_v1_to_v2(store: &Db) -> AtomicResult<()> { _other => {} }; } - new.insert(migrate_subject(&subject), bincode::serialize(&propvals)?)?; + new.insert( + migrate_subject(&mut subject.clone()).as_bytes(), + bincode::serialize(&propvals)?, + )?; count += 1; } diff --git a/server/src/handlers/download.rs b/server/src/handlers/download.rs index 67e2d50c1..a91e4e1eb 100644 --- a/server/src/handlers/download.rs +++ b/server/src/handlers/download.rs @@ -5,6 +5,7 @@ use atomic_lib::{urls, Resource, Storelike}; use image::GenericImageView; use image::{codecs::avif::AvifEncoder, ImageReader}; use serde::Deserialize; +use std::path::Path; use std::{collections::HashSet, io::Write, path::PathBuf}; #[serde_with::serde_as] @@ -164,8 +165,8 @@ fn process_image( Err(format!("Unsupported format: {}", format).into()) } -fn create_processed_folder_if_not_exists(base_path: &PathBuf) -> AtomicServerResult<()> { - let mut processed_folder = base_path.clone(); +fn create_processed_folder_if_not_exists(base_path: &Path) -> AtomicServerResult<()> { + let mut processed_folder = base_path.to_path_buf(); processed_folder.push("processed"); std::fs::create_dir_all(processed_folder)?; Ok(()) diff --git a/server/src/handlers/export.rs b/server/src/handlers/export.rs index e77225db2..5586ee67a 100644 --- a/server/src/handlers/export.rs +++ b/server/src/handlers/export.rs @@ -6,7 +6,8 @@ use actix_web::{web, HttpResponse}; use atomic_lib::agents::ForAgent; use atomic_lib::errors::AtomicResult; use atomic_lib::values::SubResource; -use atomic_lib::{urls, Db, Query, Resource, Storelike, Value}; +use atomic_lib::Query; +use atomic_lib::{urls, Db, Resource, Storelike, Value}; use chrono::DateTime; use serde::Deserialize; @@ -224,18 +225,16 @@ impl<'a> CSVExporter<'a> { fn value_to_string(&self, value: &Value) -> String { match value { Value::Timestamp(ts) => { - // Convert the timestamp to a NaiveDateTime (no timezone) let seconds = ts / 1000; let remaining_nanoseconds = (ts % 1000) * 1_000_000; // Convert remaining milliseconds to nanoseconds - let Some(datetime) = + let Some(date_time) = DateTime::from_timestamp(seconds, remaining_nanoseconds as u32) else { return ts.to_string(); }; - // Format the DateTime as a string in RFC3339 format (e.g., "2023-03-20T12:34:56Z") - datetime.to_rfc3339() + date_time.to_rfc3339() } Value::ResourceArray(values) => { let names: Vec = values From 2e828032f1ea493fa0ca8cead68e2784bf2e5f7b Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Fri, 23 Aug 2024 13:59:43 +0200 Subject: [PATCH 47/51] Fix slow build.rs / rust analyzer #913 --- .vscode/settings.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index 6ef8e7ba9..04efc0c7c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,8 @@ { + // This is used to prevent `build.rs` from running every time you make a change to a file. + "rust-analyzer.check.extraEnv": { + "IS_RUST_ANALYZER": "true" + }, // The linter in the CI is quite strict, so running `cargo fmt` on save is probably a good idea! "editor.formatOnSave": true, "files.autoSave": "onFocusChange", From c69e268f83e8fa789be3d0a5c0f9fb8bebdc0fb1 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Fri, 23 Aug 2024 15:40:09 +0200 Subject: [PATCH 48/51] Fix initialize bug --- server/src/appstate.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/appstate.rs b/server/src/appstate.rs index d59679de2..7c4666cd4 100644 --- a/server/src/appstate.rs +++ b/server/src/appstate.rs @@ -61,7 +61,7 @@ impl AppState { let should_initialize = !&config.store_path.exists() || config.initialize || no_server_resource; if should_initialize { - tracing::info!("Initialize: creating and populating new Database..."); + tracing::warn!("Initialize: creating and populating new Database..."); atomic_lib::populate::populate_default_store(&store) .map_err(|e| format!("Failed to populate default store. {}", e))?; } From 02d76d313c0264e52d0aba418a42f3be4be835a1 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Mon, 7 Oct 2024 11:24:03 +0200 Subject: [PATCH 49/51] Fix issues rebase --- .../src/views/TablePage/TableExportDialog.tsx | 2 +- browser/deno.lock | 8382 +++++++++++++++++ lib/src/db.rs | 6 + lib/src/db/migrations.rs | 2 +- lib/src/db/query_index.rs | 146 +- lib/src/resources.rs | 1 - lib/src/serialize.rs | 2 +- lib/src/store.rs | 2 +- server/build.rs | 3 - server/src/appstate.rs | 1 + server/src/commit_monitor.rs | 2 +- server/src/serve.rs | 1 + 12 files changed, 8476 insertions(+), 74 deletions(-) create mode 100644 browser/deno.lock diff --git a/browser/data-browser/src/views/TablePage/TableExportDialog.tsx b/browser/data-browser/src/views/TablePage/TableExportDialog.tsx index afc247b75..23498205e 100644 --- a/browser/data-browser/src/views/TablePage/TableExportDialog.tsx +++ b/browser/data-browser/src/views/TablePage/TableExportDialog.tsx @@ -33,7 +33,7 @@ export function TableExportDialog({ bindShow, }: TableExportDialogProps): React.JSX.Element { const store = useStore(); - const [dialogProps, showDialog] = useDialog({ bindShow }); + const { dialogProps, show: showDialog } = useDialog({ bindShow }); const [refAsSubject, setRefAsSubject] = useState(false); const url = buildLink(subject, refAsSubject, store); diff --git a/browser/deno.lock b/browser/deno.lock new file mode 100644 index 000000000..771ef7e51 --- /dev/null +++ b/browser/deno.lock @@ -0,0 +1,8382 @@ +{ + "version": "4", + "specifiers": { + "npm:@types/node@^20.11.5": "20.16.10", + "npm:@typescript-eslint/eslint-plugin@^7.8.0": "7.18.0_@typescript-eslint+parser@7.18.0__eslint@8.57.1__typescript@5.6.2_eslint@8.57.1_typescript@5.6.2", + "npm:@typescript-eslint/parser@^7.8.0": "7.18.0_eslint@8.57.1_typescript@5.6.2", + "npm:eslint-config-prettier@^9.1.0": "9.1.0_eslint@8.57.1", + "npm:eslint-plugin-import@^2.29.1": "2.31.0_eslint@8.57.1", + "npm:eslint-plugin-jsx-a11y@^6.6.1": "6.10.0_eslint@8.57.1", + "npm:eslint-plugin-prettier@^5.1.3": "5.2.1_eslint@8.57.1_eslint-config-prettier@9.1.0__eslint@8.57.1_prettier@3.2.5", + "npm:eslint-plugin-react-hooks@^4.3.0": "4.6.2_eslint@8.57.1", + "npm:eslint-plugin-react@^7.28.0": "7.37.1_eslint@8.57.1", + "npm:eslint@^8.57.0": "8.57.1", + "npm:husky@^8.0.3": "8.0.3", + "npm:netlify-cli@16.2.0": "16.2.0_inquirer@6.5.2", + "npm:prettier-plugin-jsdoc@^1.3.0": "1.3.0_prettier@3.2.5", + "npm:prettier@3.2.5": "3.2.5", + "npm:typedoc-plugin-missing-exports@^2.1.0": "2.3.0_typedoc@0.25.13__typescript@5.4.5", + "npm:typedoc@~0.25.3": "0.25.13_typescript@5.4.5", + "npm:typescript@^5.4.5": "5.6.2", + "npm:vite@^5.0.12": "5.4.8_@types+node@20.16.10", + "npm:vitest@^2.0.5": "2.1.1_@types+node@20.16.10_@vitest+spy@2.1.1_vite@5.4.8__@types+node@20.16.10" + }, + "npm": { + "@babel/code-frame@7.25.7": { + "integrity": "sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==", + "dependencies": [ + "@babel/highlight", + "picocolors" + ] + }, + "@babel/helper-string-parser@7.25.7": { + "integrity": "sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==" + }, + "@babel/helper-validator-identifier@7.25.7": { + "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==" + }, + "@babel/highlight@7.25.7": { + "integrity": "sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==", + "dependencies": [ + "@babel/helper-validator-identifier", + "chalk@2.4.2", + "js-tokens", + "picocolors" + ] + }, + "@babel/parser@7.25.7": { + "integrity": "sha512-aZn7ETtQsjjGG5HruveUK06cU3Hljuhd9Iojm4M8WWv3wLE6OkE5PWbDUkItmMgegmccaITudyuW5RPYrYlgWw==", + "dependencies": [ + "@babel/types@7.25.7" + ] + }, + "@babel/types@7.25.6": { + "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", + "dependencies": [ + "@babel/helper-string-parser", + "@babel/helper-validator-identifier", + "to-fast-properties" + ] + }, + "@babel/types@7.25.7": { + "integrity": "sha512-vwIVdXG+j+FOpkwqHRcBgHLYNL7XMkufrlaFvL9o6Ai9sJn9+PdyIL5qa0XzTZw084c+u9LOls53eoZWP/W5WQ==", + "dependencies": [ + "@babel/helper-string-parser", + "@babel/helper-validator-identifier", + "to-fast-properties" + ] + }, + "@bugsnag/browser@7.25.0": { + "integrity": "sha512-PzzWy5d9Ly1CU1KkxTB6ZaOw/dO+CYSfVtqxVJccy832e6+7rW/dvSw5Jy7rsNhgcKSKjZq86LtNkPSvritOLA==", + "dependencies": [ + "@bugsnag/core" + ] + }, + "@bugsnag/core@7.25.0": { + "integrity": "sha512-JZLak1b5BVzy77CPcklViZrppac/pE07L3uSDmfSvFYSCGReXkik2txOgV05VlF9EDe36dtUAIIV7iAPDfFpQQ==", + "dependencies": [ + "@bugsnag/cuid", + "@bugsnag/safe-json-stringify", + "error-stack-parser", + "iserror", + "stack-generator" + ] + }, + "@bugsnag/cuid@3.1.1": { + "integrity": "sha512-d2z4b0rEo3chI07FNN1Xds8v25CNeekecU6FC/2Fs9MxY2EipkZTThVcV2YinMn8dvRUlViKOyC50evoUxg8tw==" + }, + "@bugsnag/js@7.20.2": { + "integrity": "sha512-Q08k0h0h6NFwFGkFmib39Uln2WpvJdqT1EGF1JlyYiGW03Y+VopVb9r37pZrRrN9IY08mxaIEO8la5xeaWAs6A==", + "dependencies": [ + "@bugsnag/browser", + "@bugsnag/node" + ] + }, + "@bugsnag/node@7.25.0": { + "integrity": "sha512-KlxBaJ8EREEsfKInybAjTO9LmdDXV3cUH5+XNXyqUZrcRVuPOu4j4xvljh+n24ifok/wbFZTKVXUzrN4iKIeIA==", + "dependencies": [ + "@bugsnag/core", + "byline", + "error-stack-parser", + "iserror", + "pump@3.0.0", + "stack-generator" + ] + }, + "@bugsnag/safe-json-stringify@6.0.0": { + "integrity": "sha512-htzFO1Zc57S8kgdRK9mLcPVTW1BY2ijfH7Dk2CeZmspTWKdKqSo1iwmqrq2WtRjFlo8aRZYgLX0wFrDXF/9DLA==" + }, + "@colors/colors@1.5.0": { + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==" + }, + "@colors/colors@1.6.0": { + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==" + }, + "@cspotcode/source-map-support@0.8.1": { + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dependencies": [ + "@jridgewell/trace-mapping" + ] + }, + "@dabh/diagnostics@2.0.3": { + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dependencies": [ + "colorspace", + "enabled", + "kuler" + ] + }, + "@dependents/detective-less@4.1.0": { + "integrity": "sha512-KrkT6qO5NxqNfy68sBl6CTSoJ4SNDIS5iQArkibhlbGU4LaDukZ3q2HIkh8aUKDio6o4itU4xDR7t82Y2eP1Bg==", + "dependencies": [ + "gonzales-pe", + "node-source-walk" + ] + }, + "@esbuild/aix-ppc64@0.19.11": { + "integrity": "sha512-FnzU0LyE3ySQk7UntJO4+qIiQgI7KoODnZg5xzXIrFJlKd2P2gwHsHY4927xj9y5PJmJSzULiUCWmv7iWnNa7g==" + }, + "@esbuild/aix-ppc64@0.21.5": { + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==" + }, + "@esbuild/android-arm64@0.19.11": { + "integrity": "sha512-aiu7K/5JnLj//KOnOfEZ0D90obUkRzDMyqd/wNAUQ34m4YUPVhRZpnqKV9uqDGxT7cToSDnIHsGooyIczu9T+Q==" + }, + "@esbuild/android-arm64@0.19.2": { + "integrity": "sha512-lsB65vAbe90I/Qe10OjkmrdxSX4UJDjosDgb8sZUKcg3oefEuW2OT2Vozz8ef7wrJbMcmhvCC+hciF8jY/uAkw==" + }, + "@esbuild/android-arm64@0.21.5": { + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==" + }, + "@esbuild/android-arm@0.19.11": { + "integrity": "sha512-5OVapq0ClabvKvQ58Bws8+wkLCV+Rxg7tUVbo9xu034Nm536QTII4YzhaFriQ7rMrorfnFKUsArD2lqKbFY4vw==" + }, + "@esbuild/android-arm@0.19.2": { + "integrity": "sha512-tM8yLeYVe7pRyAu9VMi/Q7aunpLwD139EY1S99xbQkT4/q2qa6eA4ige/WJQYdJ8GBL1K33pPFhPfPdJ/WzT8Q==" + }, + "@esbuild/android-arm@0.21.5": { + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==" + }, + "@esbuild/android-x64@0.19.11": { + "integrity": "sha512-eccxjlfGw43WYoY9QgB82SgGgDbibcqyDTlk3l3C0jOVHKxrjdc9CTwDUQd0vkvYg5um0OH+GpxYvp39r+IPOg==" + }, + "@esbuild/android-x64@0.19.2": { + "integrity": "sha512-qK/TpmHt2M/Hg82WXHRc/W/2SGo/l1thtDHZWqFq7oi24AjZ4O/CpPSu6ZuYKFkEgmZlFoa7CooAyYmuvnaG8w==" + }, + "@esbuild/android-x64@0.21.5": { + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==" + }, + "@esbuild/darwin-arm64@0.19.11": { + "integrity": "sha512-ETp87DRWuSt9KdDVkqSoKoLFHYTrkyz2+65fj9nfXsaV3bMhTCjtQfw3y+um88vGRKRiF7erPrh/ZuIdLUIVxQ==" + }, + "@esbuild/darwin-arm64@0.19.2": { + "integrity": "sha512-Ora8JokrvrzEPEpZO18ZYXkH4asCdc1DLdcVy8TGf5eWtPO1Ie4WroEJzwI52ZGtpODy3+m0a2yEX9l+KUn0tA==" + }, + "@esbuild/darwin-arm64@0.21.5": { + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==" + }, + "@esbuild/darwin-x64@0.19.11": { + "integrity": "sha512-fkFUiS6IUK9WYUO/+22omwetaSNl5/A8giXvQlcinLIjVkxwTLSktbF5f/kJMftM2MJp9+fXqZ5ezS7+SALp4g==" + }, + "@esbuild/darwin-x64@0.19.2": { + "integrity": "sha512-tP+B5UuIbbFMj2hQaUr6EALlHOIOmlLM2FK7jeFBobPy2ERdohI4Ka6ZFjZ1ZYsrHE/hZimGuU90jusRE0pwDw==" + }, + "@esbuild/darwin-x64@0.21.5": { + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==" + }, + "@esbuild/freebsd-arm64@0.19.11": { + "integrity": "sha512-lhoSp5K6bxKRNdXUtHoNc5HhbXVCS8V0iZmDvyWvYq9S5WSfTIHU2UGjcGt7UeS6iEYp9eeymIl5mJBn0yiuxA==" + }, + "@esbuild/freebsd-arm64@0.19.2": { + "integrity": "sha512-YbPY2kc0acfzL1VPVK6EnAlig4f+l8xmq36OZkU0jzBVHcOTyQDhnKQaLzZudNJQyymd9OqQezeaBgkTGdTGeQ==" + }, + "@esbuild/freebsd-arm64@0.21.5": { + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==" + }, + "@esbuild/freebsd-x64@0.19.11": { + "integrity": "sha512-JkUqn44AffGXitVI6/AbQdoYAq0TEullFdqcMY/PCUZ36xJ9ZJRtQabzMA+Vi7r78+25ZIBosLTOKnUXBSi1Kw==" + }, + "@esbuild/freebsd-x64@0.19.2": { + "integrity": "sha512-nSO5uZT2clM6hosjWHAsS15hLrwCvIWx+b2e3lZ3MwbYSaXwvfO528OF+dLjas1g3bZonciivI8qKR/Hm7IWGw==" + }, + "@esbuild/freebsd-x64@0.21.5": { + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==" + }, + "@esbuild/linux-arm64@0.19.11": { + "integrity": "sha512-LneLg3ypEeveBSMuoa0kwMpCGmpu8XQUh+mL8XXwoYZ6Be2qBnVtcDI5azSvh7vioMDhoJFZzp9GWp9IWpYoUg==" + }, + "@esbuild/linux-arm64@0.19.2": { + "integrity": "sha512-ig2P7GeG//zWlU0AggA3pV1h5gdix0MA3wgB+NsnBXViwiGgY77fuN9Wr5uoCrs2YzaYfogXgsWZbm+HGr09xg==" + }, + "@esbuild/linux-arm64@0.21.5": { + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==" + }, + "@esbuild/linux-arm@0.19.11": { + "integrity": "sha512-3CRkr9+vCV2XJbjwgzjPtO8T0SZUmRZla+UL1jw+XqHZPkPgZiyWvbDvl9rqAN8Zl7qJF0O/9ycMtjU67HN9/Q==" + }, + "@esbuild/linux-arm@0.19.2": { + "integrity": "sha512-Odalh8hICg7SOD7XCj0YLpYCEc+6mkoq63UnExDCiRA2wXEmGlK5JVrW50vZR9Qz4qkvqnHcpH+OFEggO3PgTg==" + }, + "@esbuild/linux-arm@0.21.5": { + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==" + }, + "@esbuild/linux-ia32@0.19.11": { + "integrity": "sha512-caHy++CsD8Bgq2V5CodbJjFPEiDPq8JJmBdeyZ8GWVQMjRD0sU548nNdwPNvKjVpamYYVL40AORekgfIubwHoA==" + }, + "@esbuild/linux-ia32@0.19.2": { + "integrity": "sha512-mLfp0ziRPOLSTek0Gd9T5B8AtzKAkoZE70fneiiyPlSnUKKI4lp+mGEnQXcQEHLJAcIYDPSyBvsUbKUG2ri/XQ==" + }, + "@esbuild/linux-ia32@0.21.5": { + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==" + }, + "@esbuild/linux-loong64@0.19.11": { + "integrity": "sha512-ppZSSLVpPrwHccvC6nQVZaSHlFsvCQyjnvirnVjbKSHuE5N24Yl8F3UwYUUR1UEPaFObGD2tSvVKbvR+uT1Nrg==" + }, + "@esbuild/linux-loong64@0.19.2": { + "integrity": "sha512-hn28+JNDTxxCpnYjdDYVMNTR3SKavyLlCHHkufHV91fkewpIyQchS1d8wSbmXhs1fiYDpNww8KTFlJ1dHsxeSw==" + }, + "@esbuild/linux-loong64@0.21.5": { + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==" + }, + "@esbuild/linux-mips64el@0.19.11": { + "integrity": "sha512-B5x9j0OgjG+v1dF2DkH34lr+7Gmv0kzX6/V0afF41FkPMMqaQ77pH7CrhWeR22aEeHKaeZVtZ6yFwlxOKPVFyg==" + }, + "@esbuild/linux-mips64el@0.19.2": { + "integrity": "sha512-KbXaC0Sejt7vD2fEgPoIKb6nxkfYW9OmFUK9XQE4//PvGIxNIfPk1NmlHmMg6f25x57rpmEFrn1OotASYIAaTg==" + }, + "@esbuild/linux-mips64el@0.21.5": { + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==" + }, + "@esbuild/linux-ppc64@0.19.11": { + "integrity": "sha512-MHrZYLeCG8vXblMetWyttkdVRjQlQUb/oMgBNurVEnhj4YWOr4G5lmBfZjHYQHHN0g6yDmCAQRR8MUHldvvRDA==" + }, + "@esbuild/linux-ppc64@0.19.2": { + "integrity": "sha512-dJ0kE8KTqbiHtA3Fc/zn7lCd7pqVr4JcT0JqOnbj4LLzYnp+7h8Qi4yjfq42ZlHfhOCM42rBh0EwHYLL6LEzcw==" + }, + "@esbuild/linux-ppc64@0.21.5": { + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==" + }, + "@esbuild/linux-riscv64@0.19.11": { + "integrity": "sha512-f3DY++t94uVg141dozDu4CCUkYW+09rWtaWfnb3bqe4w5NqmZd6nPVBm+qbz7WaHZCoqXqHz5p6CM6qv3qnSSQ==" + }, + "@esbuild/linux-riscv64@0.19.2": { + "integrity": "sha512-7Z/jKNFufZ/bbu4INqqCN6DDlrmOTmdw6D0gH+6Y7auok2r02Ur661qPuXidPOJ+FSgbEeQnnAGgsVynfLuOEw==" + }, + "@esbuild/linux-riscv64@0.21.5": { + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==" + }, + "@esbuild/linux-s390x@0.19.11": { + "integrity": "sha512-A5xdUoyWJHMMlcSMcPGVLzYzpcY8QP1RtYzX5/bS4dvjBGVxdhuiYyFwp7z74ocV7WDc0n1harxmpq2ePOjI0Q==" + }, + "@esbuild/linux-s390x@0.19.2": { + "integrity": "sha512-U+RinR6aXXABFCcAY4gSlv4CL1oOVvSSCdseQmGO66H+XyuQGZIUdhG56SZaDJQcLmrSfRmx5XZOWyCJPRqS7g==" + }, + "@esbuild/linux-s390x@0.21.5": { + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==" + }, + "@esbuild/linux-x64@0.19.11": { + "integrity": "sha512-grbyMlVCvJSfxFQUndw5mCtWs5LO1gUlwP4CDi4iJBbVpZcqLVT29FxgGuBJGSzyOxotFG4LoO5X+M1350zmPA==" + }, + "@esbuild/linux-x64@0.19.2": { + "integrity": "sha512-oxzHTEv6VPm3XXNaHPyUTTte+3wGv7qVQtqaZCrgstI16gCuhNOtBXLEBkBREP57YTd68P0VgDgG73jSD8bwXQ==" + }, + "@esbuild/linux-x64@0.21.5": { + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==" + }, + "@esbuild/netbsd-x64@0.19.11": { + "integrity": "sha512-13jvrQZJc3P230OhU8xgwUnDeuC/9egsjTkXN49b3GcS5BKvJqZn86aGM8W9pd14Kd+u7HuFBMVtrNGhh6fHEQ==" + }, + "@esbuild/netbsd-x64@0.19.2": { + "integrity": "sha512-WNa5zZk1XpTTwMDompZmvQLHszDDDN7lYjEHCUmAGB83Bgs20EMs7ICD+oKeT6xt4phV4NDdSi/8OfjPbSbZfQ==" + }, + "@esbuild/netbsd-x64@0.21.5": { + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==" + }, + "@esbuild/openbsd-x64@0.19.11": { + "integrity": "sha512-ysyOGZuTp6SNKPE11INDUeFVVQFrhcNDVUgSQVDzqsqX38DjhPEPATpid04LCoUr2WXhQTEZ8ct/EgJCUDpyNw==" + }, + "@esbuild/openbsd-x64@0.19.2": { + "integrity": "sha512-S6kI1aT3S++Dedb7vxIuUOb3oAxqxk2Rh5rOXOTYnzN8JzW1VzBd+IqPiSpgitu45042SYD3HCoEyhLKQcDFDw==" + }, + "@esbuild/openbsd-x64@0.21.5": { + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==" + }, + "@esbuild/sunos-x64@0.19.11": { + "integrity": "sha512-Hf+Sad9nVwvtxy4DXCZQqLpgmRTQqyFyhT3bZ4F2XlJCjxGmRFF0Shwn9rzhOYRB61w9VMXUkxlBy56dk9JJiQ==" + }, + "@esbuild/sunos-x64@0.19.2": { + "integrity": "sha512-VXSSMsmb+Z8LbsQGcBMiM+fYObDNRm8p7tkUDMPG/g4fhFX5DEFmjxIEa3N8Zr96SjsJ1woAhF0DUnS3MF3ARw==" + }, + "@esbuild/sunos-x64@0.21.5": { + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==" + }, + "@esbuild/win32-arm64@0.19.11": { + "integrity": "sha512-0P58Sbi0LctOMOQbpEOvOL44Ne0sqbS0XWHMvvrg6NE5jQ1xguCSSw9jQeUk2lfrXYsKDdOe6K+oZiwKPilYPQ==" + }, + "@esbuild/win32-arm64@0.19.2": { + "integrity": "sha512-5NayUlSAyb5PQYFAU9x3bHdsqB88RC3aM9lKDAz4X1mo/EchMIT1Q+pSeBXNgkfNmRecLXA0O8xP+x8V+g/LKg==" + }, + "@esbuild/win32-arm64@0.21.5": { + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==" + }, + "@esbuild/win32-ia32@0.19.11": { + "integrity": "sha512-6YOrWS+sDJDmshdBIQU+Uoyh7pQKrdykdefC1avn76ss5c+RN6gut3LZA4E2cH5xUEp5/cA0+YxRaVtRAb0xBg==" + }, + "@esbuild/win32-ia32@0.19.2": { + "integrity": "sha512-47gL/ek1v36iN0wL9L4Q2MFdujR0poLZMJwhO2/N3gA89jgHp4MR8DKCmwYtGNksbfJb9JoTtbkoe6sDhg2QTA==" + }, + "@esbuild/win32-ia32@0.21.5": { + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==" + }, + "@esbuild/win32-x64@0.19.11": { + "integrity": "sha512-vfkhltrjCAb603XaFhqhAF4LGDi2M4OrCRrFusyQ+iTLQ/o60QQXxc9cZC/FFpihBI9N1Grn6SMKVJ4KP7Fuiw==" + }, + "@esbuild/win32-x64@0.19.2": { + "integrity": "sha512-tcuhV7ncXBqbt/Ybf0IyrMcwVOAPDckMK9rXNHtF17UTK18OKLpg08glminN06pt2WCoALhXdLfSPbVvK/6fxw==" + }, + "@esbuild/win32-x64@0.21.5": { + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==" + }, + "@eslint-community/eslint-utils@4.4.0_eslint@8.57.1": { + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dependencies": [ + "eslint", + "eslint-visitor-keys" + ] + }, + "@eslint-community/regexpp@4.11.1": { + "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==" + }, + "@eslint/eslintrc@2.1.4": { + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dependencies": [ + "ajv@6.12.6", + "debug@4.3.7", + "espree", + "globals", + "ignore", + "import-fresh", + "js-yaml", + "minimatch@3.1.2", + "strip-json-comments@3.1.1" + ] + }, + "@eslint/js@8.57.1": { + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==" + }, + "@fastify/accept-negotiator@1.1.0": { + "integrity": "sha512-OIHZrb2ImZ7XG85HXOONLcJWGosv7sIvM2ifAPQVhg9Lv7qdmMBNVaai4QTdyuaqbKM5eO6sLSQOYI7wEQeCJQ==" + }, + "@fastify/ajv-compiler@3.6.0_ajv@8.17.1": { + "integrity": "sha512-LwdXQJjmMD+GwLOkP7TVC68qa+pSSogeWWmznRJ/coyTcfe9qA05AHFSe1eZFwK6q+xVRpChnvFUkf1iYaSZsQ==", + "dependencies": [ + "ajv@8.17.1", + "ajv-formats@2.1.1_ajv@8.17.1", + "fast-uri@2.4.0" + ] + }, + "@fastify/error@3.4.1": { + "integrity": "sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ==" + }, + "@fastify/fast-json-stringify-compiler@4.3.0": { + "integrity": "sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==", + "dependencies": [ + "fast-json-stringify" + ] + }, + "@fastify/merge-json-schemas@0.1.1": { + "integrity": "sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==", + "dependencies": [ + "fast-deep-equal" + ] + }, + "@fastify/send@2.1.0": { + "integrity": "sha512-yNYiY6sDkexoJR0D8IDy3aRP3+L4wdqCpvx5WP+VtEU58sn7USmKynBzDQex5X42Zzvw2gNzzYgP90UfWShLFA==", + "dependencies": [ + "@lukeed/ms", + "escape-html", + "fast-decode-uri-component", + "http-errors@2.0.0", + "mime@3.0.0" + ] + }, + "@fastify/static@6.10.2": { + "integrity": "sha512-UoaMvIHSBLCZBYOVZwFRYqX2ufUhd7FFMYGDeSf0Z+D8jhYtwljjmuQGuanUP8kS4y/ZEV1a8mfLha3zNwsnnQ==", + "dependencies": [ + "@fastify/accept-negotiator", + "@fastify/send", + "content-disposition", + "fastify-plugin", + "glob@8.1.0", + "p-limit@3.1.0", + "readable-stream@4.5.2" + ] + }, + "@grpc/grpc-js@1.12.0": { + "integrity": "sha512-eWdP97A6xKtZXVP/ze9y8zYRB2t6ugQAuLXFuZXAsyqmyltaAjl4yPkmIfc0wuTFJMOUF1AdvIFQCL7fMtaX6g==", + "dependencies": [ + "@grpc/proto-loader", + "@js-sdsl/ordered-map" + ] + }, + "@grpc/proto-loader@0.7.13": { + "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "dependencies": [ + "lodash.camelcase", + "long@5.2.3", + "protobufjs", + "yargs" + ] + }, + "@honeycombio/opentelemetry-node@0.4.0_@opentelemetry+api@1.9.0": { + "integrity": "sha512-6PFX8FGW7uA7vQ3mxNIoN36rH9Zx5kXh4kKP9zu28nynyWyy9JE3l8PNJYd9FS2L/d88ZUpQAiQ1pROaANd5MA==", + "dependencies": [ + "@grpc/grpc-js", + "@opentelemetry/api@1.9.0", + "@opentelemetry/exporter-metrics-otlp-grpc", + "@opentelemetry/exporter-metrics-otlp-proto", + "@opentelemetry/exporter-trace-otlp-grpc@0.36.1_@opentelemetry+api@1.9.0", + "@opentelemetry/exporter-trace-otlp-proto@0.36.1_@opentelemetry+api@1.9.0", + "@opentelemetry/resources@1.26.0_@opentelemetry+api@1.9.0", + "@opentelemetry/sdk-metrics@1.26.0_@opentelemetry+api@1.9.0", + "@opentelemetry/sdk-node", + "@opentelemetry/sdk-trace-base@1.26.0_@opentelemetry+api@1.9.0", + "axios" + ] + }, + "@humanwhocodes/config-array@0.13.0": { + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "dependencies": [ + "@humanwhocodes/object-schema", + "debug@4.3.7", + "minimatch@3.1.2" + ] + }, + "@humanwhocodes/module-importer@1.0.1": { + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==" + }, + "@humanwhocodes/momoa@2.0.4": { + "integrity": "sha512-RE815I4arJFtt+FVeU1Tgp9/Xvecacji8w/V6XtXsWWH/wz/eNkNbhb+ny/+PlVZjV0rxQpRSQKNKE3lcktHEA==" + }, + "@humanwhocodes/object-schema@2.0.3": { + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==" + }, + "@import-maps/resolve@1.0.1": { + "integrity": "sha512-tWZNBIS1CoekcwlMuyG2mr0a1Wo5lb5lEHwwWvZo+5GLgr3e9LLDTtmgtCWEwBpXMkxn9D+2W9j2FY6eZQq0tA==" + }, + "@isaacs/cliui@8.0.2": { + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dependencies": [ + "string-width@5.1.2", + "string-width-cjs@npm:string-width@4.2.3", + "strip-ansi@7.1.0", + "strip-ansi-cjs@npm:strip-ansi@6.0.1", + "wrap-ansi@8.1.0", + "wrap-ansi-cjs@npm:wrap-ansi@7.0.0" + ] + }, + "@jest/types@27.5.1": { + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "dependencies": [ + "@types/istanbul-lib-coverage", + "@types/istanbul-reports", + "@types/node@22.5.4", + "@types/yargs", + "chalk@4.1.2" + ] + }, + "@jridgewell/resolve-uri@3.1.2": { + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==" + }, + "@jridgewell/sourcemap-codec@1.5.0": { + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" + }, + "@jridgewell/trace-mapping@0.3.9": { + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dependencies": [ + "@jridgewell/resolve-uri", + "@jridgewell/sourcemap-codec" + ] + }, + "@js-sdsl/ordered-map@4.4.2": { + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==" + }, + "@lukeed/ms@2.0.2": { + "integrity": "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==" + }, + "@mapbox/node-pre-gyp@1.0.11": { + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "dependencies": [ + "detect-libc", + "https-proxy-agent", + "make-dir@3.1.0", + "node-fetch@2.6.12", + "nopt", + "npmlog", + "rimraf", + "semver@7.6.3", + "tar" + ] + }, + "@netlify/binary-info@1.0.0": { + "integrity": "sha512-4wMPu9iN3/HL97QblBsBay3E1etIciR84izI3U+4iALY+JHCrI+a2jO0qbAZ/nxKoegypYEaiiqWXylm+/zfrw==" + }, + "@netlify/build-info@7.7.4": { + "integrity": "sha512-dJt4looPD2UuGprGB29Y/tXAHKKV05gWOIsY71gByv7fs2DQ3LqXYXy4qdGcrGAAeEKRHBwXOMfAn1TAq4hgMQ==", + "dependencies": [ + "@bugsnag/js", + "dot-prop@7.2.0", + "find-up@6.3.0", + "minimatch@9.0.5", + "read-pkg", + "semver@7.6.3", + "toml", + "yaml", + "yargs" + ] + }, + "@netlify/build@29.20.8_typescript@5.6.2": { + "integrity": "sha512-dPO62SIzn4KkaVDLC9UPzOs8vyBD36wz4DcjSNFhxJ0ASp51HneJaWFQi0vZon4nizCob7iBKVm4QpgqAkeTjA==", + "dependencies": [ + "@bugsnag/js", + "@honeycombio/opentelemetry-node", + "@netlify/cache-utils", + "@netlify/config", + "@netlify/edge-bundler", + "@netlify/framework-info", + "@netlify/functions-utils", + "@netlify/git-utils", + "@netlify/plugins-list", + "@netlify/run-utils", + "@netlify/zip-it-and-ship-it@9.17.0", + "@opentelemetry/api@1.9.0", + "@sindresorhus/slugify", + "ansi-escapes@6.2.0", + "chalk@5.2.0", + "clean-stack", + "execa@6.1.0", + "fdir", + "figures@5.0.0", + "filter-obj@5.1.0", + "got", + "hot-shots", + "indent-string@5.0.0", + "is-plain-obj@4.1.0", + "js-yaml", + "keep-func-props", + "locate-path@7.2.0", + "log-process-errors", + "map-obj", + "memoize-one", + "os-name", + "p-event@5.0.1", + "p-every", + "p-filter", + "p-locate@6.0.0", + "p-reduce", + "path-exists@5.0.0", + "path-type@5.0.0", + "pkg-dir", + "pretty-ms", + "ps-list", + "read-pkg-up", + "readdirp@3.6.0", + "resolve@2.0.0-next.5", + "rfdc", + "safe-json-stringify", + "semver@7.6.3", + "string-width@5.1.2", + "strip-ansi@7.1.0", + "supports-color@9.4.0", + "terminal-link", + "ts-node", + "typescript@5.6.2", + "uuid@9.0.0", + "yargs" + ] + }, + "@netlify/cache-utils@5.1.6": { + "integrity": "sha512-0K1+5umxENy9H3CC+v5qGQbeTmKv/PBAhOxPKK6GPykOVa7OxT26KGMU7Jozo6pVNeLPJUvCCMw48ycwtQ1fvw==", + "dependencies": [ + "cpy", + "get-stream@6.0.1", + "globby@13.2.2", + "junk", + "locate-path@7.2.0", + "move-file", + "path-exists@5.0.0", + "readdirp@3.6.0" + ] + }, + "@netlify/config@20.8.0": { + "integrity": "sha512-jzklg2Kj9D/2h+QO2MNbbc7oz9Wo56Zp1ob/kaG9P7DJLZSgc0h6G2GQSybqKqvApLju+8iqPB2rMAp02QSjpA==", + "dependencies": [ + "chalk@5.2.0", + "cron-parser", + "deepmerge", + "dot-prop@7.2.0", + "execa@6.1.0", + "fast-safe-stringify", + "figures@5.0.0", + "filter-obj@5.1.0", + "find-up@6.3.0", + "indent-string@5.0.0", + "is-plain-obj@4.1.0", + "js-yaml", + "map-obj", + "netlify", + "netlify-headers-parser", + "netlify-redirect-parser", + "node-fetch@3.3.2", + "omit.js", + "p-locate@6.0.0", + "path-type@5.0.0", + "toml", + "tomlify-j0.4", + "validate-npm-package-name", + "yargs" + ] + }, + "@netlify/edge-bundler@8.19.0_ajv@8.17.1": { + "integrity": "sha512-blIZHLXlEXcjpAhd2TJ+Rw7H+WhNXSBfmFfRHn2pyzNiAbQa71eU0eNUE2+Nw58DGfvUC0unerbTlN2gd3iVdA==", + "dependencies": [ + "@import-maps/resolve", + "ajv@8.17.1", + "ajv-errors", + "better-ajv-errors", + "common-path-prefix", + "env-paths", + "execa@6.1.0", + "find-up@6.3.0", + "get-port@6.1.2", + "is-path-inside@4.0.0", + "jsonc-parser", + "node-fetch@3.3.2", + "node-stream-zip", + "p-retry", + "p-wait-for@4.1.0", + "path-key@4.0.0", + "regexp-tree", + "semver@7.6.3", + "tmp-promise", + "urlpattern-polyfill", + "uuid@9.0.0" + ] + }, + "@netlify/framework-info@9.8.13": { + "integrity": "sha512-ZZXCggokY/y5Sz93XYbl/Lig1UAUSWPMBiQRpkVfbrrkjmW2ZPkYS/BgrM2/MxwXRvYhc/TQpZX6y5JPe3quQg==", + "dependencies": [ + "ajv@8.17.1", + "filter-obj@5.1.0", + "find-up@6.3.0", + "is-plain-obj@4.1.0", + "locate-path@7.2.0", + "p-filter", + "p-locate@6.0.0", + "process@0.11.10", + "read-pkg-up", + "semver@7.6.3" + ] + }, + "@netlify/functions-utils@5.2.86": { + "integrity": "sha512-aG4hAeRJ3NVUCBImxIaI4H1/OR9vQ4iaLhRd/dPCEgKccQyw2/EOjuGNg28tkhqwTNnCLpN3uDM2KK5n02IXUg==", + "dependencies": [ + "@netlify/zip-it-and-ship-it@9.39.5", + "cpy", + "path-exists@5.0.0" + ] + }, + "@netlify/git-utils@5.1.1": { + "integrity": "sha512-oyHieuTZH3rKTmg7EKpGEGa28IFxta2oXuVwpPJI/FJAtBje3UE+yko0eDjNufgm3AyGa8G77trUxgBhInAYuw==", + "dependencies": [ + "execa@6.1.0", + "map-obj", + "micromatch@4.0.8", + "moize", + "path-exists@5.0.0" + ] + }, + "@netlify/local-functions-proxy-darwin-arm64@1.1.1": { + "integrity": "sha512-lphJ9qqZ3glnKWEqlemU1LMqXxtJ/tKf7VzakqqyjigwLscXSZSb6fupSjQfd4tR1xqxA76ylws/2HDhc/gs+Q==" + }, + "@netlify/local-functions-proxy-darwin-x64@1.1.1": { + "integrity": "sha512-4CRB0H+dXZzoEklq5Jpmg+chizXlVwCko94d8+UHWCgy/bA3M/rU/BJ8OLZisnJaAktHoeLABKtcLOhtRHpxZQ==" + }, + "@netlify/local-functions-proxy-freebsd-arm64@1.1.1": { + "integrity": "sha512-u13lWTVMJDF0A6jX7V4N3HYGTIHLe5d1Z2wT43fSIHwXkTs6UXi72cGSraisajG+5JFIwHfPr7asw5vxFC0P9w==" + }, + "@netlify/local-functions-proxy-freebsd-x64@1.1.1": { + "integrity": "sha512-g5xw4xATK5YDzvXtzJ8S1qSkWBiyF8VVRehXPMOAMzpGjCX86twYhWp8rbAk7yA1zBWmmWrWNA2Odq/MgpKJJg==" + }, + "@netlify/local-functions-proxy-linux-arm64@1.1.1": { + "integrity": "sha512-dPGu1H5n8na7mBKxiXQ+FNmthDAiA57wqgpm5JMAHtcdcmRvcXwJkwWVGvwfj8ShhYJHQaSaS9oPgO+mpKkgmA==" + }, + "@netlify/local-functions-proxy-linux-arm@1.1.1": { + "integrity": "sha512-YsTpL+AbHwQrfHWXmKnwUrJBjoUON363nr6jUG1ueYnpbbv6wTUA7gI5snMi/gkGpqFusBthAA7C30e6bixfiA==" + }, + "@netlify/local-functions-proxy-linux-ia32@1.1.1": { + "integrity": "sha512-Ra0FlXDrmPRaq+rYH3/ttkXSrwk1D5Zx/Na7UPfJZxMY7Qo5iY4bgi/FuzjzWzlp0uuKZOhYOYzYzsIIyrSvmw==" + }, + "@netlify/local-functions-proxy-linux-ppc64@1.1.1": { + "integrity": "sha512-oXf1satwqwUUxz7LHS1BxbRqc4FFEKIDFTls04eXiLReFR3sqv9H/QuYNTCCDMuRcCOd92qKyDfATdnxT4HR8w==" + }, + "@netlify/local-functions-proxy-linux-x64@1.1.1": { + "integrity": "sha512-bS3u4JuDg/eC0y4Na3i/29JBOxrdUvsK5JSjHfzUeZEbOcuXYf4KavTpHS5uikdvTgyczoSrvbmQJ5m0FLXfLA==" + }, + "@netlify/local-functions-proxy-openbsd-x64@1.1.1": { + "integrity": "sha512-1xLef/kLRNkBTXJ+ZGoRFcwsFxd/B2H3oeJZyXaZ3CN5umd9Mv9wZuAD74NuMt/535yRva8jtAJqvEgl9xMSdA==" + }, + "@netlify/local-functions-proxy-win32-ia32@1.1.1": { + "integrity": "sha512-4IOMDBxp2f8VbIkhZ85zGNDrZR4ey8d68fCMSOIwitjsnKav35YrCf8UmAh3UR6CNIRJdJL4MW1GYePJ7iJ8uA==" + }, + "@netlify/local-functions-proxy-win32-x64@1.1.1": { + "integrity": "sha512-VCBXBJWBujVxyo5f+3r8ovLc9I7wJqpmgDn3ixs1fvdrER5Ac+SzYwYH4mUug9HI08mzTSAKZErzKeuadSez3w==" + }, + "@netlify/local-functions-proxy@1.1.1": { + "integrity": "sha512-eXSsayLT6PMvjzFQpjC9nkg2Otc3lZ5GoYele9M6f8PmsvWpaXRhwjNQ0NYhQQ2UZbLMIiO2dH8dbRsT3bMkFw==", + "dependencies": [ + "@netlify/local-functions-proxy-darwin-arm64", + "@netlify/local-functions-proxy-darwin-x64", + "@netlify/local-functions-proxy-freebsd-arm64", + "@netlify/local-functions-proxy-freebsd-x64", + "@netlify/local-functions-proxy-linux-arm", + "@netlify/local-functions-proxy-linux-arm64", + "@netlify/local-functions-proxy-linux-ia32", + "@netlify/local-functions-proxy-linux-ppc64", + "@netlify/local-functions-proxy-linux-x64", + "@netlify/local-functions-proxy-openbsd-x64", + "@netlify/local-functions-proxy-win32-ia32", + "@netlify/local-functions-proxy-win32-x64" + ] + }, + "@netlify/node-cookies@0.1.0": { + "integrity": "sha512-OAs1xG+FfLX0LoRASpqzVntVV/RpYkgpI0VrUnw2u0Q1qiZUzcPffxRK8HF3gc4GjuhG5ahOEMJ9bswBiZPq0g==" + }, + "@netlify/open-api@2.34.0": { + "integrity": "sha512-C4v7Od/vnGgZ1P4JK3Fn9uUi9HkTxeUqUtj4OLnGD+rGyaVrl4JY89xMCoVksijDtO8XylYFU59CSTnQNeNw7g==" + }, + "@netlify/plugins-list@6.80.0": { + "integrity": "sha512-bCKLI51UZ70ziIWsf2nvgPd4XuG6m8AMCoHiYtl/BSsiaSBfmryZnTTqdRXerH09tBRpbPPwzaEgUJwyU9o8Qw==" + }, + "@netlify/run-utils@5.1.1": { + "integrity": "sha512-V2B8ZB19heVKa715uOeDkztxLH7uaqZ+9U5fV7BRzbQ2514DO5Vxj9hG0irzuRLfZXZZjp/chPUesv4VVsce/A==", + "dependencies": [ + "execa@6.1.0" + ] + }, + "@netlify/serverless-functions-api@1.26.1": { + "integrity": "sha512-q3L9i3HoNfz0SGpTIS4zTcKBbRkxzCRpd169eyiTuk3IwcPC3/85mzLHranlKo2b+HYT0gu37YxGB45aD8A3Tw==", + "dependencies": [ + "@netlify/node-cookies", + "urlpattern-polyfill" + ] + }, + "@netlify/serverless-functions-api@1.7.3": { + "integrity": "sha512-n6/7cJlSWvvbBlUOEAbkGyEld80S6KbG/ldQI9OhLfe1lTatgKmrTNIgqVNpaWpUdTgP2OHWFjmFBzkxxBWs5w==", + "dependencies": [ + "@netlify/node-cookies", + "urlpattern-polyfill" + ] + }, + "@netlify/zip-it-and-ship-it@9.17.0": { + "integrity": "sha512-7wnrWxtczXzBMYh9QXmvG9WkCJSyK+abQGdhwSoZcFPQ0u0HZzY/9rU8jLcRnTodEK20lZPil60FRU/Nta5spg==", + "dependencies": [ + "@babel/parser", + "@netlify/binary-info", + "@netlify/serverless-functions-api@1.7.3", + "@vercel/nft@0.23.1", + "archiver@5.3.2", + "common-path-prefix", + "cp-file@10.0.0", + "es-module-lexer", + "esbuild@0.19.2", + "execa@6.1.0", + "filter-obj@5.1.0", + "find-up@6.3.0", + "get-tsconfig", + "glob@8.1.0", + "is-builtin-module", + "is-path-inside@4.0.0", + "junk", + "locate-path@7.2.0", + "merge-options", + "minimatch@9.0.5", + "normalize-path@3.0.0", + "p-map@5.5.0", + "path-exists@5.0.0", + "precinct", + "require-package-name", + "resolve@2.0.0-next.5", + "semver@7.6.3", + "tmp-promise", + "toml", + "unixify", + "urlpattern-polyfill", + "yargs" + ] + }, + "@netlify/zip-it-and-ship-it@9.39.5": { + "integrity": "sha512-Kj9rTshNo20YCuW/5IUZfmo/oCfzKUSk6nqBw7Lh0FLoKNVXe+6+YPRY2U9/VGuNqG/pTWPKnNYOzFyAI9xPIQ==", + "dependencies": [ + "@babel/parser", + "@babel/types@7.25.6", + "@netlify/binary-info", + "@netlify/serverless-functions-api@1.26.1", + "@vercel/nft@0.27.4_acorn@8.12.1", + "archiver@7.0.1", + "common-path-prefix", + "cp-file@10.0.0", + "es-module-lexer", + "esbuild@0.19.11", + "execa@6.1.0", + "fast-glob", + "filter-obj@5.1.0", + "find-up@6.3.0", + "glob@8.1.0", + "is-builtin-module", + "is-path-inside@4.0.0", + "junk", + "locate-path@7.2.0", + "merge-options", + "minimatch@9.0.5", + "normalize-path@3.0.0", + "p-map@5.5.0", + "path-exists@5.0.0", + "precinct", + "require-package-name", + "resolve@2.0.0-next.5", + "semver@7.6.3", + "tmp-promise", + "toml", + "unixify", + "urlpattern-polyfill", + "yargs", + "zod" + ] + }, + "@nodelib/fs.scandir@2.1.5": { + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dependencies": [ + "@nodelib/fs.stat", + "run-parallel" + ] + }, + "@nodelib/fs.stat@2.0.5": { + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" + }, + "@nodelib/fs.walk@1.2.8": { + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dependencies": [ + "@nodelib/fs.scandir", + "fastq" + ] + }, + "@octokit/auth-token@3.0.4": { + "integrity": "sha512-TWFX7cZF2LXoCvdmJWY7XVPi74aSY0+FfBZNSXEXFkMpjcqsQwDSYVv5FhRFaI0V1ECnwbz4j59T/G+rXNWaIQ==" + }, + "@octokit/core@4.2.4": { + "integrity": "sha512-rYKilwgzQ7/imScn3M9/pFfUf4I1AZEH3KhyJmtPdE2zfaXAn2mFfUy4FbKewzc2We5y/LlKLj36fWJLKC2SIQ==", + "dependencies": [ + "@octokit/auth-token", + "@octokit/graphql", + "@octokit/request", + "@octokit/request-error", + "@octokit/types@9.3.2", + "before-after-hook", + "universal-user-agent" + ] + }, + "@octokit/endpoint@7.0.6": { + "integrity": "sha512-5L4fseVRUsDFGR00tMWD/Trdeeihn999rTMGRMC1G/Ldi1uWlWJzI98H4Iak5DB/RVvQuyMYKqSK/R6mbSOQyg==", + "dependencies": [ + "@octokit/types@9.3.2", + "is-plain-object@5.0.0", + "universal-user-agent" + ] + }, + "@octokit/graphql@5.0.6": { + "integrity": "sha512-Fxyxdy/JH0MnIB5h+UQ3yCoh1FG4kWXfFKkpWqjZHw/p+Kc8Y44Hu/kCgNBT6nU1shNumEchmW/sUO1JuQnPcw==", + "dependencies": [ + "@octokit/request", + "@octokit/types@9.3.2", + "universal-user-agent" + ] + }, + "@octokit/openapi-types@18.1.1": { + "integrity": "sha512-VRaeH8nCDtF5aXWnjPuEMIYf1itK/s3JYyJcWFJT8X9pSNnBtriDf7wlEWsGuhPLl4QIH4xM8fqTXDwJ3Mu6sw==" + }, + "@octokit/plugin-paginate-rest@6.1.2_@octokit+core@4.2.4": { + "integrity": "sha512-qhrmtQeHU/IivxucOV1bbI/xZyC/iOBhclokv7Sut5vnejAIAEXVcGQeRpQlU39E0WwK9lNvJHphHri/DB6lbQ==", + "dependencies": [ + "@octokit/core", + "@octokit/tsconfig", + "@octokit/types@9.3.2" + ] + }, + "@octokit/plugin-request-log@1.0.4_@octokit+core@4.2.4": { + "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", + "dependencies": [ + "@octokit/core" + ] + }, + "@octokit/plugin-rest-endpoint-methods@7.2.3_@octokit+core@4.2.4": { + "integrity": "sha512-I5Gml6kTAkzVlN7KCtjOM+Ruwe/rQppp0QU372K1GP7kNOYEKe8Xn5BW4sE62JAHdwpq95OQK/qGNyKQMUzVgA==", + "dependencies": [ + "@octokit/core", + "@octokit/types@10.0.0" + ] + }, + "@octokit/request-error@3.0.3": { + "integrity": "sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==", + "dependencies": [ + "@octokit/types@9.3.2", + "deprecation", + "once" + ] + }, + "@octokit/request@6.2.8": { + "integrity": "sha512-ow4+pkVQ+6XVVsekSYBzJC0VTVvh/FCTUUgTsboGq+DTeWdyIFV8WSCdo0RIxk6wSkBTHqIK1mYuY7nOBXOchw==", + "dependencies": [ + "@octokit/endpoint", + "@octokit/request-error", + "@octokit/types@9.3.2", + "is-plain-object@5.0.0", + "node-fetch@2.6.12", + "universal-user-agent" + ] + }, + "@octokit/rest@19.0.13_@octokit+core@4.2.4": { + "integrity": "sha512-/EzVox5V9gYGdbAI+ovYj3nXQT1TtTHRT+0eZPcuC05UFSWO3mdO9UY1C0i2eLF9Un1ONJkAk+IEtYGAC+TahA==", + "dependencies": [ + "@octokit/core", + "@octokit/plugin-paginate-rest", + "@octokit/plugin-request-log", + "@octokit/plugin-rest-endpoint-methods" + ] + }, + "@octokit/tsconfig@1.0.2": { + "integrity": "sha512-I0vDR0rdtP8p2lGMzvsJzbhdOWy405HcGovrspJ8RRibHnyRgggUSNO5AIox5LmqiwmatHKYsvj6VGFHkqS7lA==" + }, + "@octokit/types@10.0.0": { + "integrity": "sha512-Vm8IddVmhCgU1fxC1eyinpwqzXPEYu0NrYzD3YZjlGjyftdLBTeqNblRC0jmJmgxbJIsQlyogVeGnrNaaMVzIg==", + "dependencies": [ + "@octokit/openapi-types" + ] + }, + "@octokit/types@9.3.2": { + "integrity": "sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA==", + "dependencies": [ + "@octokit/openapi-types" + ] + }, + "@opentelemetry/api@1.4.1": { + "integrity": "sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA==" + }, + "@opentelemetry/api@1.9.0": { + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==" + }, + "@opentelemetry/context-async-hooks@1.10.1_@opentelemetry+api@1.4.1": { + "integrity": "sha512-6CC9sWOZDkUkKrAR957fmxXXlaK3uiBu5xVnuNEQ7hI7VqkUC/r0mNYIql0ouRInLz5o0HwmDuga1eXgQU7KNQ==", + "dependencies": [ + "@opentelemetry/api@1.4.1" + ] + }, + "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1": { + "integrity": "sha512-uBZs9poKMWX7WWHsRfaGHqvrn77D9EU5LwU8Ge3YKD/Su5Gy+T1v476l49nl1UOzEMNo4cISao3nIqQVsABB8g==", + "dependencies": [ + "@opentelemetry/api@1.4.1", + "@opentelemetry/semantic-conventions@1.10.1" + ] + }, + "@opentelemetry/core@1.26.0_@opentelemetry+api@1.9.0": { + "integrity": "sha512-1iKxXXE8415Cdv0yjG3G6hQnB5eVEsJce3QaawX8SjDn0mAS0ZM8fAbZZJD4ajvhC15cePvosSCut404KrIIvQ==", + "dependencies": [ + "@opentelemetry/api@1.9.0", + "@opentelemetry/semantic-conventions@1.27.0" + ] + }, + "@opentelemetry/exporter-jaeger@1.10.1_@opentelemetry+api@1.4.1": { + "integrity": "sha512-bZIoSD6M7uxO19HtRJCAceAahX56LUmj5N/XQFHmoi3iFqA2JfR7bqsyHQCYbgINdiee155UejaqkNpgvjV7fw==", + "dependencies": [ + "@opentelemetry/api@1.4.1", + "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/sdk-trace-base@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/semantic-conventions@1.10.1", + "jaeger-client" + ] + }, + "@opentelemetry/exporter-metrics-otlp-grpc@0.36.1_@opentelemetry+api@1.9.0": { + "integrity": "sha512-yQPHny0Y3HIE1BSqbN82MoqqbbJeLINjL7Qf3kJwv1zt5YLUhYbn3FkqHQWS0YWpAvdjK0/OcN40SjEbVz2HRA==", + "dependencies": [ + "@grpc/grpc-js", + "@opentelemetry/api@1.9.0", + "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/exporter-metrics-otlp-http", + "@opentelemetry/otlp-grpc-exporter-base@0.36.1_@opentelemetry+api@1.9.0", + "@opentelemetry/otlp-transformer", + "@opentelemetry/resources@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/sdk-metrics@1.10.1_@opentelemetry+api@1.4.1" + ] + }, + "@opentelemetry/exporter-metrics-otlp-http@0.36.1_@opentelemetry+api@1.9.0": { + "integrity": "sha512-JcpEBwtBpNhVvmCLH3zjTPDcOld2AeI5rNglv2JrB16QCxQ5pwsOgzw7mPe/UR4u/53Ij7LIjFTOCeyVto/6aA==", + "dependencies": [ + "@opentelemetry/api@1.9.0", + "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/otlp-exporter-base@0.36.1_@opentelemetry+api@1.9.0", + "@opentelemetry/otlp-transformer", + "@opentelemetry/resources@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/sdk-metrics@1.10.1_@opentelemetry+api@1.4.1" + ] + }, + "@opentelemetry/exporter-metrics-otlp-proto@0.36.1_@opentelemetry+api@1.9.0": { + "integrity": "sha512-dKJRKvIiyupuZJOVCzW9wNfsK6RxkELnzCSJHzFoIwhGRXSYpbWyYrfHj4ZJZWYZiQSJ7+I8BFUa4aSkBgnO0w==", + "dependencies": [ + "@opentelemetry/api@1.9.0", + "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/exporter-metrics-otlp-http", + "@opentelemetry/otlp-exporter-base@0.36.1_@opentelemetry+api@1.9.0", + "@opentelemetry/otlp-proto-exporter-base@0.36.1_@opentelemetry+api@1.9.0", + "@opentelemetry/otlp-transformer", + "@opentelemetry/resources@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/sdk-metrics@1.10.1_@opentelemetry+api@1.4.1" + ] + }, + "@opentelemetry/exporter-trace-otlp-grpc@0.36.1_@opentelemetry+api@1.4.1": { + "integrity": "sha512-U2HdWvQho2VkeSAcAhkZ2wjfUb/1SKQixo5x6LNBF17ES4QYuh5+BagYxfN5FP4dbLnjZpTtFk5lj+97lfNLEw==", + "dependencies": [ + "@grpc/grpc-js", + "@opentelemetry/api@1.4.1", + "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/otlp-grpc-exporter-base@0.36.1_@opentelemetry+api@1.4.1", + "@opentelemetry/otlp-transformer", + "@opentelemetry/resources@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/sdk-trace-base@1.10.1_@opentelemetry+api@1.4.1" + ] + }, + "@opentelemetry/exporter-trace-otlp-grpc@0.36.1_@opentelemetry+api@1.9.0": { + "integrity": "sha512-U2HdWvQho2VkeSAcAhkZ2wjfUb/1SKQixo5x6LNBF17ES4QYuh5+BagYxfN5FP4dbLnjZpTtFk5lj+97lfNLEw==", + "dependencies": [ + "@grpc/grpc-js", + "@opentelemetry/api@1.9.0", + "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/otlp-grpc-exporter-base@0.36.1_@opentelemetry+api@1.9.0", + "@opentelemetry/otlp-transformer", + "@opentelemetry/resources@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/sdk-trace-base@1.10.1_@opentelemetry+api@1.4.1" + ] + }, + "@opentelemetry/exporter-trace-otlp-http@0.36.1_@opentelemetry+api@1.4.1": { + "integrity": "sha512-q/jKlfuKiHqltDzgzgEvXkoEJ/EyVSIAZhfiaoyBeQ49UhHCPvNTH36/hSwbGSEhKeX98WxXZK4NB/S3sUs8ig==", + "dependencies": [ + "@opentelemetry/api@1.4.1", + "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/otlp-exporter-base@0.36.1_@opentelemetry+api@1.4.1", + "@opentelemetry/otlp-transformer", + "@opentelemetry/resources@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/sdk-trace-base@1.10.1_@opentelemetry+api@1.4.1" + ] + }, + "@opentelemetry/exporter-trace-otlp-proto@0.36.1_@opentelemetry+api@1.4.1": { + "integrity": "sha512-pNfrto7amygyyhmL4Kf96wuepROEecBYXSrtoXIVb1aUhUqjWLsA3/6DR3unB5EfSRA1Oq1Z9bqHfNuKqGfPNw==", + "dependencies": [ + "@opentelemetry/api@1.4.1", + "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/otlp-exporter-base@0.36.1_@opentelemetry+api@1.4.1", + "@opentelemetry/otlp-proto-exporter-base@0.36.1_@opentelemetry+api@1.4.1", + "@opentelemetry/otlp-transformer", + "@opentelemetry/resources@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/sdk-trace-base@1.10.1_@opentelemetry+api@1.4.1" + ] + }, + "@opentelemetry/exporter-trace-otlp-proto@0.36.1_@opentelemetry+api@1.9.0": { + "integrity": "sha512-pNfrto7amygyyhmL4Kf96wuepROEecBYXSrtoXIVb1aUhUqjWLsA3/6DR3unB5EfSRA1Oq1Z9bqHfNuKqGfPNw==", + "dependencies": [ + "@opentelemetry/api@1.9.0", + "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/otlp-exporter-base@0.36.1_@opentelemetry+api@1.9.0", + "@opentelemetry/otlp-proto-exporter-base@0.36.1_@opentelemetry+api@1.9.0", + "@opentelemetry/otlp-transformer", + "@opentelemetry/resources@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/sdk-trace-base@1.10.1_@opentelemetry+api@1.4.1" + ] + }, + "@opentelemetry/exporter-zipkin@1.10.1_@opentelemetry+api@1.4.1": { + "integrity": "sha512-8gF8MjcFf6IhQ7vm6W4tPYtu/vQswYVzpYvk3pUSaX9BMGrwgjeXg+LpuRtaxGoiGd08/g7JjZ4sWLUaELnzWw==", + "dependencies": [ + "@opentelemetry/api@1.4.1", + "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/resources@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/sdk-trace-base@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/semantic-conventions@1.10.1" + ] + }, + "@opentelemetry/instrumentation@0.36.1_@opentelemetry+api@1.4.1": { + "integrity": "sha512-gtYErugMEF5NXVacmuE+tHFBiyB82YIiO5l8iZX9/4R4TDV8uCWdrLW5QZMqgTzPhiyOG9AITFdqhwIZMw/5lA==", + "dependencies": [ + "@opentelemetry/api@1.4.1", + "require-in-the-middle", + "semver@7.6.3", + "shimmer" + ] + }, + "@opentelemetry/otlp-exporter-base@0.36.1_@opentelemetry+api@1.4.1": { + "integrity": "sha512-fpjPwLafJIjgxY5qx7Ly74AYmRCd9spC6/jCxvEgGheg1YT4+NkfVnrfllxLRgc9wQNhDj7Y0Knp8RcmXLLVfA==", + "dependencies": [ + "@opentelemetry/api@1.4.1", + "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1" + ] + }, + "@opentelemetry/otlp-exporter-base@0.36.1_@opentelemetry+api@1.9.0": { + "integrity": "sha512-fpjPwLafJIjgxY5qx7Ly74AYmRCd9spC6/jCxvEgGheg1YT4+NkfVnrfllxLRgc9wQNhDj7Y0Knp8RcmXLLVfA==", + "dependencies": [ + "@opentelemetry/api@1.9.0", + "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1" + ] + }, + "@opentelemetry/otlp-grpc-exporter-base@0.36.1_@opentelemetry+api@1.4.1": { + "integrity": "sha512-71TdQ3Z0D2Trq8rc2UMvky7tmIpg8kVPUhdYH3p0tNsTmbx6GDpEBOpjp2/zCFvQ0SZFVfHH2Oj2OZxZiz+FNQ==", + "dependencies": [ + "@grpc/grpc-js", + "@grpc/proto-loader", + "@opentelemetry/api@1.4.1", + "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/otlp-exporter-base@0.36.1_@opentelemetry+api@1.4.1" + ] + }, + "@opentelemetry/otlp-grpc-exporter-base@0.36.1_@opentelemetry+api@1.9.0": { + "integrity": "sha512-71TdQ3Z0D2Trq8rc2UMvky7tmIpg8kVPUhdYH3p0tNsTmbx6GDpEBOpjp2/zCFvQ0SZFVfHH2Oj2OZxZiz+FNQ==", + "dependencies": [ + "@grpc/grpc-js", + "@grpc/proto-loader", + "@opentelemetry/api@1.9.0", + "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/otlp-exporter-base@0.36.1_@opentelemetry+api@1.9.0" + ] + }, + "@opentelemetry/otlp-proto-exporter-base@0.36.1_@opentelemetry+api@1.4.1": { + "integrity": "sha512-9ErknJ5fS7r2NxEFeca93H+pGWnCjZCUWsz6Stcj5/z2rgsiZGHXLz3fQoUGQz+iXjiXKkks9wxTCRgWOW+Yiw==", + "dependencies": [ + "@opentelemetry/api@1.4.1", + "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/otlp-exporter-base@0.36.1_@opentelemetry+api@1.4.1", + "protobufjs" + ] + }, + "@opentelemetry/otlp-proto-exporter-base@0.36.1_@opentelemetry+api@1.9.0": { + "integrity": "sha512-9ErknJ5fS7r2NxEFeca93H+pGWnCjZCUWsz6Stcj5/z2rgsiZGHXLz3fQoUGQz+iXjiXKkks9wxTCRgWOW+Yiw==", + "dependencies": [ + "@opentelemetry/api@1.9.0", + "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/otlp-exporter-base@0.36.1_@opentelemetry+api@1.9.0", + "protobufjs" + ] + }, + "@opentelemetry/otlp-transformer@0.36.1_@opentelemetry+api@1.4.1": { + "integrity": "sha512-d2MomkVHBHwfsmNz6E60s/sm7gtpSjFwDzkFLm9brVq//VXzEhaEyfYSeTabdUs4BmrzhqTIogHWlcd6cOiL+w==", + "dependencies": [ + "@opentelemetry/api@1.4.1", + "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/resources@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/sdk-metrics@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/sdk-trace-base@1.10.1_@opentelemetry+api@1.4.1" + ] + }, + "@opentelemetry/propagator-b3@1.10.1_@opentelemetry+api@1.4.1": { + "integrity": "sha512-YrWqU93PH8RyCmqGhtDZgyk64D+cp8XIjQsLhEgOPcOsxvxSSGXnGt46rx9Z8+WdIbJgj13Q4nV/xuh36k+O+A==", + "dependencies": [ + "@opentelemetry/api@1.4.1", + "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1" + ] + }, + "@opentelemetry/propagator-jaeger@1.10.1_@opentelemetry+api@1.4.1": { + "integrity": "sha512-qvwFfDPoBw2YQW/OsGHdLdD/rqNRGBRLz5UZR/akO21C4qwIK+lQcXbSi5ve0p2eLHnFshhNFqDmgQclOYBcmg==", + "dependencies": [ + "@opentelemetry/api@1.4.1", + "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1" + ] + }, + "@opentelemetry/resources@1.10.1_@opentelemetry+api@1.4.1": { + "integrity": "sha512-e+wwdyO44jZtsT1aqGiWMFOfN1XuP9Tv4+H0OYP3yQajBtGdsZjdSUn9UNjw46JsW0Mb+RaTxJwsb2uvfHar0g==", + "dependencies": [ + "@opentelemetry/api@1.4.1", + "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/semantic-conventions@1.10.1" + ] + }, + "@opentelemetry/resources@1.26.0_@opentelemetry+api@1.9.0": { + "integrity": "sha512-CPNYchBE7MBecCSVy0HKpUISEeJOniWqcHaAHpmasZ3j9o6V3AyBzhRc90jdmemq0HOxDr6ylhUbDhBqqPpeNw==", + "dependencies": [ + "@opentelemetry/api@1.9.0", + "@opentelemetry/core@1.26.0_@opentelemetry+api@1.9.0", + "@opentelemetry/semantic-conventions@1.27.0" + ] + }, + "@opentelemetry/sdk-metrics@1.10.1_@opentelemetry+api@1.4.1": { + "integrity": "sha512-ARAD4e6lZhLwstwW+1HG2Q3XuYFA/t8vn10KK/mA4em1pZYKFn64c45RJZJcntxWp4wOZRbp9iL1RXsg7zIjow==", + "dependencies": [ + "@opentelemetry/api@1.4.1", + "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/resources@1.10.1_@opentelemetry+api@1.4.1", + "lodash.merge" + ] + }, + "@opentelemetry/sdk-metrics@1.26.0_@opentelemetry+api@1.9.0": { + "integrity": "sha512-0SvDXmou/JjzSDOjUmetAAvcKQW6ZrvosU0rkbDGpXvvZN+pQF6JbK/Kd4hNdK4q/22yeruqvukXEJyySTzyTQ==", + "dependencies": [ + "@opentelemetry/api@1.9.0", + "@opentelemetry/core@1.26.0_@opentelemetry+api@1.9.0", + "@opentelemetry/resources@1.26.0_@opentelemetry+api@1.9.0" + ] + }, + "@opentelemetry/sdk-node@0.36.1_@opentelemetry+api@1.4.1": { + "integrity": "sha512-7cRIxls3Ccg6HmzSu30R5upi0yHEizab2rm2rATrAyFV3JJ/ISA7cojmwKwYG8p4rkPNNPLOwCxI3vlLJrBnKA==", + "dependencies": [ + "@opentelemetry/api@1.4.1", + "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/exporter-jaeger", + "@opentelemetry/exporter-trace-otlp-grpc@0.36.1_@opentelemetry+api@1.4.1", + "@opentelemetry/exporter-trace-otlp-http", + "@opentelemetry/exporter-trace-otlp-proto@0.36.1_@opentelemetry+api@1.4.1", + "@opentelemetry/exporter-zipkin", + "@opentelemetry/instrumentation", + "@opentelemetry/resources@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/sdk-metrics@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/sdk-trace-base@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/sdk-trace-node", + "@opentelemetry/semantic-conventions@1.10.1" + ] + }, + "@opentelemetry/sdk-trace-base@1.10.1_@opentelemetry+api@1.4.1": { + "integrity": "sha512-jutSP5t22wrPKReJKzI5uKht4mJ4cQdF/mGFJkN+emFFsDXru9CuFv/NfUrD0jEqoaaiqjcZtPSyTzMgu9LXvw==", + "dependencies": [ + "@opentelemetry/api@1.4.1", + "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/resources@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/semantic-conventions@1.10.1" + ] + }, + "@opentelemetry/sdk-trace-base@1.26.0_@opentelemetry+api@1.9.0": { + "integrity": "sha512-olWQldtvbK4v22ymrKLbIcBi9L2SpMO84sCPY54IVsJhP9fRsxJT194C/AVaAuJzLE30EdhhM1VmvVYR7az+cw==", + "dependencies": [ + "@opentelemetry/api@1.9.0", + "@opentelemetry/core@1.26.0_@opentelemetry+api@1.9.0", + "@opentelemetry/resources@1.26.0_@opentelemetry+api@1.9.0", + "@opentelemetry/semantic-conventions@1.27.0" + ] + }, + "@opentelemetry/sdk-trace-node@1.10.1_@opentelemetry+api@1.4.1": { + "integrity": "sha512-/y+s1j8rPTaKnPnbrsbYv3ygTb4hjx/1H32zqobFr85cvWX+Tt1RWmcZ51TaPAfq5uJobGFhhLh6ADI2RDvk5Q==", + "dependencies": [ + "@opentelemetry/api@1.4.1", + "@opentelemetry/context-async-hooks", + "@opentelemetry/core@1.10.1_@opentelemetry+api@1.4.1", + "@opentelemetry/propagator-b3", + "@opentelemetry/propagator-jaeger", + "@opentelemetry/sdk-trace-base@1.10.1_@opentelemetry+api@1.4.1", + "semver@7.6.3" + ] + }, + "@opentelemetry/semantic-conventions@1.10.1": { + "integrity": "sha512-qiAueuCoN+1YEuHNXnsct9bkbroZBPd7QwQgd56YURG0LBRVHwE/lF6FOprfUvp1n1tu0O6+E3s6x+dmUndXFQ==" + }, + "@opentelemetry/semantic-conventions@1.27.0": { + "integrity": "sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg==" + }, + "@pkgjs/parseargs@0.11.0": { + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==" + }, + "@pkgr/core@0.1.1": { + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==" + }, + "@pnpm/config.env-replace@1.1.0": { + "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==" + }, + "@pnpm/network.ca-file@1.0.2": { + "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", + "dependencies": [ + "graceful-fs@4.2.10" + ] + }, + "@pnpm/npm-conf@2.3.1": { + "integrity": "sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==", + "dependencies": [ + "@pnpm/config.env-replace", + "@pnpm/network.ca-file", + "config-chain" + ] + }, + "@protobufjs/aspromise@1.1.2": { + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "@protobufjs/base64@1.1.2": { + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "@protobufjs/codegen@2.0.4": { + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "@protobufjs/eventemitter@1.1.0": { + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "@protobufjs/fetch@1.1.0": { + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": [ + "@protobufjs/aspromise", + "@protobufjs/inquire" + ] + }, + "@protobufjs/float@1.0.2": { + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "@protobufjs/inquire@1.1.0": { + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "@protobufjs/path@1.1.2": { + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "@protobufjs/pool@1.1.0": { + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "@protobufjs/utf8@1.1.0": { + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "@rollup/pluginutils@4.2.1": { + "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==", + "dependencies": [ + "estree-walker@2.0.2", + "picomatch" + ] + }, + "@rollup/rollup-android-arm-eabi@4.24.0": { + "integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==" + }, + "@rollup/rollup-android-arm64@4.24.0": { + "integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==" + }, + "@rollup/rollup-darwin-arm64@4.24.0": { + "integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==" + }, + "@rollup/rollup-darwin-x64@4.24.0": { + "integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==" + }, + "@rollup/rollup-linux-arm-gnueabihf@4.24.0": { + "integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==" + }, + "@rollup/rollup-linux-arm-musleabihf@4.24.0": { + "integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==" + }, + "@rollup/rollup-linux-arm64-gnu@4.24.0": { + "integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==" + }, + "@rollup/rollup-linux-arm64-musl@4.24.0": { + "integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==" + }, + "@rollup/rollup-linux-powerpc64le-gnu@4.24.0": { + "integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==" + }, + "@rollup/rollup-linux-riscv64-gnu@4.24.0": { + "integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==" + }, + "@rollup/rollup-linux-s390x-gnu@4.24.0": { + "integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==" + }, + "@rollup/rollup-linux-x64-gnu@4.24.0": { + "integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==" + }, + "@rollup/rollup-linux-x64-musl@4.24.0": { + "integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==" + }, + "@rollup/rollup-win32-arm64-msvc@4.24.0": { + "integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==" + }, + "@rollup/rollup-win32-ia32-msvc@4.24.0": { + "integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==" + }, + "@rollup/rollup-win32-x64-msvc@4.24.0": { + "integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==" + }, + "@rtsao/scc@1.1.0": { + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==" + }, + "@samverschueren/stream-to-observable@0.3.1": { + "integrity": "sha512-c/qwwcHyafOQuVQJj0IlBjf5yYgBI7YPJ77k4fOJYesb41jio65eaJODRUmfYKhTOFBrIZ66kgvGPlNbjuoRdQ==", + "dependencies": [ + "any-observable" + ] + }, + "@sindresorhus/is@5.6.0": { + "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==" + }, + "@sindresorhus/slugify@2.2.1": { + "integrity": "sha512-MkngSCRZ8JdSOCHRaYd+D01XhvU3Hjy6MGl06zhOk614hp9EOAp5gIkBeQg7wtmxpitU6eAL4kdiRMcJa2dlrw==", + "dependencies": [ + "@sindresorhus/transliterate", + "escape-string-regexp@5.0.0" + ] + }, + "@sindresorhus/transliterate@1.6.0": { + "integrity": "sha512-doH1gimEu3A46VX6aVxpHTeHrytJAG6HgdxntYnCFiIFHEM/ZGpG8KiZGBChchjQmG0XFIBL552kBTjVcMZXwQ==", + "dependencies": [ + "escape-string-regexp@5.0.0" + ] + }, + "@szmarczak/http-timer@5.0.1": { + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", + "dependencies": [ + "defer-to-connect" + ] + }, + "@tokenizer/token@0.3.0": { + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" + }, + "@tsconfig/node10@1.0.11": { + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==" + }, + "@tsconfig/node12@1.0.11": { + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==" + }, + "@tsconfig/node14@1.0.3": { + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" + }, + "@tsconfig/node16@1.0.4": { + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==" + }, + "@types/debug@4.1.12": { + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dependencies": [ + "@types/ms" + ] + }, + "@types/estree@1.0.6": { + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" + }, + "@types/http-cache-semantics@4.0.4": { + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==" + }, + "@types/http-proxy@1.17.15": { + "integrity": "sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ==", + "dependencies": [ + "@types/node@22.5.4" + ] + }, + "@types/istanbul-lib-coverage@2.0.6": { + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==" + }, + "@types/istanbul-lib-report@3.0.3": { + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dependencies": [ + "@types/istanbul-lib-coverage" + ] + }, + "@types/istanbul-reports@3.0.4": { + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dependencies": [ + "@types/istanbul-lib-report" + ] + }, + "@types/json5@0.0.29": { + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" + }, + "@types/mdast@4.0.4": { + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "dependencies": [ + "@types/unist" + ] + }, + "@types/ms@0.7.34": { + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" + }, + "@types/node@20.16.10": { + "integrity": "sha512-vQUKgWTjEIRFCvK6CyriPH3MZYiYlNy0fKiEYHWbcoWLEgs4opurGGKlebrTLqdSMIbXImH6XExNiIyNUv3WpA==", + "dependencies": [ + "undici-types" + ] + }, + "@types/node@22.5.4": { + "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==", + "dependencies": [ + "undici-types" + ] + }, + "@types/normalize-package-data@2.4.4": { + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==" + }, + "@types/retry@0.12.1": { + "integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==" + }, + "@types/triple-beam@1.3.5": { + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" + }, + "@types/unist@3.0.3": { + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, + "@types/yargs-parser@21.0.3": { + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==" + }, + "@types/yargs@16.0.9": { + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", + "dependencies": [ + "@types/yargs-parser" + ] + }, + "@types/yauzl@2.10.3": { + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dependencies": [ + "@types/node@22.5.4" + ] + }, + "@typescript-eslint/eslint-plugin@7.18.0_@typescript-eslint+parser@7.18.0__eslint@8.57.1__typescript@5.6.2_eslint@8.57.1_typescript@5.6.2": { + "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", + "dependencies": [ + "@eslint-community/regexpp", + "@typescript-eslint/parser", + "@typescript-eslint/scope-manager", + "@typescript-eslint/type-utils", + "@typescript-eslint/utils", + "@typescript-eslint/visitor-keys@7.18.0", + "eslint", + "graphemer", + "ignore", + "natural-compare", + "ts-api-utils" + ] + }, + "@typescript-eslint/parser@7.18.0_eslint@8.57.1_typescript@5.6.2": { + "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", + "dependencies": [ + "@typescript-eslint/scope-manager", + "@typescript-eslint/types@7.18.0", + "@typescript-eslint/typescript-estree@7.18.0_typescript@5.6.2", + "@typescript-eslint/visitor-keys@7.18.0", + "debug@4.3.7", + "eslint" + ] + }, + "@typescript-eslint/scope-manager@7.18.0": { + "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", + "dependencies": [ + "@typescript-eslint/types@7.18.0", + "@typescript-eslint/visitor-keys@7.18.0" + ] + }, + "@typescript-eslint/type-utils@7.18.0_eslint@8.57.1_typescript@5.6.2": { + "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", + "dependencies": [ + "@typescript-eslint/typescript-estree@7.18.0_typescript@5.6.2", + "@typescript-eslint/utils", + "debug@4.3.7", + "eslint", + "ts-api-utils" + ] + }, + "@typescript-eslint/types@5.62.0": { + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==" + }, + "@typescript-eslint/types@7.18.0": { + "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==" + }, + "@typescript-eslint/typescript-estree@5.62.0_typescript@5.6.2": { + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "dependencies": [ + "@typescript-eslint/types@5.62.0", + "@typescript-eslint/visitor-keys@5.62.0", + "debug@4.3.7", + "globby@11.1.0", + "is-glob", + "semver@7.6.3", + "tsutils" + ] + }, + "@typescript-eslint/typescript-estree@7.18.0_typescript@5.6.2": { + "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", + "dependencies": [ + "@typescript-eslint/types@7.18.0", + "@typescript-eslint/visitor-keys@7.18.0", + "debug@4.3.7", + "globby@11.1.0", + "is-glob", + "minimatch@9.0.5", + "semver@7.6.3", + "ts-api-utils" + ] + }, + "@typescript-eslint/utils@7.18.0_eslint@8.57.1_typescript@5.6.2": { + "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", + "dependencies": [ + "@eslint-community/eslint-utils", + "@typescript-eslint/scope-manager", + "@typescript-eslint/types@7.18.0", + "@typescript-eslint/typescript-estree@7.18.0_typescript@5.6.2", + "eslint" + ] + }, + "@typescript-eslint/visitor-keys@5.62.0": { + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "dependencies": [ + "@typescript-eslint/types@5.62.0", + "eslint-visitor-keys" + ] + }, + "@typescript-eslint/visitor-keys@7.18.0": { + "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", + "dependencies": [ + "@typescript-eslint/types@7.18.0", + "eslint-visitor-keys" + ] + }, + "@ungap/structured-clone@1.2.0": { + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" + }, + "@vercel/nft@0.23.1": { + "integrity": "sha512-NE0xSmGWVhgHF1OIoir71XAd0W0C1UE3nzFyhpFiMr3rVhetww7NvM1kc41trBsPG37Bh+dE5FYCTMzM/gBu0w==", + "dependencies": [ + "@mapbox/node-pre-gyp", + "@rollup/pluginutils", + "acorn", + "async-sema", + "bindings", + "estree-walker@2.0.2", + "glob@7.2.3", + "graceful-fs@4.2.11", + "micromatch@4.0.8", + "node-gyp-build", + "resolve-from@5.0.0" + ] + }, + "@vercel/nft@0.27.4_acorn@8.12.1": { + "integrity": "sha512-Rioz3LJkEKicKCi9BSyc1RXZ5R6GmXosFMeBSThh6msWSOiArKhb7c75MiWwZEgPL7x0/l3TAfH/l0cxKNuUFA==", + "dependencies": [ + "@mapbox/node-pre-gyp", + "@rollup/pluginutils", + "acorn", + "acorn-import-attributes", + "async-sema", + "bindings", + "estree-walker@2.0.2", + "glob@7.2.3", + "graceful-fs@4.2.11", + "micromatch@4.0.8", + "node-gyp-build", + "resolve-from@5.0.0" + ] + }, + "@vitest/expect@2.1.1": { + "integrity": "sha512-YeueunS0HiHiQxk+KEOnq/QMzlUuOzbU1Go+PgAsHvvv3tUkJPm9xWt+6ITNTlzsMXUjmgm5T+U7KBPK2qQV6w==", + "dependencies": [ + "@vitest/spy", + "@vitest/utils", + "chai", + "tinyrainbow" + ] + }, + "@vitest/mocker@2.1.1_@vitest+spy@2.1.1_vite@5.4.8__@types+node@20.16.10_@types+node@20.16.10": { + "integrity": "sha512-LNN5VwOEdJqCmJ/2XJBywB11DLlkbY0ooDJW3uRX5cZyYCrc4PI/ePX0iQhE3BiEGiQmK4GE7Q/PqCkkaiPnrA==", + "dependencies": [ + "@vitest/spy", + "estree-walker@3.0.3", + "magic-string", + "vite" + ] + }, + "@vitest/pretty-format@2.1.1": { + "integrity": "sha512-SjxPFOtuINDUW8/UkElJYQSFtnWX7tMksSGW0vfjxMneFqxVr8YJ979QpMbDW7g+BIiq88RAGDjf7en6rvLPPQ==", + "dependencies": [ + "tinyrainbow" + ] + }, + "@vitest/pretty-format@2.1.2": { + "integrity": "sha512-FIoglbHrSUlOJPDGIrh2bjX1sNars5HbxlcsFKCtKzu4+5lpsRhOCVcuzp0fEhAGHkPZRIXVNzPcpSlkoZ3LuA==", + "dependencies": [ + "tinyrainbow" + ] + }, + "@vitest/runner@2.1.1": { + "integrity": "sha512-uTPuY6PWOYitIkLPidaY5L3t0JJITdGTSwBtwMjKzo5O6RCOEncz9PUN+0pDidX8kTHYjO0EwUIvhlGpnGpxmA==", + "dependencies": [ + "@vitest/utils", + "pathe" + ] + }, + "@vitest/snapshot@2.1.1": { + "integrity": "sha512-BnSku1WFy7r4mm96ha2FzN99AZJgpZOWrAhtQfoxjUU5YMRpq1zmHRq7a5K9/NjqonebO7iVDla+VvZS8BOWMw==", + "dependencies": [ + "@vitest/pretty-format@2.1.1", + "magic-string", + "pathe" + ] + }, + "@vitest/spy@2.1.1": { + "integrity": "sha512-ZM39BnZ9t/xZ/nF4UwRH5il0Sw93QnZXd9NAZGRpIgj0yvVwPpLd702s/Cx955rGaMlyBQkZJ2Ir7qyY48VZ+g==", + "dependencies": [ + "tinyspy" + ] + }, + "@vitest/utils@2.1.1": { + "integrity": "sha512-Y6Q9TsI+qJ2CC0ZKj6VBb+T8UPz593N113nnUykqwANqhgf3QkZeHFlusgKLTqrnVHbj/XDKZcDHol+dxVT+rQ==", + "dependencies": [ + "@vitest/pretty-format@2.1.1", + "loupe", + "tinyrainbow" + ] + }, + "@xhmikosr/archive-type@6.0.1": { + "integrity": "sha512-PB3NeJL8xARZt52yDBupK0dNPn8uIVQDe15qNehUpoeeLWCZyAOam4vGXnoZGz2N9D1VXtjievJuCsXam2TmbQ==", + "dependencies": [ + "file-type" + ] + }, + "@xhmikosr/decompress-tar@7.0.0": { + "integrity": "sha512-kyWf2hybtQVbWtB+FdRyOT+jyR5jxCNZPLqvQGB7djZj75lrpLUPEmRbyo86AtJ5OEtivpYaNWjCkqSJ8xtRWw==", + "dependencies": [ + "file-type", + "is-stream@3.0.0", + "tar-stream@3.1.7" + ] + }, + "@xhmikosr/decompress-tarbz2@7.0.0": { + "integrity": "sha512-3QnjipYkRgh3Dee1MWDgKmANWxOQBVN4e1IwiGNe2fHYfMYTeSkVvWREt87UIoSucKUh3E95v8uGFttgTknZcA==", + "dependencies": [ + "@xhmikosr/decompress-tar", + "file-type", + "is-stream@3.0.0", + "seek-bzip", + "unbzip2-stream" + ] + }, + "@xhmikosr/decompress-targz@7.0.0": { + "integrity": "sha512-7BNHJl92g9OLhw89zqcFS67V1LAtm4Ex02j6OiQzuE8P7Yy9lQcyBuEL3x6v436grLdL+BcFjgbmhWxnem4GHw==", + "dependencies": [ + "@xhmikosr/decompress-tar", + "file-type", + "is-stream@3.0.0" + ] + }, + "@xhmikosr/decompress-unzip@6.0.0": { + "integrity": "sha512-R1HAkjXLS7RAL74YFLxYY9zYflCcYGssld9KKFDu87PnJ4h4btdhzXfSC8J5i5A2njH3oYIoCzx03RIGTH07Sg==", + "dependencies": [ + "file-type", + "get-stream@6.0.1", + "yauzl" + ] + }, + "@xhmikosr/decompress@9.0.1": { + "integrity": "sha512-9Lvlt6Qdpo9SaRQyRIXCo3lgU++eMZ68lzgjcTwtuKDrlwT635+5zsHZ1yrSx/Blc5IDuVLlPkBPj5CZkx+2+Q==", + "dependencies": [ + "@xhmikosr/decompress-tar", + "@xhmikosr/decompress-tarbz2", + "@xhmikosr/decompress-targz", + "@xhmikosr/decompress-unzip", + "graceful-fs@4.2.11", + "make-dir@4.0.0", + "strip-dirs" + ] + }, + "@xhmikosr/downloader@13.0.1": { + "integrity": "sha512-mBvWew1kZJHfNQVVfVllMjUDwCGN9apPa0t4/z1zaUJ9MzpXjRL3w8fsfJKB8gHN/h4rik9HneKfDbh2fErN+w==", + "dependencies": [ + "@xhmikosr/archive-type", + "@xhmikosr/decompress", + "content-disposition", + "ext-name", + "file-type", + "filenamify", + "get-stream@6.0.1", + "got", + "merge-options", + "p-event@5.0.1" + ] + }, + "abbrev@1.1.1": { + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "abort-controller@3.0.0": { + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": [ + "event-target-shim" + ] + }, + "abstract-logging@2.0.1": { + "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==" + }, + "accepts@1.3.8": { + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": [ + "mime-types", + "negotiator" + ] + }, + "acorn-import-attributes@1.9.5_acorn@8.12.1": { + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "dependencies": [ + "acorn" + ] + }, + "acorn-jsx@5.3.2_acorn@8.12.1": { + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dependencies": [ + "acorn" + ] + }, + "acorn-walk@8.3.4": { + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dependencies": [ + "acorn" + ] + }, + "acorn@8.12.1": { + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==" + }, + "agent-base@6.0.2": { + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": [ + "debug@4.3.7" + ] + }, + "aggregate-error@4.0.1": { + "integrity": "sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==", + "dependencies": [ + "clean-stack", + "indent-string@5.0.0" + ] + }, + "ajv-errors@3.0.0_ajv@8.17.1": { + "integrity": "sha512-V3wD15YHfHz6y0KdhYFjyy9vWtEVALT9UrxfN3zqlI6dMioHnJrqOYfyPKol3oqrnCM9uwkcdCwkJ0WUcbLMTQ==", + "dependencies": [ + "ajv@8.17.1" + ] + }, + "ajv-formats@2.1.1_ajv@8.17.1": { + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dependencies": [ + "ajv@8.17.1" + ] + }, + "ajv-formats@3.0.1_ajv@8.17.1": { + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dependencies": [ + "ajv@8.17.1" + ] + }, + "ajv@6.12.6": { + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": [ + "fast-deep-equal", + "fast-json-stable-stringify", + "json-schema-traverse@0.4.1", + "uri-js" + ] + }, + "ajv@8.17.1": { + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dependencies": [ + "fast-deep-equal", + "fast-uri@3.0.2", + "json-schema-traverse@1.0.0", + "require-from-string" + ] + }, + "all-node-versions@11.3.0": { + "integrity": "sha512-psMkc5s3qpr+QMfires9bC4azRYciPWql1wqZKMsYRh1731qefQDH2X4+O19xSBX6u0Ra/8Y5diG6y/fEmqKsw==", + "dependencies": [ + "fetch-node-website", + "filter-obj@5.1.0", + "get-stream@6.0.1", + "global-cache-dir", + "is-plain-obj@4.1.0", + "path-exists@5.0.0", + "semver@7.6.3", + "write-file-atomic@4.0.2" + ] + }, + "ansi-align@3.0.1": { + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "dependencies": [ + "string-width@4.2.3" + ] + }, + "ansi-color@0.2.1": { + "integrity": "sha512-bF6xLaZBLpOQzgYUtYEhJx090nPSZk1BQ/q2oyBK9aMMcJHzx9uXGCjI2Y+LebsN4Jwoykr0V9whbPiogdyHoQ==" + }, + "ansi-escapes@3.2.0": { + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==" + }, + "ansi-escapes@4.3.2": { + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dependencies": [ + "type-fest@0.21.3" + ] + }, + "ansi-escapes@5.0.0": { + "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==", + "dependencies": [ + "type-fest@1.4.0" + ] + }, + "ansi-escapes@6.2.0": { + "integrity": "sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==", + "dependencies": [ + "type-fest@3.13.1" + ] + }, + "ansi-regex@2.1.1": { + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==" + }, + "ansi-regex@3.0.1": { + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==" + }, + "ansi-regex@4.1.1": { + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==" + }, + "ansi-regex@5.0.1": { + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "ansi-regex@6.1.0": { + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==" + }, + "ansi-sequence-parser@1.1.1": { + "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==" + }, + "ansi-styles@2.2.1": { + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==" + }, + "ansi-styles@3.2.1": { + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": [ + "color-convert@1.9.3" + ] + }, + "ansi-styles@4.3.0": { + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": [ + "color-convert@2.0.1" + ] + }, + "ansi-styles@5.2.0": { + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + }, + "ansi-styles@6.2.1": { + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==" + }, + "ansi-to-html@0.7.2": { + "integrity": "sha512-v6MqmEpNlxF+POuyhKkidusCHWWkaLcGRURzivcU3I9tv7k4JVhFcnukrM5Rlk2rUywdZuzYAZ+kbZqWCnfN3g==", + "dependencies": [ + "entities" + ] + }, + "any-observable@0.3.0": { + "integrity": "sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==" + }, + "anymatch@3.1.3": { + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": [ + "normalize-path@3.0.0", + "picomatch" + ] + }, + "aproba@2.0.0": { + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, + "archiver-utils@2.1.0": { + "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", + "dependencies": [ + "glob@7.2.3", + "graceful-fs@4.2.11", + "lazystream", + "lodash.defaults", + "lodash.difference", + "lodash.flatten", + "lodash.isplainobject", + "lodash.union", + "normalize-path@3.0.0", + "readable-stream@2.3.8" + ] + }, + "archiver-utils@3.0.4": { + "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", + "dependencies": [ + "glob@7.2.3", + "graceful-fs@4.2.11", + "lazystream", + "lodash.defaults", + "lodash.difference", + "lodash.flatten", + "lodash.isplainobject", + "lodash.union", + "normalize-path@3.0.0", + "readable-stream@3.6.2" + ] + }, + "archiver-utils@5.0.2": { + "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==", + "dependencies": [ + "glob@10.4.5", + "graceful-fs@4.2.11", + "is-stream@2.0.1", + "lazystream", + "lodash", + "normalize-path@3.0.0", + "readable-stream@4.5.2" + ] + }, + "archiver@5.3.2": { + "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", + "dependencies": [ + "archiver-utils@2.1.0", + "async@3.2.6", + "buffer-crc32@0.2.13", + "readable-stream@3.6.2", + "readdir-glob", + "tar-stream@2.2.0", + "zip-stream@4.1.1" + ] + }, + "archiver@7.0.1": { + "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==", + "dependencies": [ + "archiver-utils@5.0.2", + "async@3.2.6", + "buffer-crc32@1.0.0", + "readable-stream@4.5.2", + "readdir-glob", + "tar-stream@3.1.7", + "zip-stream@6.0.1" + ] + }, + "are-we-there-yet@2.0.0": { + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "dependencies": [ + "delegates", + "readable-stream@3.6.2" + ] + }, + "arg@4.1.3": { + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, + "argparse@2.0.1": { + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "aria-query@5.1.3": { + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "dependencies": [ + "deep-equal" + ] + }, + "arr-diff@4.0.0": { + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==" + }, + "arr-flatten@1.1.0": { + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" + }, + "arr-union@3.1.0": { + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==" + }, + "array-buffer-byte-length@1.0.1": { + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "dependencies": [ + "call-bind", + "is-array-buffer" + ] + }, + "array-flatten@1.1.1": { + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "array-includes@3.1.8": { + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "dependencies": [ + "call-bind", + "define-properties", + "es-abstract", + "es-object-atoms", + "get-intrinsic", + "is-string" + ] + }, + "array-timsort@1.0.3": { + "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==" + }, + "array-union@2.1.0": { + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" + }, + "array-unique@0.3.2": { + "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==" + }, + "array.prototype.findlast@1.2.5": { + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dependencies": [ + "call-bind", + "define-properties", + "es-abstract", + "es-errors", + "es-object-atoms", + "es-shim-unscopables" + ] + }, + "array.prototype.findlastindex@1.2.5": { + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "dependencies": [ + "call-bind", + "define-properties", + "es-abstract", + "es-errors", + "es-object-atoms", + "es-shim-unscopables" + ] + }, + "array.prototype.flat@1.3.2": { + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "dependencies": [ + "call-bind", + "define-properties", + "es-abstract", + "es-shim-unscopables" + ] + }, + "array.prototype.flatmap@1.3.2": { + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "dependencies": [ + "call-bind", + "define-properties", + "es-abstract", + "es-shim-unscopables" + ] + }, + "array.prototype.tosorted@1.1.4": { + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dependencies": [ + "call-bind", + "define-properties", + "es-abstract", + "es-errors", + "es-shim-unscopables" + ] + }, + "arraybuffer.prototype.slice@1.0.3": { + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "dependencies": [ + "array-buffer-byte-length", + "call-bind", + "define-properties", + "es-abstract", + "es-errors", + "get-intrinsic", + "is-array-buffer", + "is-shared-array-buffer" + ] + }, + "arrify@3.0.0": { + "integrity": "sha512-tLkvA81vQG/XqE2mjDkGQHoOINtMHtysSnemrmoGe6PydDPMRbVugqyk4A6V/WDWEfm3l+0d8anA9r8cv/5Jaw==" + }, + "ascii-table@0.0.9": { + "integrity": "sha512-xpkr6sCDIYTPqzvjG8M3ncw1YOTaloWZOyrUmicoEifBEKzQzt+ooUpRpQ/AbOoJfO/p2ZKiyp79qHThzJDulQ==" + }, + "assertion-error@2.0.1": { + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==" + }, + "assign-symbols@1.0.0": { + "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==" + }, + "ast-module-types@5.0.0": { + "integrity": "sha512-JvqziE0Wc0rXQfma0HZC/aY7URXHFuZV84fJRtP8u+lhp0JYCNd5wJzVXP45t0PH0Mej3ynlzvdyITYIu0G4LQ==" + }, + "ast-types-flow@0.0.8": { + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==" + }, + "async-sema@3.1.1": { + "integrity": "sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==" + }, + "async@1.5.2": { + "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==" + }, + "async@3.2.6": { + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==" + }, + "asynckit@0.4.0": { + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "atob@2.1.2": { + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" + }, + "atomic-sleep@1.0.0": { + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==" + }, + "available-typed-arrays@1.0.7": { + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dependencies": [ + "possible-typed-array-names" + ] + }, + "avvio@8.4.0": { + "integrity": "sha512-CDSwaxINFy59iNwhYnkvALBwZiTydGkOecZyPkqBpABYR1KqGEsET0VOOYDwtleZSUIdeY36DC2bSZ24CO1igA==", + "dependencies": [ + "@fastify/error", + "fastq" + ] + }, + "axe-core@4.10.0": { + "integrity": "sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==" + }, + "axios@1.7.7": { + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "dependencies": [ + "follow-redirects", + "form-data", + "proxy-from-env" + ] + }, + "axobject-query@4.1.0": { + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==" + }, + "b4a@1.6.7": { + "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==" + }, + "backoff@2.5.0": { + "integrity": "sha512-wC5ihrnUXmR2douXmXLCe5O3zg3GKIyvRi/hi58a/XyRxVI+3/yM0PYueQOZXPXQ9pxBislYkw+sF9b7C/RuMA==", + "dependencies": [ + "precond" + ] + }, + "balanced-match@1.0.2": { + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "bare-events@2.5.0": { + "integrity": "sha512-/E8dDe9dsbLyh2qrZ64PEPadOQ0F4gbl1sUJOrmph7xOiIxfY8vwab/4bFLh4Y88/Hk/ujKcrQKc+ps0mv873A==" + }, + "base64-js@1.5.1": { + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "base@0.11.2": { + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dependencies": [ + "cache-base", + "class-utils", + "component-emitter", + "define-property@1.0.0", + "isobject@3.0.1", + "mixin-deep", + "pascalcase" + ] + }, + "before-after-hook@2.2.3": { + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==" + }, + "better-ajv-errors@1.2.0_ajv@8.17.1": { + "integrity": "sha512-UW+IsFycygIo7bclP9h5ugkNH8EjCSgqyFB/yQ4Hqqa1OEYDtb0uFIkYE0b6+CjkgJYVM5UKI/pJPxjYe9EZlA==", + "dependencies": [ + "@babel/code-frame", + "@humanwhocodes/momoa", + "ajv@8.17.1", + "chalk@4.1.2", + "jsonpointer", + "leven" + ] + }, + "better-opn@3.0.2": { + "integrity": "sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==", + "dependencies": [ + "open" + ] + }, + "binary-extensions@2.3.0": { + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==" + }, + "binary-searching@2.0.5": { + "integrity": "sha512-v4N2l3RxL+m4zDxyxz3Ne2aTmiPn8ZUpKFpdPtO+ItW1NcTCXA7JeHG5GMBSvoKSkQZ9ycS+EouDVxYB9ufKWA==" + }, + "bindings@1.5.0": { + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dependencies": [ + "file-uri-to-path" + ] + }, + "bl@4.1.0": { + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dependencies": [ + "buffer@5.7.1", + "inherits", + "readable-stream@3.6.2" + ] + }, + "bl@5.1.0": { + "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", + "dependencies": [ + "buffer@6.0.3", + "inherits", + "readable-stream@3.6.2" + ] + }, + "blueimp-md5@2.19.0": { + "integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==" + }, + "body-parser@1.20.1": { + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dependencies": [ + "bytes", + "content-type", + "debug@2.6.9", + "depd@2.0.0", + "destroy", + "http-errors@2.0.0", + "iconv-lite", + "on-finished", + "qs", + "raw-body@2.5.1", + "type-is", + "unpipe" + ] + }, + "boxen@7.1.1": { + "integrity": "sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==", + "dependencies": [ + "ansi-align", + "camelcase@7.0.1", + "chalk@5.2.0", + "cli-boxes", + "string-width@5.1.2", + "type-fest@2.19.0", + "widest-line", + "wrap-ansi@8.1.0" + ] + }, + "brace-expansion@1.1.11": { + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": [ + "balanced-match", + "concat-map" + ] + }, + "brace-expansion@2.0.1": { + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": [ + "balanced-match" + ] + }, + "braces@2.3.2": { + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dependencies": [ + "arr-flatten", + "array-unique", + "extend-shallow@2.0.1", + "fill-range@4.0.0", + "isobject@3.0.1", + "repeat-element", + "snapdragon", + "snapdragon-node", + "split-string", + "to-regex" + ] + }, + "braces@3.0.3": { + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dependencies": [ + "fill-range@7.1.1" + ] + }, + "buffer-crc32@0.2.13": { + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==" + }, + "buffer-crc32@1.0.0": { + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==" + }, + "buffer-equal-constant-time@1.0.1": { + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "buffer-from@1.1.2": { + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "buffer@5.7.1": { + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dependencies": [ + "base64-js", + "ieee754" + ] + }, + "buffer@6.0.3": { + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dependencies": [ + "base64-js", + "ieee754" + ] + }, + "bufrw@1.4.0": { + "integrity": "sha512-sWm8iPbqvL9+5SiYxXH73UOkyEbGQg7kyHQmReF89WJHQJw2eV4P/yZ0E+b71cczJ4pPobVhXxgQcmfSTgGHxQ==", + "dependencies": [ + "ansi-color", + "error", + "hexer", + "xtend" + ] + }, + "builtin-modules@3.3.0": { + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==" + }, + "builtins@5.1.0": { + "integrity": "sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==", + "dependencies": [ + "semver@7.6.3" + ] + }, + "byline@5.0.0": { + "integrity": "sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==" + }, + "bytes@3.1.2": { + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + }, + "cac@6.7.14": { + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==" + }, + "cache-base@1.0.1": { + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dependencies": [ + "collection-visit", + "component-emitter", + "get-value", + "has-value@1.0.0", + "isobject@3.0.1", + "set-value", + "to-object-path", + "union-value", + "unset-value" + ] + }, + "cacheable-lookup@7.0.0": { + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==" + }, + "cacheable-request@10.2.14": { + "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", + "dependencies": [ + "@types/http-cache-semantics", + "get-stream@6.0.1", + "http-cache-semantics", + "keyv", + "mimic-response@4.0.0", + "normalize-url", + "responselike" + ] + }, + "cachedir@2.4.0": { + "integrity": "sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==" + }, + "call-bind@1.0.7": { + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dependencies": [ + "es-define-property", + "es-errors", + "function-bind", + "get-intrinsic", + "set-function-length" + ] + }, + "callsite@1.0.0": { + "integrity": "sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ==" + }, + "callsites@3.1.0": { + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" + }, + "camelcase@6.3.0": { + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==" + }, + "camelcase@7.0.1": { + "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==" + }, + "chai@5.1.1": { + "integrity": "sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==", + "dependencies": [ + "assertion-error", + "check-error", + "deep-eql", + "loupe", + "pathval" + ] + }, + "chalk@1.1.3": { + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dependencies": [ + "ansi-styles@2.2.1", + "escape-string-regexp@1.0.5", + "has-ansi", + "strip-ansi@3.0.1", + "supports-color@2.0.0" + ] + }, + "chalk@2.4.2": { + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": [ + "ansi-styles@3.2.1", + "escape-string-regexp@1.0.5", + "supports-color@5.5.0" + ] + }, + "chalk@4.1.2": { + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": [ + "ansi-styles@4.3.0", + "supports-color@7.2.0" + ] + }, + "chalk@5.2.0": { + "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==" + }, + "character-entities@2.0.2": { + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==" + }, + "chardet@0.7.0": { + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + }, + "check-error@2.1.1": { + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==" + }, + "chokidar@3.5.3": { + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dependencies": [ + "anymatch", + "braces@3.0.3", + "fsevents", + "glob-parent@5.1.2", + "is-binary-path", + "is-glob", + "normalize-path@3.0.0", + "readdirp@3.6.0" + ] + }, + "chownr@2.0.0": { + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" + }, + "ci-info@3.8.0": { + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==" + }, + "class-utils@0.3.6": { + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dependencies": [ + "arr-union", + "define-property@0.2.5", + "isobject@3.0.1", + "static-extend" + ] + }, + "clean-deep@3.4.0": { + "integrity": "sha512-Lo78NV5ItJL/jl+B5w0BycAisaieJGXK1qYi/9m4SjR8zbqmrUtO7Yhro40wEShGmmxs/aJLI/A+jNhdkXK8mw==", + "dependencies": [ + "lodash.isempty", + "lodash.isplainobject", + "lodash.transform" + ] + }, + "clean-stack@4.2.0": { + "integrity": "sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==", + "dependencies": [ + "escape-string-regexp@5.0.0" + ] + }, + "cli-boxes@3.0.0": { + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==" + }, + "cli-cursor@2.1.0": { + "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", + "dependencies": [ + "restore-cursor@2.0.0" + ] + }, + "cli-cursor@4.0.0": { + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", + "dependencies": [ + "restore-cursor@4.0.0" + ] + }, + "cli-progress@3.12.0": { + "integrity": "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==", + "dependencies": [ + "string-width@4.2.3" + ] + }, + "cli-spinners@2.9.2": { + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==" + }, + "cli-truncate@0.2.1": { + "integrity": "sha512-f4r4yJnbT++qUPI9NR4XLDLq41gQ+uqnPItWG0F5ZkehuNiTTa3EY0S4AqTSUOeJ7/zU41oWPQSNkW5BqPL9bg==", + "dependencies": [ + "slice-ansi@0.0.4", + "string-width@1.0.2" + ] + }, + "cli-width@2.2.1": { + "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==" + }, + "cliui@8.0.1": { + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": [ + "string-width@4.2.3", + "strip-ansi@6.0.1", + "wrap-ansi@7.0.0" + ] + }, + "clone@1.0.4": { + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==" + }, + "code-point-at@1.1.0": { + "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==" + }, + "collection-visit@1.0.0": { + "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==", + "dependencies": [ + "map-visit", + "object-visit" + ] + }, + "color-convert@1.9.3": { + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": [ + "color-name@1.1.3" + ] + }, + "color-convert@2.0.1": { + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": [ + "color-name@1.1.4" + ] + }, + "color-name@1.1.3": { + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "color-name@1.1.4": { + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "color-string@1.9.1": { + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": [ + "color-name@1.1.4", + "simple-swizzle" + ] + }, + "color-support@1.1.3": { + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" + }, + "color@3.2.1": { + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dependencies": [ + "color-convert@1.9.3", + "color-string" + ] + }, + "colors-option@3.0.0": { + "integrity": "sha512-DP3FpjsiDDvnQC1OJBsdOJZPuy7r0o6sepY2T5M3L/d2nrE23O/ErFkEqyY3ngVL1ZhTj/H0pCMNObZGkEOaaQ==", + "dependencies": [ + "chalk@5.2.0", + "filter-obj@3.0.0", + "is-plain-obj@4.1.0", + "jest-validate" + ] + }, + "colors-option@4.5.0": { + "integrity": "sha512-Soe5lerRg3erMRgYC0EC696/8dMCGpBzcQchFfi55Yrkja8F+P7cUt0LVTIg7u5ob5BexLZ/F1kO+ejmv+nq8w==", + "dependencies": [ + "chalk@5.2.0", + "is-plain-obj@4.1.0" + ] + }, + "colors@1.4.0": { + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" + }, + "colorspace@1.1.4": { + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dependencies": [ + "color", + "text-hex" + ] + }, + "combined-stream@1.0.8": { + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": [ + "delayed-stream" + ] + }, + "commander@10.0.1": { + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==" + }, + "commander@2.20.3": { + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "commander@9.5.0": { + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==" + }, + "comment-json@4.2.3": { + "integrity": "sha512-SsxdiOf064DWoZLH799Ata6u7iV658A11PlWtZATDlXPpKGJnbJZ5Z24ybixAi+LUUqJ/GKowAejtC5GFUG7Tw==", + "dependencies": [ + "array-timsort", + "core-util-is", + "esprima", + "has-own-prop", + "repeat-string" + ] + }, + "comment-parser@1.4.1": { + "integrity": "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==" + }, + "common-path-prefix@3.0.0": { + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==" + }, + "component-emitter@1.3.1": { + "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==" + }, + "compress-commons@4.1.2": { + "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", + "dependencies": [ + "buffer-crc32@0.2.13", + "crc32-stream@4.0.3", + "normalize-path@3.0.0", + "readable-stream@3.6.2" + ] + }, + "compress-commons@6.0.2": { + "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==", + "dependencies": [ + "crc-32", + "crc32-stream@6.0.0", + "is-stream@2.0.1", + "normalize-path@3.0.0", + "readable-stream@4.5.2" + ] + }, + "concat-map@0.0.1": { + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "concordance@5.0.4": { + "integrity": "sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw==", + "dependencies": [ + "date-time", + "esutils", + "fast-diff", + "js-string-escape", + "lodash", + "md5-hex", + "semver@7.6.3", + "well-known-symbols" + ] + }, + "config-chain@1.1.13": { + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dependencies": [ + "ini@1.3.8", + "proto-list" + ] + }, + "configstore@6.0.0": { + "integrity": "sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==", + "dependencies": [ + "dot-prop@6.0.1", + "graceful-fs@4.2.11", + "unique-string", + "write-file-atomic@3.0.3", + "xdg-basedir" + ] + }, + "console-control-strings@1.1.0": { + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + }, + "content-disposition@0.5.4": { + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": [ + "safe-buffer@5.2.1" + ] + }, + "content-type@1.0.5": { + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" + }, + "cookie-signature@1.0.6": { + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "cookie@0.5.0": { + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" + }, + "cookie@0.6.0": { + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==" + }, + "copy-descriptor@0.1.1": { + "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==" + }, + "copy-template-dir@1.4.0": { + "integrity": "sha512-xkXSJhvKz4MfLbVkZ7GyCaFo4ciB3uKI/HHzkGwj1eyTH5+7RTFxW5CE0irWAZgV5oFcO9hd6+NVXAtY9hlo7Q==", + "dependencies": [ + "end-of-stream", + "graceful-fs@4.2.11", + "maxstache", + "maxstache-stream", + "mkdirp@0.5.6", + "noop2", + "pump@1.0.3", + "readdirp@2.2.1", + "run-parallel" + ] + }, + "core-util-is@1.0.3": { + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "cp-file@10.0.0": { + "integrity": "sha512-vy2Vi1r2epK5WqxOLnskeKeZkdZvTKfFZQCplE3XWsP+SUJyd5XAUFC9lFgTjjXJF2GMne/UML14iEmkAaDfFg==", + "dependencies": [ + "graceful-fs@4.2.11", + "nested-error-stacks", + "p-event@5.0.1" + ] + }, + "cp-file@9.1.0": { + "integrity": "sha512-3scnzFj/94eb7y4wyXRWwvzLFaQp87yyfTnChIjlfYrVqp5lVO3E2hIJMeQIltUT0K2ZAB3An1qXcBmwGyvuwA==", + "dependencies": [ + "graceful-fs@4.2.11", + "make-dir@3.1.0", + "nested-error-stacks", + "p-event@4.2.0" + ] + }, + "cpy@9.0.1": { + "integrity": "sha512-D9U0DR5FjTCN3oMTcFGktanHnAG5l020yvOCR1zKILmAyPP7I/9pl6NFgRbDcmSENtbK1sQLBz1p9HIOlroiNg==", + "dependencies": [ + "arrify", + "cp-file@9.1.0", + "globby@13.2.2", + "junk", + "micromatch@4.0.8", + "nested-error-stacks", + "p-filter", + "p-map@5.5.0" + ] + }, + "crc-32@1.2.2": { + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==" + }, + "crc32-stream@4.0.3": { + "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", + "dependencies": [ + "crc-32", + "readable-stream@3.6.2" + ] + }, + "crc32-stream@6.0.0": { + "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==", + "dependencies": [ + "crc-32", + "readable-stream@4.5.2" + ] + }, + "create-require@1.1.1": { + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" + }, + "cron-parser@4.8.1": { + "integrity": "sha512-jbokKWGcyU4gl6jAfX97E1gDpY12DJ1cLJZmoDzaAln/shZ+S3KBFBuA2Q6WeUN4gJf/8klnV1EfvhA2lK5IRQ==", + "dependencies": [ + "luxon" + ] + }, + "cross-spawn@7.0.3": { + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dependencies": [ + "path-key@3.1.1", + "shebang-command", + "which" + ] + }, + "crypto-random-string@4.0.0": { + "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", + "dependencies": [ + "type-fest@1.4.0" + ] + }, + "cyclist@1.0.2": { + "integrity": "sha512-0sVXIohTfLqVIW3kb/0n6IiWF3Ifj5nm2XaSrLq2DI6fKIGa2fYAZdk917rUneaeLVpYfFcyXE2ft0fe3remsA==" + }, + "damerau-levenshtein@1.0.8": { + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==" + }, + "data-uri-to-buffer@4.0.1": { + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==" + }, + "data-view-buffer@1.0.1": { + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "dependencies": [ + "call-bind", + "es-errors", + "is-data-view" + ] + }, + "data-view-byte-length@1.0.1": { + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "dependencies": [ + "call-bind", + "es-errors", + "is-data-view" + ] + }, + "data-view-byte-offset@1.0.0": { + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "dependencies": [ + "call-bind", + "es-errors", + "is-data-view" + ] + }, + "date-fns@1.30.1": { + "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==" + }, + "date-time@3.1.0": { + "integrity": "sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==", + "dependencies": [ + "time-zone" + ] + }, + "debug@2.6.9": { + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": [ + "ms@2.0.0" + ] + }, + "debug@3.2.7": { + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": [ + "ms@2.1.3" + ] + }, + "debug@4.3.4": { + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": [ + "ms@2.1.2" + ] + }, + "debug@4.3.7": { + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": [ + "ms@2.1.3" + ] + }, + "decache@4.6.2": { + "integrity": "sha512-2LPqkLeu8XWHU8qNCS3kcF6sCcb5zIzvWaAHYSvPfwhdd7mHuah29NssMzrTYyHN4F5oFy2ko9OBYxegtU0FEw==", + "dependencies": [ + "callsite" + ] + }, + "decode-named-character-reference@1.0.2": { + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", + "dependencies": [ + "character-entities" + ] + }, + "decode-uri-component@0.2.2": { + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==" + }, + "decompress-response@6.0.0": { + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dependencies": [ + "mimic-response@3.1.0" + ] + }, + "deep-eql@5.0.2": { + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==" + }, + "deep-equal@2.2.3": { + "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", + "dependencies": [ + "array-buffer-byte-length", + "call-bind", + "es-get-iterator", + "get-intrinsic", + "is-arguments", + "is-array-buffer", + "is-date-object", + "is-regex", + "is-shared-array-buffer", + "isarray@2.0.5", + "object-is", + "object-keys", + "object.assign", + "regexp.prototype.flags", + "side-channel", + "which-boxed-primitive", + "which-collection", + "which-typed-array" + ] + }, + "deep-extend@0.6.0": { + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + }, + "deep-is@0.1.4": { + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" + }, + "deepmerge@4.3.1": { + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==" + }, + "defaults@1.0.4": { + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dependencies": [ + "clone" + ] + }, + "defer-to-connect@2.0.1": { + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==" + }, + "define-data-property@1.1.4": { + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dependencies": [ + "es-define-property", + "es-errors", + "gopd" + ] + }, + "define-lazy-prop@2.0.0": { + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==" + }, + "define-properties@1.2.1": { + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dependencies": [ + "define-data-property", + "has-property-descriptors", + "object-keys" + ] + }, + "define-property@0.2.5": { + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dependencies": [ + "is-descriptor@0.1.7" + ] + }, + "define-property@1.0.0": { + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "dependencies": [ + "is-descriptor@1.0.3" + ] + }, + "define-property@2.0.2": { + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dependencies": [ + "is-descriptor@1.0.3", + "isobject@3.0.1" + ] + }, + "delayed-stream@1.0.0": { + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, + "delegates@1.0.0": { + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + }, + "depd@1.1.2": { + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==" + }, + "depd@2.0.0": { + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "deprecation@2.3.1": { + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + }, + "dequal@2.0.3": { + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==" + }, + "destroy@1.2.0": { + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" + }, + "detect-libc@2.0.3": { + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==" + }, + "detective-amd@5.0.2": { + "integrity": "sha512-XFd/VEQ76HSpym80zxM68ieB77unNuoMwopU2TFT/ErUk5n4KvUTwW4beafAVUugrjV48l4BmmR0rh2MglBaiA==", + "dependencies": [ + "ast-module-types", + "escodegen", + "get-amd-module-type", + "node-source-walk" + ] + }, + "detective-cjs@5.0.1": { + "integrity": "sha512-6nTvAZtpomyz/2pmEmGX1sXNjaqgMplhQkskq2MLrar0ZAIkHMrDhLXkRiK2mvbu9wSWr0V5/IfiTrZqAQMrmQ==", + "dependencies": [ + "ast-module-types", + "node-source-walk" + ] + }, + "detective-es6@4.0.1": { + "integrity": "sha512-k3Z5tB4LQ8UVHkuMrFOlvb3GgFWdJ9NqAa2YLUU/jTaWJIm+JJnEh4PsMc+6dfT223Y8ACKOaC0qcj7diIhBKw==", + "dependencies": [ + "node-source-walk" + ] + }, + "detective-postcss@6.1.3_postcss@8.4.47": { + "integrity": "sha512-7BRVvE5pPEvk2ukUWNQ+H2XOq43xENWbH0LcdCE14mwgTBEAMoAx+Fc1rdp76SmyZ4Sp48HlV7VedUnP6GA1Tw==", + "dependencies": [ + "is-url", + "postcss", + "postcss-values-parser" + ] + }, + "detective-sass@5.0.3": { + "integrity": "sha512-YsYT2WuA8YIafp2RVF5CEfGhhyIVdPzlwQgxSjK+TUm3JoHP+Tcorbk3SfG0cNZ7D7+cYWa0ZBcvOaR0O8+LlA==", + "dependencies": [ + "gonzales-pe", + "node-source-walk" + ] + }, + "detective-scss@4.0.3": { + "integrity": "sha512-VYI6cHcD0fLokwqqPFFtDQhhSnlFWvU614J42eY6G0s8c+MBhi9QAWycLwIOGxlmD8I/XvGSOUV1kIDhJ70ZPg==", + "dependencies": [ + "gonzales-pe", + "node-source-walk" + ] + }, + "detective-stylus@4.0.0": { + "integrity": "sha512-TfPotjhszKLgFBzBhTOxNHDsutIxx9GTWjrL5Wh7Qx/ydxKhwUrlSFeLIn+ZaHPF+h0siVBkAQSuy6CADyTxgQ==" + }, + "detective-typescript@11.2.0_typescript@5.6.2": { + "integrity": "sha512-ARFxjzizOhPqs1fYC/2NMC3N4jrQ6HvVflnXBTRqNEqJuXwyKLRr9CrJwkRcV/SnZt1sNXgsF6FPm0x57Tq0rw==", + "dependencies": [ + "@typescript-eslint/typescript-estree@5.62.0_typescript@5.6.2", + "ast-module-types", + "node-source-walk", + "typescript@5.6.2" + ] + }, + "devlop@1.1.0": { + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dependencies": [ + "dequal" + ] + }, + "diff@4.0.2": { + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" + }, + "dir-glob@3.0.1": { + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dependencies": [ + "path-type@4.0.0" + ] + }, + "doctrine@2.1.0": { + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dependencies": [ + "esutils" + ] + }, + "doctrine@3.0.0": { + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dependencies": [ + "esutils" + ] + }, + "dot-prop@6.0.1": { + "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", + "dependencies": [ + "is-obj" + ] + }, + "dot-prop@7.2.0": { + "integrity": "sha512-Ol/IPXUARn9CSbkrdV4VJo7uCy1I3VuSiWCaFSg+8BdUOzF9n3jefIpcgAydvUZbTdEBZs2vEiTiS9m61ssiDA==", + "dependencies": [ + "type-fest@2.19.0" + ] + }, + "dotenv@16.0.3": { + "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==" + }, + "dotenv@16.4.5": { + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==" + }, + "eastasianwidth@0.2.0": { + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + }, + "ecdsa-sig-formatter@1.0.11": { + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": [ + "safe-buffer@5.2.1" + ] + }, + "ee-first@1.1.1": { + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "elegant-spinner@1.0.1": { + "integrity": "sha512-B+ZM+RXvRqQaAmkMlO/oSe5nMUOaUnyfGYCEHoR8wrXsZR2mA0XVibsxV1bvTwxdRWah1PkQqso2EzhILGHtEQ==" + }, + "emoji-regex@8.0.0": { + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "emoji-regex@9.2.2": { + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "enabled@2.0.0": { + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" + }, + "encodeurl@1.0.2": { + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" + }, + "end-of-stream@1.4.4": { + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": [ + "once" + ] + }, + "entities@2.2.0": { + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" + }, + "env-paths@3.0.0": { + "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==" + }, + "envinfo@7.8.1": { + "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==" + }, + "error-ex@1.3.2": { + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dependencies": [ + "is-arrayish@0.2.1" + ] + }, + "error-stack-parser@2.1.4": { + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "dependencies": [ + "stackframe" + ] + }, + "error@7.0.2": { + "integrity": "sha512-UtVv4l5MhijsYUxPJo4390gzfZvAnTHreNnDjnTZaKIiZ/SemXxAhBkYSKtWa5RtBXbLP8tMgn/n0RUa/H7jXw==", + "dependencies": [ + "string-template", + "xtend" + ] + }, + "es-abstract@1.23.3": { + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "dependencies": [ + "array-buffer-byte-length", + "arraybuffer.prototype.slice", + "available-typed-arrays", + "call-bind", + "data-view-buffer", + "data-view-byte-length", + "data-view-byte-offset", + "es-define-property", + "es-errors", + "es-object-atoms", + "es-set-tostringtag", + "es-to-primitive", + "function.prototype.name", + "get-intrinsic", + "get-symbol-description", + "globalthis", + "gopd", + "has-property-descriptors", + "has-proto", + "has-symbols", + "hasown", + "internal-slot", + "is-array-buffer", + "is-callable", + "is-data-view", + "is-negative-zero", + "is-regex", + "is-shared-array-buffer", + "is-string", + "is-typed-array", + "is-weakref", + "object-inspect", + "object-keys", + "object.assign", + "regexp.prototype.flags", + "safe-array-concat", + "safe-regex-test", + "string.prototype.trim", + "string.prototype.trimend", + "string.prototype.trimstart", + "typed-array-buffer", + "typed-array-byte-length", + "typed-array-byte-offset", + "typed-array-length", + "unbox-primitive", + "which-typed-array" + ] + }, + "es-define-property@1.0.0": { + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": [ + "get-intrinsic" + ] + }, + "es-errors@1.3.0": { + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" + }, + "es-get-iterator@1.1.3": { + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", + "dependencies": [ + "call-bind", + "get-intrinsic", + "has-symbols", + "is-arguments", + "is-map", + "is-set", + "is-string", + "isarray@2.0.5", + "stop-iteration-iterator" + ] + }, + "es-iterator-helpers@1.0.19": { + "integrity": "sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==", + "dependencies": [ + "call-bind", + "define-properties", + "es-abstract", + "es-errors", + "es-set-tostringtag", + "function-bind", + "get-intrinsic", + "globalthis", + "has-property-descriptors", + "has-proto", + "has-symbols", + "internal-slot", + "iterator.prototype", + "safe-array-concat" + ] + }, + "es-module-lexer@1.5.4": { + "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==" + }, + "es-object-atoms@1.0.0": { + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dependencies": [ + "es-errors" + ] + }, + "es-set-tostringtag@2.0.3": { + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "dependencies": [ + "get-intrinsic", + "has-tostringtag", + "hasown" + ] + }, + "es-shim-unscopables@1.0.2": { + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dependencies": [ + "hasown" + ] + }, + "es-to-primitive@1.2.1": { + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dependencies": [ + "is-callable", + "is-date-object", + "is-symbol" + ] + }, + "es6-promisify@6.1.1": { + "integrity": "sha512-HBL8I3mIki5C1Cc9QjKUenHtnG0A5/xA8Q/AllRcfiwl2CZFXGK7ddBiCoRwAix4i2KxcQfjtIVcrVbB3vbmwg==" + }, + "esbuild@0.19.11": { + "integrity": "sha512-HJ96Hev2hX/6i5cDVwcqiJBBtuo9+FeIJOtZ9W1kA5M6AMJRHUZlpYZ1/SbEwtO0ioNAW8rUooVpC/WehY2SfA==", + "dependencies": [ + "@esbuild/aix-ppc64@0.19.11", + "@esbuild/android-arm@0.19.11", + "@esbuild/android-arm64@0.19.11", + "@esbuild/android-x64@0.19.11", + "@esbuild/darwin-arm64@0.19.11", + "@esbuild/darwin-x64@0.19.11", + "@esbuild/freebsd-arm64@0.19.11", + "@esbuild/freebsd-x64@0.19.11", + "@esbuild/linux-arm@0.19.11", + "@esbuild/linux-arm64@0.19.11", + "@esbuild/linux-ia32@0.19.11", + "@esbuild/linux-loong64@0.19.11", + "@esbuild/linux-mips64el@0.19.11", + "@esbuild/linux-ppc64@0.19.11", + "@esbuild/linux-riscv64@0.19.11", + "@esbuild/linux-s390x@0.19.11", + "@esbuild/linux-x64@0.19.11", + "@esbuild/netbsd-x64@0.19.11", + "@esbuild/openbsd-x64@0.19.11", + "@esbuild/sunos-x64@0.19.11", + "@esbuild/win32-arm64@0.19.11", + "@esbuild/win32-ia32@0.19.11", + "@esbuild/win32-x64@0.19.11" + ] + }, + "esbuild@0.19.2": { + "integrity": "sha512-G6hPax8UbFakEj3hWO0Vs52LQ8k3lnBhxZWomUJDxfz3rZTLqF5k/FCzuNdLx2RbpBiQQF9H9onlDDH1lZsnjg==", + "dependencies": [ + "@esbuild/android-arm@0.19.2", + "@esbuild/android-arm64@0.19.2", + "@esbuild/android-x64@0.19.2", + "@esbuild/darwin-arm64@0.19.2", + "@esbuild/darwin-x64@0.19.2", + "@esbuild/freebsd-arm64@0.19.2", + "@esbuild/freebsd-x64@0.19.2", + "@esbuild/linux-arm@0.19.2", + "@esbuild/linux-arm64@0.19.2", + "@esbuild/linux-ia32@0.19.2", + "@esbuild/linux-loong64@0.19.2", + "@esbuild/linux-mips64el@0.19.2", + "@esbuild/linux-ppc64@0.19.2", + "@esbuild/linux-riscv64@0.19.2", + "@esbuild/linux-s390x@0.19.2", + "@esbuild/linux-x64@0.19.2", + "@esbuild/netbsd-x64@0.19.2", + "@esbuild/openbsd-x64@0.19.2", + "@esbuild/sunos-x64@0.19.2", + "@esbuild/win32-arm64@0.19.2", + "@esbuild/win32-ia32@0.19.2", + "@esbuild/win32-x64@0.19.2" + ] + }, + "esbuild@0.21.5": { + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dependencies": [ + "@esbuild/aix-ppc64@0.21.5", + "@esbuild/android-arm@0.21.5", + "@esbuild/android-arm64@0.21.5", + "@esbuild/android-x64@0.21.5", + "@esbuild/darwin-arm64@0.21.5", + "@esbuild/darwin-x64@0.21.5", + "@esbuild/freebsd-arm64@0.21.5", + "@esbuild/freebsd-x64@0.21.5", + "@esbuild/linux-arm@0.21.5", + "@esbuild/linux-arm64@0.21.5", + "@esbuild/linux-ia32@0.21.5", + "@esbuild/linux-loong64@0.21.5", + "@esbuild/linux-mips64el@0.21.5", + "@esbuild/linux-ppc64@0.21.5", + "@esbuild/linux-riscv64@0.21.5", + "@esbuild/linux-s390x@0.21.5", + "@esbuild/linux-x64@0.21.5", + "@esbuild/netbsd-x64@0.21.5", + "@esbuild/openbsd-x64@0.21.5", + "@esbuild/sunos-x64@0.21.5", + "@esbuild/win32-arm64@0.21.5", + "@esbuild/win32-ia32@0.21.5", + "@esbuild/win32-x64@0.21.5" + ] + }, + "escalade@3.2.0": { + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==" + }, + "escape-goat@4.0.0": { + "integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==" + }, + "escape-html@1.0.3": { + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "escape-string-regexp@1.0.5": { + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + }, + "escape-string-regexp@4.0.0": { + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" + }, + "escape-string-regexp@5.0.0": { + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==" + }, + "escodegen@2.1.0": { + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dependencies": [ + "esprima", + "estraverse", + "esutils", + "source-map@0.6.1" + ] + }, + "eslint-config-prettier@9.1.0_eslint@8.57.1": { + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dependencies": [ + "eslint" + ] + }, + "eslint-import-resolver-node@0.3.9": { + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dependencies": [ + "debug@3.2.7", + "is-core-module", + "resolve@1.22.8" + ] + }, + "eslint-module-utils@2.12.0": { + "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", + "dependencies": [ + "debug@3.2.7" + ] + }, + "eslint-plugin-import@2.31.0_eslint@8.57.1": { + "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", + "dependencies": [ + "@rtsao/scc", + "array-includes", + "array.prototype.findlastindex", + "array.prototype.flat", + "array.prototype.flatmap", + "debug@3.2.7", + "doctrine@2.1.0", + "eslint", + "eslint-import-resolver-node", + "eslint-module-utils", + "hasown", + "is-core-module", + "is-glob", + "minimatch@3.1.2", + "object.fromentries", + "object.groupby", + "object.values", + "semver@6.3.1", + "string.prototype.trimend", + "tsconfig-paths" + ] + }, + "eslint-plugin-jsx-a11y@6.10.0_eslint@8.57.1": { + "integrity": "sha512-ySOHvXX8eSN6zz8Bywacm7CvGNhUtdjvqfQDVe6020TUK34Cywkw7m0KsCCk1Qtm9G1FayfTN1/7mMYnYO2Bhg==", + "dependencies": [ + "aria-query", + "array-includes", + "array.prototype.flatmap", + "ast-types-flow", + "axe-core", + "axobject-query", + "damerau-levenshtein", + "emoji-regex@9.2.2", + "es-iterator-helpers", + "eslint", + "hasown", + "jsx-ast-utils", + "language-tags", + "minimatch@3.1.2", + "object.fromentries", + "safe-regex-test", + "string.prototype.includes" + ] + }, + "eslint-plugin-prettier@5.2.1_eslint@8.57.1_eslint-config-prettier@9.1.0__eslint@8.57.1_prettier@3.2.5": { + "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==", + "dependencies": [ + "eslint", + "eslint-config-prettier", + "prettier", + "prettier-linter-helpers", + "synckit" + ] + }, + "eslint-plugin-react-hooks@4.6.2_eslint@8.57.1": { + "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", + "dependencies": [ + "eslint" + ] + }, + "eslint-plugin-react@7.37.1_eslint@8.57.1": { + "integrity": "sha512-xwTnwDqzbDRA8uJ7BMxPs/EXRB3i8ZfnOIp8BsxEQkT0nHPp+WWceqGgo6rKb9ctNi8GJLDT4Go5HAWELa/WMg==", + "dependencies": [ + "array-includes", + "array.prototype.findlast", + "array.prototype.flatmap", + "array.prototype.tosorted", + "doctrine@2.1.0", + "es-iterator-helpers", + "eslint", + "estraverse", + "hasown", + "jsx-ast-utils", + "minimatch@3.1.2", + "object.entries", + "object.fromentries", + "object.values", + "prop-types", + "resolve@2.0.0-next.5", + "semver@6.3.1", + "string.prototype.matchall", + "string.prototype.repeat" + ] + }, + "eslint-scope@7.2.2": { + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dependencies": [ + "esrecurse", + "estraverse" + ] + }, + "eslint-visitor-keys@3.4.3": { + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==" + }, + "eslint@8.57.1": { + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "dependencies": [ + "@eslint-community/eslint-utils", + "@eslint-community/regexpp", + "@eslint/eslintrc", + "@eslint/js", + "@humanwhocodes/config-array", + "@humanwhocodes/module-importer", + "@nodelib/fs.walk", + "@ungap/structured-clone", + "ajv@6.12.6", + "chalk@4.1.2", + "cross-spawn", + "debug@4.3.7", + "doctrine@3.0.0", + "escape-string-regexp@4.0.0", + "eslint-scope", + "eslint-visitor-keys", + "espree", + "esquery", + "esutils", + "fast-deep-equal", + "file-entry-cache", + "find-up@5.0.0", + "glob-parent@6.0.2", + "globals", + "graphemer", + "ignore", + "imurmurhash", + "is-glob", + "is-path-inside@3.0.3", + "js-yaml", + "json-stable-stringify-without-jsonify", + "levn", + "lodash.merge", + "minimatch@3.1.2", + "natural-compare", + "optionator", + "strip-ansi@6.0.1", + "text-table" + ] + }, + "espree@9.6.1_acorn@8.12.1": { + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dependencies": [ + "acorn", + "acorn-jsx", + "eslint-visitor-keys" + ] + }, + "esprima@4.0.1": { + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "esquery@1.6.0": { + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dependencies": [ + "estraverse" + ] + }, + "esrecurse@4.3.0": { + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dependencies": [ + "estraverse" + ] + }, + "estraverse@5.3.0": { + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" + }, + "estree-walker@2.0.2": { + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, + "estree-walker@3.0.3": { + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dependencies": [ + "@types/estree" + ] + }, + "esutils@2.0.3": { + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, + "etag@1.8.1": { + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" + }, + "event-target-shim@5.0.1": { + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, + "eventemitter3@4.0.7": { + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "events@3.3.0": { + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + }, + "execa@5.1.1": { + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dependencies": [ + "cross-spawn", + "get-stream@6.0.1", + "human-signals@2.1.0", + "is-stream@2.0.1", + "merge-stream", + "npm-run-path@4.0.1", + "onetime@5.1.2", + "signal-exit@3.0.7", + "strip-final-newline@2.0.0" + ] + }, + "execa@6.1.0": { + "integrity": "sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==", + "dependencies": [ + "cross-spawn", + "get-stream@6.0.1", + "human-signals@3.0.1", + "is-stream@3.0.0", + "merge-stream", + "npm-run-path@5.3.0", + "onetime@6.0.0", + "signal-exit@3.0.7", + "strip-final-newline@3.0.0" + ] + }, + "expand-brackets@2.1.4": { + "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==", + "dependencies": [ + "debug@2.6.9", + "define-property@0.2.5", + "extend-shallow@2.0.1", + "posix-character-classes", + "regex-not", + "snapdragon", + "to-regex" + ] + }, + "express-logging@1.1.1": { + "integrity": "sha512-1KboYwxxCG5kwkJHR5LjFDTD1Mgl8n4PIMcCuhhd/1OqaxlC68P3QKbvvAbZVUtVgtlxEdTgSUwf6yxwzRCuuA==", + "dependencies": [ + "on-headers" + ] + }, + "express@4.18.2": { + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dependencies": [ + "accepts", + "array-flatten", + "body-parser", + "content-disposition", + "content-type", + "cookie@0.5.0", + "cookie-signature", + "debug@2.6.9", + "depd@2.0.0", + "encodeurl", + "escape-html", + "etag", + "finalhandler", + "fresh", + "http-errors@2.0.0", + "merge-descriptors", + "methods", + "on-finished", + "parseurl", + "path-to-regexp", + "proxy-addr", + "qs", + "range-parser", + "safe-buffer@5.2.1", + "send", + "serve-static", + "setprototypeof", + "statuses@2.0.1", + "type-is", + "utils-merge", + "vary" + ] + }, + "ext-list@2.2.2": { + "integrity": "sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==", + "dependencies": [ + "mime-db" + ] + }, + "ext-name@5.0.0": { + "integrity": "sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==", + "dependencies": [ + "ext-list", + "sort-keys-length" + ] + }, + "extend-shallow@2.0.1": { + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": [ + "is-extendable@0.1.1" + ] + }, + "extend-shallow@3.0.2": { + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dependencies": [ + "assign-symbols", + "is-extendable@1.0.1" + ] + }, + "external-editor@3.1.0": { + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dependencies": [ + "chardet", + "iconv-lite", + "tmp@0.0.33" + ] + }, + "extglob@2.0.4": { + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dependencies": [ + "array-unique", + "define-property@1.0.0", + "expand-brackets", + "extend-shallow@2.0.1", + "fragment-cache", + "regex-not", + "snapdragon", + "to-regex" + ] + }, + "extract-zip@2.0.1": { + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dependencies": [ + "@types/yauzl", + "debug@4.3.7", + "get-stream@5.2.0", + "yauzl" + ] + }, + "fast-content-type-parse@1.1.0": { + "integrity": "sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==" + }, + "fast-decode-uri-component@1.0.1": { + "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==" + }, + "fast-deep-equal@3.1.3": { + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fast-diff@1.3.0": { + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==" + }, + "fast-equals@3.0.3": { + "integrity": "sha512-NCe8qxnZFARSHGztGMZOO/PC1qa5MIFB5Hp66WdzbCRAz8U8US3bx1UTgLS49efBQPcUtO9gf5oVEY8o7y/7Kg==" + }, + "fast-fifo@1.3.2": { + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==" + }, + "fast-glob@3.3.2": { + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dependencies": [ + "@nodelib/fs.stat", + "@nodelib/fs.walk", + "glob-parent@5.1.2", + "merge2", + "micromatch@4.0.8" + ] + }, + "fast-json-stable-stringify@2.1.0": { + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "fast-json-stringify@5.16.1_ajv@8.17.1": { + "integrity": "sha512-KAdnLvy1yu/XrRtP+LJnxbBGrhN+xXu+gt3EUvZhYGKCr3lFHq/7UFJHHFgmJKoqlh6B40bZLEv7w46B0mqn1g==", + "dependencies": [ + "@fastify/merge-json-schemas", + "ajv@8.17.1", + "ajv-formats@3.0.1_ajv@8.17.1", + "fast-deep-equal", + "fast-uri@2.4.0", + "json-schema-ref-resolver", + "rfdc" + ] + }, + "fast-levenshtein@2.0.6": { + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" + }, + "fast-querystring@1.1.2": { + "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==", + "dependencies": [ + "fast-decode-uri-component" + ] + }, + "fast-redact@3.5.0": { + "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==" + }, + "fast-safe-stringify@2.1.1": { + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" + }, + "fast-uri@2.4.0": { + "integrity": "sha512-ypuAmmMKInk5q7XcepxlnUWDLWv4GFtaJqAzWKqn62IpQ3pejtr5dTVbt3vwqVaMKmkNR55sTT+CqUKIaT21BA==" + }, + "fast-uri@3.0.2": { + "integrity": "sha512-GR6f0hD7XXyNJa25Tb9BuIdN0tdr+0BMi6/CJPH3wJO1JjNG3n/VsSw38AwRdKZABm8lGbPfakLRkYzx2V9row==" + }, + "fastest-levenshtein@1.0.16": { + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==" + }, + "fastify-plugin@4.5.1": { + "integrity": "sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==" + }, + "fastify@4.17.0": { + "integrity": "sha512-tzuY1tgWJo2Y6qEKwmLhFvACUmr68Io2pqP/sDKU71KRM6A6R3DrCDqLGqANbeLZcKUfdfY58ut35CGqemcTgg==", + "dependencies": [ + "@fastify/ajv-compiler", + "@fastify/error", + "@fastify/fast-json-stringify-compiler", + "abstract-logging", + "avvio", + "fast-content-type-parse", + "fast-json-stringify", + "find-my-way", + "light-my-request", + "pino", + "process-warning@2.3.2", + "proxy-addr", + "rfdc", + "secure-json-parse", + "semver@7.6.3", + "tiny-lru" + ] + }, + "fastq@1.17.1": { + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dependencies": [ + "reusify" + ] + }, + "fd-slicer@1.1.0": { + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dependencies": [ + "pend" + ] + }, + "fdir@6.3.0": { + "integrity": "sha512-QOnuT+BOtivR77wYvCWHfGt9s4Pz1VIMbD463vegT5MLqNXy8rYFT/lPVEqf/bhYeT6qmqrNHhsX+rWwe3rOCQ==" + }, + "fecha@4.2.3": { + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" + }, + "fetch-blob@3.2.0": { + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "dependencies": [ + "node-domexception", + "web-streams-polyfill" + ] + }, + "fetch-node-website@7.3.0": { + "integrity": "sha512-/wayUHbdVUWrD72aqRNNrr6+MHnCkumZgNugN0RfiWJpbNJUdAkMk4Z18MGayGZVVqYXR1RWrV+bIFEt5HuBZg==", + "dependencies": [ + "cli-progress", + "colors-option@4.5.0", + "figures@5.0.0", + "got", + "is-plain-obj@4.1.0" + ] + }, + "figures@1.7.0": { + "integrity": "sha512-UxKlfCRuCBxSXU4C6t9scbDyWZ4VlaFFdojKtzJuSkuOBQ5CNFum+zZXFwHjo+CxBC1t6zlYPgHIgFjL8ggoEQ==", + "dependencies": [ + "escape-string-regexp@1.0.5", + "object-assign" + ] + }, + "figures@2.0.0": { + "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", + "dependencies": [ + "escape-string-regexp@1.0.5" + ] + }, + "figures@3.2.0": { + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dependencies": [ + "escape-string-regexp@1.0.5" + ] + }, + "figures@4.0.1": { + "integrity": "sha512-rElJwkA/xS04Vfg+CaZodpso7VqBknOYbzi6I76hI4X80RUjkSxO2oAyPmGbuXUppywjqndOrQDl817hDnI++w==", + "dependencies": [ + "escape-string-regexp@5.0.0", + "is-unicode-supported" + ] + }, + "figures@5.0.0": { + "integrity": "sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==", + "dependencies": [ + "escape-string-regexp@5.0.0", + "is-unicode-supported" + ] + }, + "file-entry-cache@6.0.1": { + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dependencies": [ + "flat-cache" + ] + }, + "file-type@18.7.0": { + "integrity": "sha512-ihHtXRzXEziMrQ56VSgU7wkxh55iNchFkosu7Y9/S+tXHdKyrGjVK0ujbqNnsxzea+78MaLhN6PGmfYSAv1ACw==", + "dependencies": [ + "readable-web-to-node-stream", + "strtok3", + "token-types" + ] + }, + "file-uri-to-path@1.0.0": { + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, + "filename-reserved-regex@3.0.0": { + "integrity": "sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==" + }, + "filenamify@5.1.1": { + "integrity": "sha512-M45CbrJLGACfrPOkrTp3j2EcO9OBkKUYME0eiqOCa7i2poaklU0jhlIaMlr8ijLorT0uLAzrn3qXOp5684CkfA==", + "dependencies": [ + "filename-reserved-regex", + "strip-outer", + "trim-repeated" + ] + }, + "fill-range@4.0.0": { + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dependencies": [ + "extend-shallow@2.0.1", + "is-number@3.0.0", + "repeat-string", + "to-regex-range@2.1.1" + ] + }, + "fill-range@7.1.1": { + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dependencies": [ + "to-regex-range@5.0.1" + ] + }, + "filter-obj@3.0.0": { + "integrity": "sha512-oQZM+QmVni8MsYzcq9lgTHD/qeLqaG8XaOPOW7dzuSafVxSUlH1+1ZDefj2OD9f2XsmG5lFl2Euc9NI4jgwFWg==" + }, + "filter-obj@5.1.0": { + "integrity": "sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng==" + }, + "finalhandler@1.2.0": { + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": [ + "debug@2.6.9", + "encodeurl", + "escape-html", + "on-finished", + "parseurl", + "statuses@2.0.1", + "unpipe" + ] + }, + "find-my-way@7.7.0": { + "integrity": "sha512-+SrHpvQ52Q6W9f3wJoJBbAQULJuNEEQwBvlvYwACDhBTLOTMiQ0HYWh4+vC3OivGP2ENcTI1oKlFA2OepJNjhQ==", + "dependencies": [ + "fast-deep-equal", + "fast-querystring", + "safe-regex2" + ] + }, + "find-up@5.0.0": { + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dependencies": [ + "locate-path@6.0.0", + "path-exists@4.0.0" + ] + }, + "find-up@6.3.0": { + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "dependencies": [ + "locate-path@7.2.0", + "path-exists@5.0.0" + ] + }, + "flat-cache@3.2.0": { + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dependencies": [ + "flatted", + "keyv", + "rimraf" + ] + }, + "flatted@3.3.1": { + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==" + }, + "flush-write-stream@2.0.0": { + "integrity": "sha512-uXClqPxT4xW0lcdSBheb2ObVU+kuqUk3Jk64EwieirEXZx9XUrVwp/JuBfKAWaM4T5Td/VL7QLDWPXp/MvGm/g==", + "dependencies": [ + "inherits", + "readable-stream@3.6.2" + ] + }, + "fn.name@1.1.0": { + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, + "folder-walker@3.2.0": { + "integrity": "sha512-VjAQdSLsl6AkpZNyrQJfO7BXLo4chnStqb055bumZMbRUPpVuPN3a4ktsnRCmrFZjtMlYLkyXiR5rAs4WOpC4Q==", + "dependencies": [ + "from2" + ] + }, + "follow-redirects@1.15.9": { + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==" + }, + "for-each@0.3.3": { + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dependencies": [ + "is-callable" + ] + }, + "for-in@1.0.2": { + "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==" + }, + "foreground-child@3.3.0": { + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dependencies": [ + "cross-spawn", + "signal-exit@4.1.0" + ] + }, + "form-data-encoder@2.1.4": { + "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==" + }, + "form-data@4.0.0": { + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": [ + "asynckit", + "combined-stream", + "mime-types" + ] + }, + "formdata-polyfill@4.0.10": { + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dependencies": [ + "fetch-blob" + ] + }, + "forwarded@0.2.0": { + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, + "fragment-cache@0.2.1": { + "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==", + "dependencies": [ + "map-cache" + ] + }, + "fresh@0.5.2": { + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" + }, + "from2-array@0.0.4": { + "integrity": "sha512-0G0cAp7sYLobH7ALsr835x98PU/YeVF7wlwxdWbCUaea7wsa7lJfKZUAo6p2YZGZ8F94luCuqHZS3JtFER6uPg==", + "dependencies": [ + "from2" + ] + }, + "from2@2.3.0": { + "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", + "dependencies": [ + "inherits", + "readable-stream@2.3.8" + ] + }, + "fs-constants@1.0.0": { + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "fs-minipass@2.1.0": { + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": [ + "minipass@3.3.6" + ] + }, + "fs.realpath@1.0.0": { + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "fsevents@2.3.3": { + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==" + }, + "function-bind@1.1.2": { + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" + }, + "function.prototype.name@1.1.6": { + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "dependencies": [ + "call-bind", + "define-properties", + "es-abstract", + "functions-have-names" + ] + }, + "functions-have-names@1.2.3": { + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" + }, + "fuzzy@0.1.3": { + "integrity": "sha512-/gZffu4ykarLrCiP3Ygsa86UAo1E5vEVlvTrpkKywXSbP9Xhln3oSp9QSV57gEq3JFFpGJ4GZ+5zdEp3FcUh4w==" + }, + "gauge@3.0.2": { + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "dependencies": [ + "aproba", + "color-support", + "console-control-strings", + "has-unicode", + "object-assign", + "signal-exit@3.0.7", + "string-width@4.2.3", + "strip-ansi@6.0.1", + "wide-align" + ] + }, + "get-amd-module-type@5.0.1": { + "integrity": "sha512-jb65zDeHyDjFR1loOVk0HQGM5WNwoGB8aLWy3LKCieMKol0/ProHkhO2X1JxojuN10vbz1qNn09MJ7tNp7qMzw==", + "dependencies": [ + "ast-module-types", + "node-source-walk" + ] + }, + "get-caller-file@2.0.5": { + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + }, + "get-func-name@2.0.2": { + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==" + }, + "get-intrinsic@1.2.4": { + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dependencies": [ + "es-errors", + "function-bind", + "has-proto", + "has-symbols", + "hasown" + ] + }, + "get-port@5.1.1": { + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==" + }, + "get-port@6.1.2": { + "integrity": "sha512-BrGGraKm2uPqurfGVj/z97/zv8dPleC6x9JBNRTrDNtCkkRF4rPwrQXFgL7+I+q8QSdU4ntLQX2D7KIxSy8nGw==" + }, + "get-stream@5.2.0": { + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dependencies": [ + "pump@3.0.0" + ] + }, + "get-stream@6.0.1": { + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" + }, + "get-symbol-description@1.0.2": { + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "dependencies": [ + "call-bind", + "es-errors", + "get-intrinsic" + ] + }, + "get-tsconfig@4.8.1": { + "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==", + "dependencies": [ + "resolve-pkg-maps" + ] + }, + "get-value@2.0.6": { + "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==" + }, + "gh-release-fetch@4.0.3": { + "integrity": "sha512-TOiP1nwLsH5shG85Yt6v6Kjq5JU/44jXyEpbcfPgmj3C829yeXIlx9nAEwQRaxtRF3SJinn2lz7XUkfG9W/U4g==", + "dependencies": [ + "@xhmikosr/downloader", + "node-fetch@3.3.2", + "semver@7.6.3" + ] + }, + "git-repo-info@2.1.1": { + "integrity": "sha512-8aCohiDo4jwjOwma4FmYFd3i97urZulL8XL24nIPxuE+GZnfsAyy/g2Shqx6OjUiFKUXZM+Yy+KHnOmmA3FVcg==" + }, + "gitconfiglocal@2.1.0": { + "integrity": "sha512-qoerOEliJn3z+Zyn1HW2F6eoYJqKwS6MgC9cztTLUB/xLWX8gD/6T60pKn4+t/d6tP7JlybI7Z3z+I572CR/Vg==", + "dependencies": [ + "ini@1.3.8" + ] + }, + "glob-parent@5.1.2": { + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": [ + "is-glob" + ] + }, + "glob-parent@6.0.2": { + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dependencies": [ + "is-glob" + ] + }, + "glob@10.4.5": { + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dependencies": [ + "foreground-child", + "jackspeak", + "minimatch@9.0.5", + "minipass@7.1.2", + "package-json-from-dist", + "path-scurry" + ] + }, + "glob@7.2.3": { + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": [ + "fs.realpath", + "inflight", + "inherits", + "minimatch@3.1.2", + "once", + "path-is-absolute" + ] + }, + "glob@8.1.0": { + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dependencies": [ + "fs.realpath", + "inflight", + "inherits", + "minimatch@5.1.6", + "once" + ] + }, + "global-cache-dir@4.4.0": { + "integrity": "sha512-bk0gI6IbbphRjAaCJJn5H+T/CcEck5B3a5KBO2BXSDzjFSV+API17w8GA7YPJ6IXJiasW8M0VsEIig1PCHdfOQ==", + "dependencies": [ + "cachedir", + "path-exists@5.0.0" + ] + }, + "global-dirs@3.0.1": { + "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", + "dependencies": [ + "ini@2.0.0" + ] + }, + "globals@13.24.0": { + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dependencies": [ + "type-fest@0.20.2" + ] + }, + "globalthis@1.0.4": { + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dependencies": [ + "define-properties", + "gopd" + ] + }, + "globby@11.1.0": { + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dependencies": [ + "array-union", + "dir-glob", + "fast-glob", + "ignore", + "merge2", + "slash@3.0.0" + ] + }, + "globby@13.2.2": { + "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", + "dependencies": [ + "dir-glob", + "fast-glob", + "ignore", + "merge2", + "slash@4.0.0" + ] + }, + "gonzales-pe@4.3.0": { + "integrity": "sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ==", + "dependencies": [ + "minimist" + ] + }, + "gopd@1.0.1": { + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": [ + "get-intrinsic" + ] + }, + "got@12.6.1": { + "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", + "dependencies": [ + "@sindresorhus/is", + "@szmarczak/http-timer", + "cacheable-lookup", + "cacheable-request", + "decompress-response", + "form-data-encoder", + "get-stream@6.0.1", + "http2-wrapper", + "lowercase-keys", + "p-cancelable", + "responselike" + ] + }, + "graceful-fs@4.2.10": { + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" + }, + "graceful-fs@4.2.11": { + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "graphemer@1.4.0": { + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==" + }, + "has-ansi@2.0.0": { + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", + "dependencies": [ + "ansi-regex@2.1.1" + ] + }, + "has-bigints@1.0.2": { + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==" + }, + "has-flag@3.0.0": { + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + }, + "has-flag@4.0.0": { + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "has-own-prop@2.0.0": { + "integrity": "sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==" + }, + "has-property-descriptors@1.0.2": { + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dependencies": [ + "es-define-property" + ] + }, + "has-proto@1.0.3": { + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==" + }, + "has-symbols@1.0.3": { + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, + "has-tostringtag@1.0.2": { + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dependencies": [ + "has-symbols" + ] + }, + "has-unicode@2.0.1": { + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, + "has-value@0.3.1": { + "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==", + "dependencies": [ + "get-value", + "has-values@0.1.4", + "isobject@2.1.0" + ] + }, + "has-value@1.0.0": { + "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==", + "dependencies": [ + "get-value", + "has-values@1.0.0", + "isobject@3.0.1" + ] + }, + "has-values@0.1.4": { + "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==" + }, + "has-values@1.0.0": { + "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==", + "dependencies": [ + "is-number@3.0.0", + "kind-of@4.0.0" + ] + }, + "has-yarn@3.0.0": { + "integrity": "sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==" + }, + "hasbin@1.2.3": { + "integrity": "sha512-CCd8e/w2w28G8DyZvKgiHnQJ/5XXDz6qiUHnthvtag/6T5acUeN5lqq+HMoBqcmgWueWDhiCplrw0Kb1zDACRg==", + "dependencies": [ + "async@1.5.2" + ] + }, + "hasha@5.2.2": { + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "dependencies": [ + "is-stream@2.0.1", + "type-fest@0.8.1" + ] + }, + "hasown@2.0.2": { + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": [ + "function-bind" + ] + }, + "hexer@1.5.0": { + "integrity": "sha512-dyrPC8KzBzUJ19QTIo1gXNqIISRXQ0NwteW6OeQHRN4ZuZeHkdODfj0zHBdOlHbRY8GqbqK57C9oWSvQZizFsg==", + "dependencies": [ + "ansi-color", + "minimist", + "process@0.10.1", + "xtend" + ] + }, + "hosted-git-info@4.1.0": { + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dependencies": [ + "lru-cache@6.0.0" + ] + }, + "hot-shots@10.0.0": { + "integrity": "sha512-uy/uGpuJk7yuyiKRfZMBNkF1GAOX5O2ifO9rDCaX9jw8fu6eW9QeWC7WRPDI+O98frW1HQgV3+xwjWsZPECIzQ==", + "dependencies": [ + "unix-dgram" + ] + }, + "http-cache-semantics@4.1.1": { + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" + }, + "http-errors@1.8.1": { + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "dependencies": [ + "depd@1.1.2", + "inherits", + "setprototypeof", + "statuses@1.5.0", + "toidentifier" + ] + }, + "http-errors@2.0.0": { + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": [ + "depd@2.0.0", + "inherits", + "setprototypeof", + "statuses@2.0.1", + "toidentifier" + ] + }, + "http-proxy-middleware@2.0.6": { + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "dependencies": [ + "@types/http-proxy", + "http-proxy", + "is-glob", + "is-plain-obj@3.0.0", + "micromatch@4.0.8" + ] + }, + "http-proxy@1.18.1": { + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dependencies": [ + "eventemitter3", + "follow-redirects", + "requires-port" + ] + }, + "http2-wrapper@2.2.1": { + "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", + "dependencies": [ + "quick-lru", + "resolve-alpn" + ] + }, + "https-proxy-agent@5.0.1": { + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": [ + "agent-base", + "debug@4.3.7" + ] + }, + "human-signals@2.1.0": { + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" + }, + "human-signals@3.0.1": { + "integrity": "sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==" + }, + "husky@8.0.3": { + "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==" + }, + "iconv-lite@0.4.24": { + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": [ + "safer-buffer" + ] + }, + "ieee754@1.2.1": { + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "ignore@5.3.2": { + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==" + }, + "import-fresh@3.3.0": { + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dependencies": [ + "parent-module", + "resolve-from@4.0.0" + ] + }, + "import-lazy@4.0.0": { + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==" + }, + "imurmurhash@0.1.4": { + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==" + }, + "indent-string@3.2.0": { + "integrity": "sha512-BYqTHXTGUIvg7t1r4sJNKcbDZkL92nkXA8YtRpbjFHRHGDL/NtUeiBJMeE60kIFN/Mg8ESaWQvftaYMGJzQZCQ==" + }, + "indent-string@5.0.0": { + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==" + }, + "inflight@1.0.6": { + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dependencies": [ + "once", + "wrappy" + ] + }, + "inherits@2.0.4": { + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini@1.3.8": { + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "ini@2.0.0": { + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==" + }, + "inquirer-autocomplete-prompt@1.4.0_inquirer@6.5.2": { + "integrity": "sha512-qHgHyJmbULt4hI+kCmwX92MnSxDs/Yhdt4wPA30qnoa01OF6uTXV8yvH4hKXgdaTNmkZ9D01MHjqKYEuJN+ONw==", + "dependencies": [ + "ansi-escapes@4.3.2", + "chalk@4.1.2", + "figures@3.2.0", + "inquirer", + "run-async", + "rxjs" + ] + }, + "inquirer@6.5.2": { + "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", + "dependencies": [ + "ansi-escapes@3.2.0", + "chalk@2.4.2", + "cli-cursor@2.1.0", + "cli-width", + "external-editor", + "figures@2.0.0", + "lodash", + "mute-stream", + "run-async", + "rxjs", + "string-width@2.1.1", + "strip-ansi@5.2.0", + "through" + ] + }, + "inspect-with-kind@1.0.5": { + "integrity": "sha512-MAQUJuIo7Xqk8EVNP+6d3CKq9c80hi4tjIbIAT6lmGW9W6WzlHiu9PS8uSuUYU+Do+j1baiFp3H25XEVxDIG2g==", + "dependencies": [ + "kind-of@6.0.3" + ] + }, + "internal-slot@1.0.7": { + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "dependencies": [ + "es-errors", + "hasown", + "side-channel" + ] + }, + "ipaddr.js@1.9.1": { + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "is-accessor-descriptor@1.0.1": { + "integrity": "sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==", + "dependencies": [ + "hasown" + ] + }, + "is-arguments@1.1.1": { + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dependencies": [ + "call-bind", + "has-tostringtag" + ] + }, + "is-array-buffer@3.0.4": { + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "dependencies": [ + "call-bind", + "get-intrinsic" + ] + }, + "is-arrayish@0.2.1": { + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "is-arrayish@0.3.2": { + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, + "is-async-function@2.0.0": { + "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", + "dependencies": [ + "has-tostringtag" + ] + }, + "is-bigint@1.0.4": { + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dependencies": [ + "has-bigints" + ] + }, + "is-binary-path@2.1.0": { + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": [ + "binary-extensions" + ] + }, + "is-boolean-object@1.1.2": { + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dependencies": [ + "call-bind", + "has-tostringtag" + ] + }, + "is-buffer@1.1.6": { + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "is-builtin-module@3.2.1": { + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dependencies": [ + "builtin-modules" + ] + }, + "is-callable@1.2.7": { + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==" + }, + "is-ci@3.0.1": { + "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", + "dependencies": [ + "ci-info" + ] + }, + "is-core-module@2.15.1": { + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "dependencies": [ + "hasown" + ] + }, + "is-data-descriptor@1.0.1": { + "integrity": "sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw==", + "dependencies": [ + "hasown" + ] + }, + "is-data-view@1.0.1": { + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "dependencies": [ + "is-typed-array" + ] + }, + "is-date-object@1.0.5": { + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dependencies": [ + "has-tostringtag" + ] + }, + "is-descriptor@0.1.7": { + "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", + "dependencies": [ + "is-accessor-descriptor", + "is-data-descriptor" + ] + }, + "is-descriptor@1.0.3": { + "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==", + "dependencies": [ + "is-accessor-descriptor", + "is-data-descriptor" + ] + }, + "is-docker@2.2.1": { + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" + }, + "is-docker@3.0.0": { + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==" + }, + "is-extendable@0.1.1": { + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==" + }, + "is-extendable@1.0.1": { + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dependencies": [ + "is-plain-object@2.0.4" + ] + }, + "is-extglob@2.1.1": { + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" + }, + "is-finalizationregistry@1.0.2": { + "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", + "dependencies": [ + "call-bind" + ] + }, + "is-fullwidth-code-point@1.0.0": { + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", + "dependencies": [ + "number-is-nan" + ] + }, + "is-fullwidth-code-point@2.0.0": { + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==" + }, + "is-fullwidth-code-point@3.0.0": { + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "is-fullwidth-code-point@4.0.0": { + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==" + }, + "is-generator-function@1.0.10": { + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dependencies": [ + "has-tostringtag" + ] + }, + "is-glob@4.0.3": { + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": [ + "is-extglob" + ] + }, + "is-installed-globally@0.4.0": { + "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", + "dependencies": [ + "global-dirs", + "is-path-inside@3.0.3" + ] + }, + "is-interactive@2.0.0": { + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==" + }, + "is-map@2.0.3": { + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==" + }, + "is-negative-zero@2.0.3": { + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==" + }, + "is-npm@6.0.0": { + "integrity": "sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==" + }, + "is-number-object@1.0.7": { + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dependencies": [ + "has-tostringtag" + ] + }, + "is-number@3.0.0": { + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dependencies": [ + "kind-of@3.2.2" + ] + }, + "is-number@7.0.0": { + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "is-obj@2.0.0": { + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" + }, + "is-observable@1.1.0": { + "integrity": "sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA==", + "dependencies": [ + "symbol-observable" + ] + }, + "is-path-inside@3.0.3": { + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==" + }, + "is-path-inside@4.0.0": { + "integrity": "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==" + }, + "is-plain-obj@1.1.0": { + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==" + }, + "is-plain-obj@2.1.0": { + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==" + }, + "is-plain-obj@3.0.0": { + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==" + }, + "is-plain-obj@4.1.0": { + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==" + }, + "is-plain-object@2.0.4": { + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dependencies": [ + "isobject@3.0.1" + ] + }, + "is-plain-object@5.0.0": { + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" + }, + "is-promise@2.2.2": { + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" + }, + "is-regex@1.1.4": { + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dependencies": [ + "call-bind", + "has-tostringtag" + ] + }, + "is-set@2.0.3": { + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==" + }, + "is-shared-array-buffer@1.0.3": { + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "dependencies": [ + "call-bind" + ] + }, + "is-stream@1.1.0": { + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==" + }, + "is-stream@2.0.1": { + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" + }, + "is-stream@3.0.0": { + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==" + }, + "is-string@1.0.7": { + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dependencies": [ + "has-tostringtag" + ] + }, + "is-symbol@1.0.4": { + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dependencies": [ + "has-symbols" + ] + }, + "is-typed-array@1.1.13": { + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "dependencies": [ + "which-typed-array" + ] + }, + "is-typedarray@1.0.0": { + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, + "is-unicode-supported@1.3.0": { + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==" + }, + "is-url-superb@4.0.0": { + "integrity": "sha512-GI+WjezhPPcbM+tqE9LnmsY5qqjwHzTvjJ36wxYX5ujNXefSUJ/T17r5bqDV8yLhcgB59KTPNOc9O9cmHTPWsA==" + }, + "is-url@1.2.4": { + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==" + }, + "is-weakmap@2.0.2": { + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==" + }, + "is-weakref@1.0.2": { + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dependencies": [ + "call-bind" + ] + }, + "is-weakset@2.0.3": { + "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", + "dependencies": [ + "call-bind", + "get-intrinsic" + ] + }, + "is-windows@1.0.2": { + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" + }, + "is-wsl@2.2.0": { + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dependencies": [ + "is-docker@2.2.1" + ] + }, + "is-yarn-global@0.4.1": { + "integrity": "sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ==" + }, + "isarray@1.0.0": { + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "isarray@2.0.5": { + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, + "iserror@0.0.2": { + "integrity": "sha512-oKGGrFVaWwETimP3SiWwjDeY27ovZoyZPHtxblC4hCq9fXxed/jasx+ATWFFjCVSRZng8VTMsN1nDnGo6zMBSw==" + }, + "isexe@2.0.0": { + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "isobject@2.1.0": { + "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", + "dependencies": [ + "isarray@1.0.0" + ] + }, + "isobject@3.0.1": { + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==" + }, + "iterator.prototype@1.1.2": { + "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", + "dependencies": [ + "define-properties", + "get-intrinsic", + "has-symbols", + "reflect.getprototypeof", + "set-function-name" + ] + }, + "jackspeak@3.4.3": { + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dependencies": [ + "@isaacs/cliui", + "@pkgjs/parseargs" + ] + }, + "jaeger-client@3.19.0": { + "integrity": "sha512-M0c7cKHmdyEUtjemnJyx/y9uX16XHocL46yQvyqDlPdvAcwPDbHrIbKjQdBqtiE4apQ/9dmr+ZLJYYPGnurgpw==", + "dependencies": [ + "node-int64", + "opentracing", + "thriftrw", + "uuid@8.3.2", + "xorshift" + ] + }, + "jest-get-type@27.5.1": { + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==" + }, + "jest-validate@27.5.1": { + "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", + "dependencies": [ + "@jest/types", + "camelcase@6.3.0", + "chalk@4.1.2", + "jest-get-type", + "leven", + "pretty-format" + ] + }, + "js-string-escape@1.0.1": { + "integrity": "sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==" + }, + "js-tokens@4.0.0": { + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "js-yaml@4.1.0": { + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": [ + "argparse" + ] + }, + "json-buffer@3.0.1": { + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + }, + "json-parse-even-better-errors@2.3.1": { + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, + "json-schema-ref-resolver@1.0.1": { + "integrity": "sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==", + "dependencies": [ + "fast-deep-equal" + ] + }, + "json-schema-traverse@0.4.1": { + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-schema-traverse@1.0.0": { + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "json-stable-stringify-without-jsonify@1.0.1": { + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" + }, + "json5@1.0.2": { + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dependencies": [ + "minimist" + ] + }, + "jsonc-parser@3.3.1": { + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==" + }, + "jsonpointer@5.0.1": { + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==" + }, + "jsonwebtoken@9.0.1": { + "integrity": "sha512-K8wx7eJ5TPvEjuiVSkv167EVboBDv9PZdDoF7BgeQnBLVvZWW9clr2PsQHVJDTKaEIH5JBIwHujGcHp7GgI2eg==", + "dependencies": [ + "jws", + "lodash", + "ms@2.1.3", + "semver@7.6.3" + ] + }, + "jsx-ast-utils@3.3.5": { + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dependencies": [ + "array-includes", + "array.prototype.flat", + "object.assign", + "object.values" + ] + }, + "junk@4.0.1": { + "integrity": "sha512-Qush0uP+G8ZScpGMZvHUiRfI0YBWuB3gVBYlI0v0vvOJt5FLicco+IkP0a50LqTTQhmts/m6tP5SWE+USyIvcQ==" + }, + "jwa@1.4.1": { + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": [ + "buffer-equal-constant-time", + "ecdsa-sig-formatter", + "safe-buffer@5.2.1" + ] + }, + "jws@3.2.2": { + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": [ + "jwa", + "safe-buffer@5.2.1" + ] + }, + "jwt-decode@3.1.2": { + "integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==" + }, + "keep-func-props@4.0.1": { + "integrity": "sha512-87ftOIICfdww3SxR5P1veq3ThBNyRPG0JGL//oaR08v0k2yTicEIHd7s0GqSJfQvlb+ybC3GiDepOweo0LDhvw==", + "dependencies": [ + "mimic-fn@4.0.0" + ] + }, + "keyv@4.5.4": { + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dependencies": [ + "json-buffer" + ] + }, + "kind-of@3.2.2": { + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dependencies": [ + "is-buffer" + ] + }, + "kind-of@4.0.0": { + "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==", + "dependencies": [ + "is-buffer" + ] + }, + "kind-of@6.0.3": { + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + }, + "kuler@2.0.0": { + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + }, + "lambda-local@2.1.2": { + "integrity": "sha512-nGTJn2JxZWcLGpNwXFmXC7UEXL7QCLieQWDiXs46vIv9y/gSPm/uHygEMCaym+HIziniAw0XIm+1VTrXCvG1Zw==", + "dependencies": [ + "commander@10.0.1", + "dotenv@16.4.5", + "winston@3.14.2" + ] + }, + "language-subtag-registry@0.3.23": { + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==" + }, + "language-tags@1.0.9": { + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "dependencies": [ + "language-subtag-registry" + ] + }, + "latest-version@7.0.0": { + "integrity": "sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==", + "dependencies": [ + "package-json" + ] + }, + "lazystream@1.0.1": { + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dependencies": [ + "readable-stream@2.3.8" + ] + }, + "leven@3.1.0": { + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==" + }, + "levn@0.4.1": { + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dependencies": [ + "prelude-ls", + "type-check" + ] + }, + "light-my-request@5.13.0": { + "integrity": "sha512-9IjUN9ZyCS9pTG+KqTDEQo68Sui2lHsYBrfMyVUTTZ3XhH8PMZq7xO94Kr+eP9dhi/kcKsx4N41p2IXEBil1pQ==", + "dependencies": [ + "cookie@0.6.0", + "process-warning@3.0.0", + "set-cookie-parser" + ] + }, + "lines-and-columns@1.2.4": { + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + }, + "listr-silent-renderer@1.1.1": { + "integrity": "sha512-L26cIFm7/oZeSNVhWB6faeorXhMg4HNlb/dS/7jHhr708jxlXrtrBWo4YUxZQkc6dGoxEAe6J/D3juTRBUzjtA==" + }, + "listr-update-renderer@0.5.0_listr@0.14.3": { + "integrity": "sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA==", + "dependencies": [ + "chalk@1.1.3", + "cli-truncate", + "elegant-spinner", + "figures@1.7.0", + "indent-string@3.2.0", + "listr", + "log-symbols@1.0.2", + "log-update@2.3.0", + "strip-ansi@3.0.1" + ] + }, + "listr-verbose-renderer@0.5.0": { + "integrity": "sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw==", + "dependencies": [ + "chalk@2.4.2", + "cli-cursor@2.1.0", + "date-fns", + "figures@2.0.0" + ] + }, + "listr@0.14.3": { + "integrity": "sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA==", + "dependencies": [ + "@samverschueren/stream-to-observable", + "is-observable", + "is-promise", + "is-stream@1.1.0", + "listr-silent-renderer", + "listr-update-renderer", + "listr-verbose-renderer", + "p-map@2.1.0", + "rxjs" + ] + }, + "locate-path@6.0.0": { + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dependencies": [ + "p-locate@5.0.0" + ] + }, + "locate-path@7.2.0": { + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dependencies": [ + "p-locate@6.0.0" + ] + }, + "lodash-es@4.17.21": { + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, + "lodash.camelcase@4.3.0": { + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, + "lodash.defaults@4.2.0": { + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" + }, + "lodash.difference@4.5.0": { + "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==" + }, + "lodash.flatten@4.4.0": { + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" + }, + "lodash.isempty@4.4.0": { + "integrity": "sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg==" + }, + "lodash.isplainobject@4.0.6": { + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "lodash.merge@4.6.2": { + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "lodash.transform@4.6.0": { + "integrity": "sha512-LO37ZnhmBVx0GvOU/caQuipEh4GN82TcWv3yHlebGDgOxbxiwwzW5Pcx2AcvpIv2WmvmSMoC492yQFNhy/l/UQ==" + }, + "lodash.union@4.6.0": { + "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==" + }, + "lodash@4.17.21": { + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "log-process-errors@8.0.0": { + "integrity": "sha512-+SNGqNC1gCMJfhwYzAHr/YgNT/ZJc+V2nCkvtPnjrENMeCe+B/jgShBW0lmWoh6uVV2edFAPc/IUOkDdsjTbTg==", + "dependencies": [ + "colors-option@3.0.0", + "figures@4.0.1", + "filter-obj@3.0.0", + "jest-validate", + "map-obj", + "moize", + "semver@7.6.3" + ] + }, + "log-symbols@1.0.2": { + "integrity": "sha512-mmPrW0Fh2fxOzdBbFv4g1m6pR72haFLPJ2G5SJEELf1y+iaQrDG6cWCPjy54RHYbZAt7X+ls690Kw62AdWXBzQ==", + "dependencies": [ + "chalk@1.1.3" + ] + }, + "log-symbols@5.1.0": { + "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", + "dependencies": [ + "chalk@5.2.0", + "is-unicode-supported" + ] + }, + "log-update@2.3.0": { + "integrity": "sha512-vlP11XfFGyeNQlmEn9tJ66rEW1coA/79m5z6BCkudjbAGE83uhAcGYrBFwfs3AdLiLzGRusRPAbSPK9xZteCmg==", + "dependencies": [ + "ansi-escapes@3.2.0", + "cli-cursor@2.1.0", + "wrap-ansi@3.0.1" + ] + }, + "log-update@5.0.1": { + "integrity": "sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw==", + "dependencies": [ + "ansi-escapes@5.0.0", + "cli-cursor@4.0.0", + "slice-ansi@5.0.0", + "strip-ansi@7.1.0", + "wrap-ansi@8.1.0" + ] + }, + "logform@2.6.1": { + "integrity": "sha512-CdaO738xRapbKIMVn2m4F6KTj4j7ooJ8POVnebSgKo3KBz5axNXRAL7ZdRjIV6NOr2Uf4vjtRkxrFETOioCqSA==", + "dependencies": [ + "@colors/colors@1.6.0", + "@types/triple-beam", + "fecha", + "ms@2.1.3", + "safe-stable-stringify", + "triple-beam" + ] + }, + "long@2.4.0": { + "integrity": "sha512-ijUtjmO/n2A5PaosNG9ZGDsQ3vxJg7ZW8vsY8Kp0f2yIZWhSJvjmegV7t+9RPQKxKrvj8yKGehhS+po14hPLGQ==" + }, + "long@5.2.3": { + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, + "loose-envify@1.4.0": { + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": [ + "js-tokens" + ] + }, + "loupe@3.1.1": { + "integrity": "sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==", + "dependencies": [ + "get-func-name" + ] + }, + "lowercase-keys@3.0.0": { + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==" + }, + "lru-cache@10.4.3": { + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" + }, + "lru-cache@6.0.0": { + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": [ + "yallist" + ] + }, + "lunr@2.3.9": { + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==" + }, + "luxon@3.5.0": { + "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==" + }, + "macos-release@3.3.0": { + "integrity": "sha512-tPJQ1HeyiU2vRruNGhZ+VleWuMQRro8iFtJxYgnS4NQe+EukKF6aGiIT+7flZhISAt2iaXBCfFGvAyif7/f8nQ==" + }, + "magic-string@0.30.11": { + "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", + "dependencies": [ + "@jridgewell/sourcemap-codec" + ] + }, + "make-dir@3.1.0": { + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dependencies": [ + "semver@6.3.1" + ] + }, + "make-dir@4.0.0": { + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dependencies": [ + "semver@7.6.3" + ] + }, + "make-error@1.3.6": { + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, + "map-cache@0.2.2": { + "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==" + }, + "map-obj@5.0.2": { + "integrity": "sha512-K6K2NgKnTXimT3779/4KxSvobxOtMmx1LBZ3NwRxT/MDIR3Br/fQ4Q+WCX5QxjyUR8zg5+RV9Tbf2c5pAWTD2A==" + }, + "map-visit@1.0.0": { + "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==", + "dependencies": [ + "object-visit" + ] + }, + "marked@4.3.0": { + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==" + }, + "maxstache-stream@1.0.4": { + "integrity": "sha512-v8qlfPN0pSp7bdSoLo1NTjG43GXGqk5W2NWFnOCq2GlmFFqebGzPCjLKSbShuqIOVorOtZSAy7O/S1OCCRONUw==", + "dependencies": [ + "maxstache", + "pump@1.0.3", + "split2@1.1.1", + "through2" + ] + }, + "maxstache@1.0.7": { + "integrity": "sha512-53ZBxHrZM+W//5AcRVewiLpDunHnucfdzZUGz54Fnvo4tE+J3p8EL66kBrs2UhBXvYKTWckWYYWBqJqoTcenqg==" + }, + "md5-hex@3.0.1": { + "integrity": "sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==", + "dependencies": [ + "blueimp-md5" + ] + }, + "mdast-util-from-markdown@2.0.1": { + "integrity": "sha512-aJEUyzZ6TzlsX2s5B4Of7lN7EQtAxvtradMMglCQDyaTFgse6CmtmdJ15ElnVRlCg1vpNyVtbem0PWzlNieZsA==", + "dependencies": [ + "@types/mdast", + "@types/unist", + "decode-named-character-reference", + "devlop", + "mdast-util-to-string", + "micromark", + "micromark-util-decode-numeric-character-reference", + "micromark-util-decode-string", + "micromark-util-normalize-identifier", + "micromark-util-symbol", + "micromark-util-types", + "unist-util-stringify-position" + ] + }, + "mdast-util-to-string@4.0.0": { + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "dependencies": [ + "@types/mdast" + ] + }, + "media-typer@0.3.0": { + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" + }, + "memoize-one@6.0.0": { + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" + }, + "merge-descriptors@1.0.1": { + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "merge-options@3.0.4": { + "integrity": "sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==", + "dependencies": [ + "is-plain-obj@2.1.0" + ] + }, + "merge-stream@2.0.0": { + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "merge2@1.4.1": { + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" + }, + "methods@1.1.2": { + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" + }, + "micro-api-client@3.3.0": { + "integrity": "sha512-y0y6CUB9RLVsy3kfgayU28746QrNMpSm9O/AYGNsBgOkJr/X/Jk0VLGoO8Ude7Bpa8adywzF+MzXNZRFRsNPhg==" + }, + "micro-memoize@4.1.2": { + "integrity": "sha512-+HzcV2H+rbSJzApgkj0NdTakkC+bnyeiUxgT6/m7mjcz1CmM22KYFKp+EVj1sWe4UYcnriJr5uqHQD/gMHLD+g==" + }, + "micromark-core-commonmark@2.0.1": { + "integrity": "sha512-CUQyKr1e///ZODyD1U3xit6zXwy1a8q2a1S1HKtIlmgvurrEpaw/Y9y6KSIbF8P59cn/NjzHyO+Q2fAyYLQrAA==", + "dependencies": [ + "decode-named-character-reference", + "devlop", + "micromark-factory-destination", + "micromark-factory-label", + "micromark-factory-space", + "micromark-factory-title", + "micromark-factory-whitespace", + "micromark-util-character", + "micromark-util-chunked", + "micromark-util-classify-character", + "micromark-util-html-tag-name", + "micromark-util-normalize-identifier", + "micromark-util-resolve-all", + "micromark-util-subtokenize", + "micromark-util-symbol", + "micromark-util-types" + ] + }, + "micromark-factory-destination@2.0.0": { + "integrity": "sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==", + "dependencies": [ + "micromark-util-character", + "micromark-util-symbol", + "micromark-util-types" + ] + }, + "micromark-factory-label@2.0.0": { + "integrity": "sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==", + "dependencies": [ + "devlop", + "micromark-util-character", + "micromark-util-symbol", + "micromark-util-types" + ] + }, + "micromark-factory-space@2.0.0": { + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "dependencies": [ + "micromark-util-character", + "micromark-util-types" + ] + }, + "micromark-factory-title@2.0.0": { + "integrity": "sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==", + "dependencies": [ + "micromark-factory-space", + "micromark-util-character", + "micromark-util-symbol", + "micromark-util-types" + ] + }, + "micromark-factory-whitespace@2.0.0": { + "integrity": "sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==", + "dependencies": [ + "micromark-factory-space", + "micromark-util-character", + "micromark-util-symbol", + "micromark-util-types" + ] + }, + "micromark-util-character@2.1.0": { + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "dependencies": [ + "micromark-util-symbol", + "micromark-util-types" + ] + }, + "micromark-util-chunked@2.0.0": { + "integrity": "sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==", + "dependencies": [ + "micromark-util-symbol" + ] + }, + "micromark-util-classify-character@2.0.0": { + "integrity": "sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==", + "dependencies": [ + "micromark-util-character", + "micromark-util-symbol", + "micromark-util-types" + ] + }, + "micromark-util-combine-extensions@2.0.0": { + "integrity": "sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==", + "dependencies": [ + "micromark-util-chunked", + "micromark-util-types" + ] + }, + "micromark-util-decode-numeric-character-reference@2.0.1": { + "integrity": "sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==", + "dependencies": [ + "micromark-util-symbol" + ] + }, + "micromark-util-decode-string@2.0.0": { + "integrity": "sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==", + "dependencies": [ + "decode-named-character-reference", + "micromark-util-character", + "micromark-util-decode-numeric-character-reference", + "micromark-util-symbol" + ] + }, + "micromark-util-encode@2.0.0": { + "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==" + }, + "micromark-util-html-tag-name@2.0.0": { + "integrity": "sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==" + }, + "micromark-util-normalize-identifier@2.0.0": { + "integrity": "sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==", + "dependencies": [ + "micromark-util-symbol" + ] + }, + "micromark-util-resolve-all@2.0.0": { + "integrity": "sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==", + "dependencies": [ + "micromark-util-types" + ] + }, + "micromark-util-sanitize-uri@2.0.0": { + "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", + "dependencies": [ + "micromark-util-character", + "micromark-util-encode", + "micromark-util-symbol" + ] + }, + "micromark-util-subtokenize@2.0.1": { + "integrity": "sha512-jZNtiFl/1aY73yS3UGQkutD0UbhTt68qnRpw2Pifmz5wV9h8gOVsN70v+Lq/f1rKaU/W8pxRe8y8Q9FX1AOe1Q==", + "dependencies": [ + "devlop", + "micromark-util-chunked", + "micromark-util-symbol", + "micromark-util-types" + ] + }, + "micromark-util-symbol@2.0.0": { + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" + }, + "micromark-util-types@2.0.0": { + "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==" + }, + "micromark@4.0.0": { + "integrity": "sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==", + "dependencies": [ + "@types/debug", + "debug@4.3.7", + "decode-named-character-reference", + "devlop", + "micromark-core-commonmark", + "micromark-factory-space", + "micromark-util-character", + "micromark-util-chunked", + "micromark-util-combine-extensions", + "micromark-util-decode-numeric-character-reference", + "micromark-util-encode", + "micromark-util-normalize-identifier", + "micromark-util-resolve-all", + "micromark-util-sanitize-uri", + "micromark-util-subtokenize", + "micromark-util-symbol", + "micromark-util-types" + ] + }, + "micromatch@3.1.10": { + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dependencies": [ + "arr-diff", + "array-unique", + "braces@2.3.2", + "define-property@2.0.2", + "extend-shallow@3.0.2", + "extglob", + "fragment-cache", + "kind-of@6.0.3", + "nanomatch", + "object.pick", + "regex-not", + "snapdragon", + "to-regex" + ] + }, + "micromatch@4.0.8": { + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dependencies": [ + "braces@3.0.3", + "picomatch" + ] + }, + "mime-db@1.52.0": { + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types@2.1.35": { + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": [ + "mime-db" + ] + }, + "mime@1.6.0": { + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime@3.0.0": { + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==" + }, + "mimic-fn@1.2.0": { + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" + }, + "mimic-fn@2.1.0": { + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, + "mimic-fn@4.0.0": { + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==" + }, + "mimic-response@3.1.0": { + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" + }, + "mimic-response@4.0.0": { + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==" + }, + "minimatch@3.1.2": { + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": [ + "brace-expansion@1.1.11" + ] + }, + "minimatch@5.1.6": { + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dependencies": [ + "brace-expansion@2.0.1" + ] + }, + "minimatch@9.0.5": { + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dependencies": [ + "brace-expansion@2.0.1" + ] + }, + "minimist@1.2.8": { + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" + }, + "minipass@3.3.6": { + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": [ + "yallist" + ] + }, + "minipass@5.0.0": { + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==" + }, + "minipass@7.1.2": { + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==" + }, + "minizlib@2.1.2": { + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": [ + "minipass@3.3.6", + "yallist" + ] + }, + "mixin-deep@1.3.2": { + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dependencies": [ + "for-in", + "is-extendable@1.0.1" + ] + }, + "mkdirp@0.5.6": { + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": [ + "minimist" + ] + }, + "mkdirp@1.0.4": { + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + }, + "module-definition@5.0.1": { + "integrity": "sha512-kvw3B4G19IXk+BOXnYq/D/VeO9qfHaapMeuS7w7sNUqmGaA6hywdFHMi+VWeR9wUScXM7XjoryTffCZ5B0/8IA==", + "dependencies": [ + "ast-module-types", + "node-source-walk" + ] + }, + "module-details-from-path@1.0.3": { + "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==" + }, + "moize@6.1.6": { + "integrity": "sha512-vSKdIUO61iCmTqhdoIDrqyrtp87nWZUmBPniNjO0fX49wEYmyDO4lvlnFXiGcaH1JLE/s/9HbiK4LSHsbiUY6Q==", + "dependencies": [ + "fast-equals", + "micro-memoize" + ] + }, + "move-file@3.1.0": { + "integrity": "sha512-4aE3U7CCBWgrQlQDMq8da4woBWDGHioJFiOZ8Ie6Yq2uwYQ9V2kGhTz4x3u6Wc+OU17nw0yc3rJ/lQ4jIiPe3A==", + "dependencies": [ + "path-exists@5.0.0" + ] + }, + "ms@2.0.0": { + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "ms@2.1.2": { + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "ms@2.1.3": { + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "multiparty@4.2.3": { + "integrity": "sha512-Ak6EUJZuhGS8hJ3c2fY6UW5MbkGUPMBEGd13djUzoY/BHqV/gTuFWtC6IuVA7A2+v3yjBS6c4or50xhzTQZImQ==", + "dependencies": [ + "http-errors@1.8.1", + "safe-buffer@5.2.1", + "uid-safe" + ] + }, + "mute-stream@0.0.7": { + "integrity": "sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ==" + }, + "nan@2.20.0": { + "integrity": "sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==" + }, + "nanoid@3.3.7": { + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==" + }, + "nanomatch@1.2.13": { + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dependencies": [ + "arr-diff", + "array-unique", + "define-property@2.0.2", + "extend-shallow@3.0.2", + "fragment-cache", + "is-windows", + "kind-of@6.0.3", + "object.pick", + "regex-not", + "snapdragon", + "to-regex" + ] + }, + "natural-compare@1.4.0": { + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" + }, + "negotiator@0.6.3": { + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" + }, + "nested-error-stacks@2.1.1": { + "integrity": "sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==" + }, + "netlify-cli@16.2.0_inquirer@6.5.2": { + "integrity": "sha512-I4oDn3ApCzykK6AnejnU6IUr2OiJqpxIR+5+F3YujBakav9AW4s/sNIE9FAryul8u8RGJMfo1J7Irb92+y2uLA==", + "dependencies": [ + "@bugsnag/js", + "@fastify/static", + "@netlify/build", + "@netlify/build-info", + "@netlify/config", + "@netlify/edge-bundler", + "@netlify/local-functions-proxy", + "@netlify/serverless-functions-api@1.7.3", + "@netlify/zip-it-and-ship-it@9.17.0", + "@octokit/rest", + "ansi-escapes@6.2.0", + "ansi-styles@6.2.1", + "ansi-to-html", + "ascii-table", + "backoff", + "better-opn", + "boxen", + "chalk@5.2.0", + "chokidar", + "ci-info", + "clean-deep", + "commander@10.0.1", + "comment-json", + "concordance", + "configstore", + "content-type", + "cookie@0.5.0", + "copy-template-dir", + "cron-parser", + "debug@4.3.4", + "decache", + "dot-prop@7.2.0", + "dotenv@16.0.3", + "env-paths", + "envinfo", + "etag", + "execa@5.1.1", + "express", + "express-logging", + "extract-zip", + "fastest-levenshtein", + "fastify", + "find-up@6.3.0", + "flush-write-stream", + "folder-walker", + "from2-array", + "fuzzy", + "get-port@5.1.1", + "gh-release-fetch", + "git-repo-info", + "gitconfiglocal", + "hasbin", + "hasha", + "http-proxy", + "http-proxy-middleware", + "https-proxy-agent", + "inquirer", + "inquirer-autocomplete-prompt", + "is-docker@3.0.0", + "is-stream@3.0.0", + "is-wsl", + "isexe", + "jsonwebtoken", + "jwt-decode", + "lambda-local", + "listr", + "locate-path@7.2.0", + "lodash", + "log-symbols@5.1.0", + "log-update@5.0.1", + "minimist", + "multiparty", + "netlify", + "netlify-headers-parser", + "netlify-redirect-parser", + "netlify-redirector", + "node-fetch@2.6.12", + "node-version-alias", + "ora", + "p-filter", + "p-map@5.5.0", + "p-wait-for@5.0.2", + "parallel-transform", + "parse-github-url", + "parse-gitignore", + "path-key@4.0.0", + "prettyjson", + "pump@3.0.0", + "raw-body@2.5.2", + "read-pkg-up", + "semver@7.5.4", + "source-map-support", + "strip-ansi-control-characters", + "tabtab", + "tempy", + "terminal-link", + "through2-filter", + "through2-map", + "to-readable-stream", + "toml", + "ulid", + "unixify", + "update-notifier", + "uuid@9.0.0", + "wait-port", + "winston@3.8.2", + "write-file-atomic@5.0.1" + ] + }, + "netlify-headers-parser@7.1.2": { + "integrity": "sha512-DfoboA8PrcLXMan3jIVyLsQtKS+nepKDx6WwZKk5EQDMr2AJoBPCtSHTOLuABzkde1UXdOITf3snmcAmzlNLqw==", + "dependencies": [ + "escape-string-regexp@5.0.0", + "fast-safe-stringify", + "is-plain-obj@4.1.0", + "map-obj", + "path-exists@5.0.0", + "toml" + ] + }, + "netlify-redirect-parser@14.2.0": { + "integrity": "sha512-3Mi7sMH7XXZhjvXx/5RtJ/rU/E6zKkE4etcYQbEu8B3r872D0opoYyGdPW/MvaYQyVdfg23XEFaEI4zzQTupaw==", + "dependencies": [ + "fast-safe-stringify", + "filter-obj@5.1.0", + "is-plain-obj@4.1.0", + "path-exists@5.0.0", + "toml" + ] + }, + "netlify-redirector@0.4.0": { + "integrity": "sha512-ssD+V9o2DD9VnilOYC+34i07IrlY8XDsh5mN+qLYA4MxCpdALKXFICcz1KzsHZabuIS5XsF1VP/HzDyx5ubJ2g==" + }, + "netlify@13.1.10": { + "integrity": "sha512-ByFz8S08HWVKd9r/lkTahZX7xSq4IRyPCUvuaduI4GHyQaSWEdVNK1krC05vlhL9W0SzDn8Yjowh0Ru4PKrOYw==", + "dependencies": [ + "@netlify/open-api", + "lodash-es", + "micro-api-client", + "node-fetch@3.3.2", + "omit.js", + "p-wait-for@4.1.0", + "qs" + ] + }, + "node-domexception@1.0.0": { + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==" + }, + "node-fetch@2.6.12": { + "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", + "dependencies": [ + "whatwg-url" + ] + }, + "node-fetch@3.3.2": { + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dependencies": [ + "data-uri-to-buffer", + "fetch-blob", + "formdata-polyfill" + ] + }, + "node-gyp-build@4.8.2": { + "integrity": "sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw==" + }, + "node-int64@0.4.0": { + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==" + }, + "node-source-walk@6.0.2": { + "integrity": "sha512-jn9vOIK/nfqoFCcpK89/VCVaLg1IHE6UVfDOzvqmANaJ/rWCTEdH8RZ1V278nv2jr36BJdyQXIAavBLXpzdlag==", + "dependencies": [ + "@babel/parser" + ] + }, + "node-stream-zip@1.15.0": { + "integrity": "sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw==" + }, + "node-version-alias@3.4.1": { + "integrity": "sha512-Kf3L9spAL6lEHMPyqpwHSTNG3LPkOXBfSUnBMG/YE2TdoC8Qoqf0+qg01nr6K9MFQEcXtWUyTQzLJByRixSBsA==", + "dependencies": [ + "all-node-versions", + "filter-obj@5.1.0", + "is-plain-obj@4.1.0", + "normalize-node-version", + "path-exists@5.0.0", + "semver@7.6.3" + ] + }, + "noop2@2.0.0": { + "integrity": "sha512-2bu7Pfpf6uNqashWV8P7yYeutQ3XkLY9MBSYI5sOAFZxuWcW/uJfLbKj5m6SvMDT9U1Y0C+7UFG+7VSiIdXjtA==" + }, + "nopt@5.0.0": { + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dependencies": [ + "abbrev" + ] + }, + "normalize-node-version@12.4.0": { + "integrity": "sha512-0oLZN5xcyKVrSHMk8/9RuNblEe7HEsXAt5Te2xmMiZD9VX7bqWYe0HMyfqSYFD3xv0949lZuXaEwjTqle1uWWQ==", + "dependencies": [ + "all-node-versions", + "filter-obj@5.1.0", + "semver@7.6.3" + ] + }, + "normalize-package-data@3.0.3": { + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dependencies": [ + "hosted-git-info", + "is-core-module", + "semver@7.6.3", + "validate-npm-package-license" + ] + }, + "normalize-path@2.1.1": { + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dependencies": [ + "remove-trailing-separator" + ] + }, + "normalize-path@3.0.0": { + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "normalize-url@8.0.1": { + "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==" + }, + "npm-run-path@4.0.1": { + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dependencies": [ + "path-key@3.1.1" + ] + }, + "npm-run-path@5.3.0": { + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dependencies": [ + "path-key@4.0.0" + ] + }, + "npmlog@5.0.1": { + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "dependencies": [ + "are-we-there-yet", + "console-control-strings", + "gauge", + "set-blocking" + ] + }, + "number-is-nan@1.0.1": { + "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==" + }, + "object-assign@4.1.1": { + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + }, + "object-copy@0.1.0": { + "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==", + "dependencies": [ + "copy-descriptor", + "define-property@0.2.5", + "kind-of@3.2.2" + ] + }, + "object-inspect@1.13.2": { + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==" + }, + "object-is@1.1.6": { + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "dependencies": [ + "call-bind", + "define-properties" + ] + }, + "object-keys@1.1.1": { + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object-visit@1.0.1": { + "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==", + "dependencies": [ + "isobject@3.0.1" + ] + }, + "object.assign@4.1.5": { + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "dependencies": [ + "call-bind", + "define-properties", + "has-symbols", + "object-keys" + ] + }, + "object.entries@1.1.8": { + "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", + "dependencies": [ + "call-bind", + "define-properties", + "es-object-atoms" + ] + }, + "object.fromentries@2.0.8": { + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dependencies": [ + "call-bind", + "define-properties", + "es-abstract", + "es-object-atoms" + ] + }, + "object.groupby@1.0.3": { + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dependencies": [ + "call-bind", + "define-properties", + "es-abstract" + ] + }, + "object.pick@1.3.0": { + "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", + "dependencies": [ + "isobject@3.0.1" + ] + }, + "object.values@1.2.0": { + "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", + "dependencies": [ + "call-bind", + "define-properties", + "es-object-atoms" + ] + }, + "omit.js@2.0.2": { + "integrity": "sha512-hJmu9D+bNB40YpL9jYebQl4lsTW6yEHRTroJzNLqQJYHm7c+NQnJGfZmIWh8S3q3KoaxV1aLhV6B3+0N0/kyJg==" + }, + "on-exit-leak-free@2.1.2": { + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==" + }, + "on-finished@2.4.1": { + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": [ + "ee-first" + ] + }, + "on-headers@1.0.2": { + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + }, + "once@1.4.0": { + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": [ + "wrappy" + ] + }, + "one-time@1.0.0": { + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dependencies": [ + "fn.name" + ] + }, + "onetime@2.0.1": { + "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", + "dependencies": [ + "mimic-fn@1.2.0" + ] + }, + "onetime@5.1.2": { + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": [ + "mimic-fn@2.1.0" + ] + }, + "onetime@6.0.0": { + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dependencies": [ + "mimic-fn@4.0.0" + ] + }, + "open@8.4.2": { + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dependencies": [ + "define-lazy-prop", + "is-docker@2.2.1", + "is-wsl" + ] + }, + "opentracing@0.14.7": { + "integrity": "sha512-vz9iS7MJ5+Bp1URw8Khvdyw1H/hGvzHWlKQ7eRrQojSCDL1/SrWfrY9QebLw97n2deyRtzHRC3MkQfVNUCo91Q==" + }, + "optionator@0.9.4": { + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dependencies": [ + "deep-is", + "fast-levenshtein", + "levn", + "prelude-ls", + "type-check", + "word-wrap" + ] + }, + "ora@6.3.1": { + "integrity": "sha512-ERAyNnZOfqM+Ao3RAvIXkYh5joP220yf59gVe2X/cI6SiCxIdi4c9HZKZD8R6q/RDXEje1THBju6iExiSsgJaQ==", + "dependencies": [ + "chalk@5.2.0", + "cli-cursor@4.0.0", + "cli-spinners", + "is-interactive", + "is-unicode-supported", + "log-symbols@5.1.0", + "stdin-discarder", + "strip-ansi@7.1.0", + "wcwidth" + ] + }, + "os-name@5.1.0": { + "integrity": "sha512-YEIoAnM6zFmzw3PQ201gCVCIWbXNyKObGlVvpAVvraAeOHnlYVKFssbA/riRX5R40WA6kKrZ7Dr7dWzO3nKSeQ==", + "dependencies": [ + "macos-release", + "windows-release" + ] + }, + "os-tmpdir@1.0.2": { + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==" + }, + "p-cancelable@3.0.0": { + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==" + }, + "p-event@4.2.0": { + "integrity": "sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==", + "dependencies": [ + "p-timeout@3.2.0" + ] + }, + "p-event@5.0.1": { + "integrity": "sha512-dd589iCQ7m1L0bmC5NLlVYfy3TbBEsMUfWx9PyAgPeIcFZ/E2yaTZ4Rz4MiBmmJShviiftHVXOqfnfzJ6kyMrQ==", + "dependencies": [ + "p-timeout@5.1.0" + ] + }, + "p-every@2.0.0": { + "integrity": "sha512-MCz9DqD5opPC48Zsd+BHm56O/HfhYIQQtupfDzhXoVgQdg/Ux4F8/JcdRuQ+arq7zD5fB6zP3axbH3d9Nr8dlw==", + "dependencies": [ + "p-map@2.1.0" + ] + }, + "p-filter@3.0.0": { + "integrity": "sha512-QtoWLjXAW++uTX67HZQz1dbTpqBfiidsB6VtQUC9iR85S120+s0T5sO6s+B5MLzFcZkrEd/DGMmCjR+f2Qpxwg==", + "dependencies": [ + "p-map@5.5.0" + ] + }, + "p-finally@1.0.0": { + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==" + }, + "p-limit@3.1.0": { + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dependencies": [ + "yocto-queue@0.1.0" + ] + }, + "p-limit@4.0.0": { + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dependencies": [ + "yocto-queue@1.1.1" + ] + }, + "p-locate@5.0.0": { + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dependencies": [ + "p-limit@3.1.0" + ] + }, + "p-locate@6.0.0": { + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dependencies": [ + "p-limit@4.0.0" + ] + }, + "p-map@2.1.0": { + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==" + }, + "p-map@5.5.0": { + "integrity": "sha512-VFqfGDHlx87K66yZrNdI4YGtD70IRyd+zSvgks6mzHPRNkoKy+9EKP4SFC77/vTTQYmRmti7dvqC+m5jBrBAcg==", + "dependencies": [ + "aggregate-error" + ] + }, + "p-reduce@3.0.0": { + "integrity": "sha512-xsrIUgI0Kn6iyDYm9StOpOeK29XM1aboGji26+QEortiFST1hGZaUQOLhtEbqHErPpGW/aSz6allwK2qcptp0Q==" + }, + "p-retry@5.1.2": { + "integrity": "sha512-couX95waDu98NfNZV+i/iLt+fdVxmI7CbrrdC2uDWfPdUAApyxT4wmDlyOtR5KtTDmkDO0zDScDjDou9YHhd9g==", + "dependencies": [ + "@types/retry", + "retry" + ] + }, + "p-timeout@3.2.0": { + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "dependencies": [ + "p-finally" + ] + }, + "p-timeout@5.1.0": { + "integrity": "sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew==" + }, + "p-timeout@6.1.2": { + "integrity": "sha512-UbD77BuZ9Bc9aABo74gfXhNvzC9Tx7SxtHSh1fxvx3jTLLYvmVhiQZZrJzqqU0jKbN32kb5VOKiLEQI/3bIjgQ==" + }, + "p-wait-for@4.1.0": { + "integrity": "sha512-i8nE5q++9h8oaQHWltS1Tnnv4IoMDOlqN7C0KFG2OdbK0iFJIt6CROZ8wfBM+K4Pxqfnq4C4lkkpXqTEpB5DZw==", + "dependencies": [ + "p-timeout@5.1.0" + ] + }, + "p-wait-for@5.0.2": { + "integrity": "sha512-lwx6u1CotQYPVju77R+D0vFomni/AqRfqLmqQ8hekklqZ6gAY9rONh7lBQ0uxWMkC2AuX9b2DVAl8To0NyP1JA==", + "dependencies": [ + "p-timeout@6.1.2" + ] + }, + "package-json-from-dist@1.0.1": { + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" + }, + "package-json@8.1.1": { + "integrity": "sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==", + "dependencies": [ + "got", + "registry-auth-token", + "registry-url", + "semver@7.6.3" + ] + }, + "parallel-transform@1.2.0": { + "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", + "dependencies": [ + "cyclist", + "inherits", + "readable-stream@2.3.8" + ] + }, + "parent-module@1.0.1": { + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dependencies": [ + "callsites" + ] + }, + "parse-github-url@1.0.2": { + "integrity": "sha512-kgBf6avCbO3Cn6+RnzRGLkUsv4ZVqv/VfAYkRsyBcgkshNvVBkRn1FEZcW0Jb+npXQWm2vHPnnOqFteZxRRGNw==" + }, + "parse-gitignore@2.0.0": { + "integrity": "sha512-RmVuCHWsfu0QPNW+mraxh/xjQVw/lhUCUru8Zni3Ctq3AoMhpDTq0OVdKS6iesd6Kqb7viCV3isAL43dciOSog==" + }, + "parse-json@5.2.0": { + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dependencies": [ + "@babel/code-frame", + "error-ex", + "json-parse-even-better-errors", + "lines-and-columns" + ] + }, + "parse-ms@3.0.0": { + "integrity": "sha512-Tpb8Z7r7XbbtBTrM9UhpkzzaMrqA2VXMT3YChzYltwV3P3pM6t8wl7TvpMnSTosz1aQAdVib7kdoys7vYOPerw==" + }, + "parseurl@1.3.3": { + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "pascalcase@0.1.1": { + "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==" + }, + "path-exists@4.0.0": { + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + }, + "path-exists@5.0.0": { + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==" + }, + "path-is-absolute@1.0.1": { + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" + }, + "path-key@3.1.1": { + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "path-key@4.0.0": { + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==" + }, + "path-parse@1.0.7": { + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "path-scurry@1.11.1": { + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dependencies": [ + "lru-cache@10.4.3", + "minipass@7.1.2" + ] + }, + "path-to-regexp@0.1.7": { + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "path-type@4.0.0": { + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" + }, + "path-type@5.0.0": { + "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==" + }, + "pathe@1.1.2": { + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==" + }, + "pathval@2.0.0": { + "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==" + }, + "peek-readable@5.2.0": { + "integrity": "sha512-U94a+eXHzct7vAd19GH3UQ2dH4Satbng0MyYTMaQatL0pvYYL5CTPR25HBhKtecl+4bfu1/i3vC6k0hydO5Vcw==" + }, + "pend@1.2.0": { + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" + }, + "picocolors@1.1.0": { + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==" + }, + "picomatch@2.3.1": { + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + }, + "pino-abstract-transport@1.2.0": { + "integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==", + "dependencies": [ + "readable-stream@4.5.2", + "split2@4.2.0" + ] + }, + "pino-std-serializers@6.2.2": { + "integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==" + }, + "pino@8.21.0": { + "integrity": "sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==", + "dependencies": [ + "atomic-sleep", + "fast-redact", + "on-exit-leak-free", + "pino-abstract-transport", + "pino-std-serializers", + "process-warning@3.0.0", + "quick-format-unescaped", + "real-require", + "safe-stable-stringify", + "sonic-boom", + "thread-stream" + ] + }, + "pkg-dir@7.0.0": { + "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", + "dependencies": [ + "find-up@6.3.0" + ] + }, + "posix-character-classes@0.1.1": { + "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==" + }, + "possible-typed-array-names@1.0.0": { + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==" + }, + "postcss-values-parser@6.0.2_postcss@8.4.47": { + "integrity": "sha512-YLJpK0N1brcNJrs9WatuJFtHaV9q5aAOj+S4DI5S7jgHlRfm0PIbDCAFRYMQD5SHq7Fy6xsDhyutgS0QOAs0qw==", + "dependencies": [ + "color-name@1.1.4", + "is-url-superb", + "postcss", + "quote-unquote" + ] + }, + "postcss@8.4.47": { + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "dependencies": [ + "nanoid", + "picocolors", + "source-map-js" + ] + }, + "precinct@11.0.5": { + "integrity": "sha512-oHSWLC8cL/0znFhvln26D14KfCQFFn4KOLSw6hmLhd+LQ2SKt9Ljm89but76Pc7flM9Ty1TnXyrA2u16MfRV3w==", + "dependencies": [ + "@dependents/detective-less", + "commander@10.0.1", + "detective-amd", + "detective-cjs", + "detective-es6", + "detective-postcss", + "detective-sass", + "detective-scss", + "detective-stylus", + "detective-typescript", + "module-definition", + "node-source-walk" + ] + }, + "precond@0.2.3": { + "integrity": "sha512-QCYG84SgGyGzqJ/vlMsxeXd/pgL/I94ixdNFyh1PusWmTCyVfPJjZ1K1jvHtsbfnXQs2TSkEP2fR7QiMZAnKFQ==" + }, + "prelude-ls@1.2.1": { + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" + }, + "prettier-linter-helpers@1.0.0": { + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dependencies": [ + "fast-diff" + ] + }, + "prettier-plugin-jsdoc@1.3.0_prettier@3.2.5": { + "integrity": "sha512-cQm8xIa0fN9ieJFMXACQd6JPycl+8ouOijAqUqu44EF/s4fXL3Wi9sKXuEaodsEWgCN42Xby/bNhqgM1iWx4uw==", + "dependencies": [ + "binary-searching", + "comment-parser", + "mdast-util-from-markdown", + "prettier" + ] + }, + "prettier@3.2.5": { + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==" + }, + "pretty-format@27.5.1": { + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dependencies": [ + "ansi-regex@5.0.1", + "ansi-styles@5.2.0", + "react-is@17.0.2" + ] + }, + "pretty-ms@8.0.0": { + "integrity": "sha512-ASJqOugUF1bbzI35STMBUpZqdfYKlJugy6JBziGi2EE+AL5JPJGSzvpeVXojxrr0ViUYoToUjb5kjSEGf7Y83Q==", + "dependencies": [ + "parse-ms" + ] + }, + "prettyjson@1.2.5": { + "integrity": "sha512-rksPWtoZb2ZpT5OVgtmy0KHVM+Dca3iVwWY9ifwhcexfjebtgjg3wmrUt9PvJ59XIYBcknQeYHD8IAnVlh9lAw==", + "dependencies": [ + "colors", + "minimist" + ] + }, + "process-nextick-args@2.0.1": { + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "process-warning@2.3.2": { + "integrity": "sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA==" + }, + "process-warning@3.0.0": { + "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==" + }, + "process@0.10.1": { + "integrity": "sha512-dyIett8dgGIZ/TXKUzeYExt7WA6ldDzys9vTDU/cCA9L17Ypme+KzS+NjQCjpn9xsvi/shbMC+yP/BcFMBz0NA==" + }, + "process@0.11.10": { + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==" + }, + "prop-types@15.8.1": { + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": [ + "loose-envify", + "object-assign", + "react-is@16.13.1" + ] + }, + "proto-list@1.2.4": { + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==" + }, + "protobufjs@7.4.0": { + "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", + "dependencies": [ + "@protobufjs/aspromise", + "@protobufjs/base64", + "@protobufjs/codegen", + "@protobufjs/eventemitter", + "@protobufjs/fetch", + "@protobufjs/float", + "@protobufjs/inquire", + "@protobufjs/path", + "@protobufjs/pool", + "@protobufjs/utf8", + "@types/node@22.5.4", + "long@5.2.3" + ] + }, + "proxy-addr@2.0.7": { + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": [ + "forwarded", + "ipaddr.js" + ] + }, + "proxy-from-env@1.1.0": { + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "ps-list@8.1.1": { + "integrity": "sha512-OPS9kEJYVmiO48u/B9qneqhkMvgCxT+Tm28VCEJpheTpl8cJ0ffZRRNgS5mrQRTrX5yRTpaJ+hRDeefXYmmorQ==" + }, + "pump@1.0.3": { + "integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==", + "dependencies": [ + "end-of-stream", + "once" + ] + }, + "pump@3.0.0": { + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": [ + "end-of-stream", + "once" + ] + }, + "punycode@2.3.1": { + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==" + }, + "pupa@3.1.0": { + "integrity": "sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==", + "dependencies": [ + "escape-goat" + ] + }, + "qs@6.11.0": { + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": [ + "side-channel" + ] + }, + "queue-microtask@1.2.3": { + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" + }, + "queue-tick@1.0.1": { + "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==" + }, + "quick-format-unescaped@4.0.4": { + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" + }, + "quick-lru@5.1.1": { + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" + }, + "quote-unquote@1.0.0": { + "integrity": "sha512-twwRO/ilhlG/FIgYeKGFqyHhoEhqgnKVkcmqMKi2r524gz3ZbDTcyFt38E9xjJI2vT+KbRNHVbnJ/e0I25Azwg==" + }, + "random-bytes@1.0.0": { + "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==" + }, + "range-parser@1.2.1": { + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body@2.5.1": { + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dependencies": [ + "bytes", + "http-errors@2.0.0", + "iconv-lite", + "unpipe" + ] + }, + "raw-body@2.5.2": { + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": [ + "bytes", + "http-errors@2.0.0", + "iconv-lite", + "unpipe" + ] + }, + "rc@1.2.8": { + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dependencies": [ + "deep-extend", + "ini@1.3.8", + "minimist", + "strip-json-comments@2.0.1" + ] + }, + "react-is@16.13.1": { + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "react-is@17.0.2": { + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, + "read-pkg-up@9.1.0": { + "integrity": "sha512-vaMRR1AC1nrd5CQM0PhlRsO5oc2AAigqr7cCrZ/MW/Rsaflz4RlgzkpL4qoU/z1F6wrbd85iFv1OQj/y5RdGvg==", + "dependencies": [ + "find-up@6.3.0", + "read-pkg", + "type-fest@2.19.0" + ] + }, + "read-pkg@7.1.0": { + "integrity": "sha512-5iOehe+WF75IccPc30bWTbpdDQLOCc3Uu8bi3Dte3Eueij81yx1Mrufk8qBx/YAbR4uL1FdUr+7BKXDwEtisXg==", + "dependencies": [ + "@types/normalize-package-data", + "normalize-package-data", + "parse-json", + "type-fest@2.19.0" + ] + }, + "readable-stream@2.3.8": { + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": [ + "core-util-is", + "inherits", + "isarray@1.0.0", + "process-nextick-args", + "safe-buffer@5.1.2", + "string_decoder@1.1.1", + "util-deprecate" + ] + }, + "readable-stream@3.6.2": { + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": [ + "inherits", + "string_decoder@1.3.0", + "util-deprecate" + ] + }, + "readable-stream@4.5.2": { + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "dependencies": [ + "abort-controller", + "buffer@6.0.3", + "events", + "process@0.11.10", + "string_decoder@1.3.0" + ] + }, + "readable-web-to-node-stream@3.0.2": { + "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", + "dependencies": [ + "readable-stream@3.6.2" + ] + }, + "readdir-glob@1.1.3": { + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "dependencies": [ + "minimatch@5.1.6" + ] + }, + "readdirp@2.2.1": { + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dependencies": [ + "graceful-fs@4.2.11", + "micromatch@3.1.10", + "readable-stream@2.3.8" + ] + }, + "readdirp@3.6.0": { + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": [ + "picomatch" + ] + }, + "real-require@0.2.0": { + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==" + }, + "reflect.getprototypeof@1.0.6": { + "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==", + "dependencies": [ + "call-bind", + "define-properties", + "es-abstract", + "es-errors", + "get-intrinsic", + "globalthis", + "which-builtin-type" + ] + }, + "regex-not@1.0.2": { + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dependencies": [ + "extend-shallow@3.0.2", + "safe-regex" + ] + }, + "regexp-tree@0.1.27": { + "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==" + }, + "regexp.prototype.flags@1.5.3": { + "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==", + "dependencies": [ + "call-bind", + "define-properties", + "es-errors", + "set-function-name" + ] + }, + "registry-auth-token@5.0.2": { + "integrity": "sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==", + "dependencies": [ + "@pnpm/npm-conf" + ] + }, + "registry-url@6.0.1": { + "integrity": "sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==", + "dependencies": [ + "rc" + ] + }, + "remove-trailing-separator@1.1.0": { + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==" + }, + "repeat-element@1.1.4": { + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==" + }, + "repeat-string@1.6.1": { + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==" + }, + "require-directory@2.1.1": { + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" + }, + "require-from-string@2.0.2": { + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + }, + "require-in-the-middle@6.0.0": { + "integrity": "sha512-+dtWQ7l2lqQDxheaG3jjyN1QI37gEwvzACSgjYi4/C2y+ZTUMeRW8BIOm+9NBKvwaMBUSZfPXVOt1skB0vBkRw==", + "dependencies": [ + "debug@4.3.7", + "module-details-from-path", + "resolve@1.22.8" + ] + }, + "require-package-name@2.0.1": { + "integrity": "sha512-uuoJ1hU/k6M0779t3VMVIYpb2VMJk05cehCaABFhXaibcbvfgR8wKiozLjVFSzJPmQMRqIcO0HMyTFqfV09V6Q==" + }, + "requires-port@1.0.0": { + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, + "resolve-alpn@1.2.1": { + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" + }, + "resolve-from@4.0.0": { + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" + }, + "resolve-from@5.0.0": { + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" + }, + "resolve-pkg-maps@1.0.0": { + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==" + }, + "resolve-url@0.2.1": { + "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==" + }, + "resolve@1.22.8": { + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dependencies": [ + "is-core-module", + "path-parse", + "supports-preserve-symlinks-flag" + ] + }, + "resolve@2.0.0-next.5": { + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "dependencies": [ + "is-core-module", + "path-parse", + "supports-preserve-symlinks-flag" + ] + }, + "responselike@3.0.0": { + "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", + "dependencies": [ + "lowercase-keys" + ] + }, + "restore-cursor@2.0.0": { + "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", + "dependencies": [ + "onetime@2.0.1", + "signal-exit@3.0.7" + ] + }, + "restore-cursor@4.0.0": { + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "dependencies": [ + "onetime@5.1.2", + "signal-exit@3.0.7" + ] + }, + "ret@0.1.15": { + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" + }, + "ret@0.2.2": { + "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==" + }, + "retry@0.13.1": { + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==" + }, + "reusify@1.0.4": { + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" + }, + "rfdc@1.4.1": { + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==" + }, + "rimraf@3.0.2": { + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dependencies": [ + "glob@7.2.3" + ] + }, + "rollup@4.24.0": { + "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==", + "dependencies": [ + "@rollup/rollup-android-arm-eabi", + "@rollup/rollup-android-arm64", + "@rollup/rollup-darwin-arm64", + "@rollup/rollup-darwin-x64", + "@rollup/rollup-linux-arm-gnueabihf", + "@rollup/rollup-linux-arm-musleabihf", + "@rollup/rollup-linux-arm64-gnu", + "@rollup/rollup-linux-arm64-musl", + "@rollup/rollup-linux-powerpc64le-gnu", + "@rollup/rollup-linux-riscv64-gnu", + "@rollup/rollup-linux-s390x-gnu", + "@rollup/rollup-linux-x64-gnu", + "@rollup/rollup-linux-x64-musl", + "@rollup/rollup-win32-arm64-msvc", + "@rollup/rollup-win32-ia32-msvc", + "@rollup/rollup-win32-x64-msvc", + "@types/estree", + "fsevents" + ] + }, + "run-async@2.4.1": { + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==" + }, + "run-parallel@1.2.0": { + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dependencies": [ + "queue-microtask" + ] + }, + "rxjs@6.6.7": { + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dependencies": [ + "tslib@1.14.1" + ] + }, + "safe-array-concat@1.1.2": { + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "dependencies": [ + "call-bind", + "get-intrinsic", + "has-symbols", + "isarray@2.0.5" + ] + }, + "safe-buffer@5.1.2": { + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safe-buffer@5.2.1": { + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safe-json-stringify@1.2.0": { + "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==" + }, + "safe-regex-test@1.0.3": { + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "dependencies": [ + "call-bind", + "es-errors", + "is-regex" + ] + }, + "safe-regex2@2.0.0": { + "integrity": "sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==", + "dependencies": [ + "ret@0.2.2" + ] + }, + "safe-regex@1.1.0": { + "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==", + "dependencies": [ + "ret@0.1.15" + ] + }, + "safe-stable-stringify@2.5.0": { + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==" + }, + "safer-buffer@2.1.2": { + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "secure-json-parse@2.7.0": { + "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==" + }, + "seek-bzip@1.0.6": { + "integrity": "sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==", + "dependencies": [ + "commander@2.20.3" + ] + }, + "semver-diff@4.0.0": { + "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", + "dependencies": [ + "semver@7.6.3" + ] + }, + "semver@6.3.1": { + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + }, + "semver@7.5.4": { + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": [ + "lru-cache@6.0.0" + ] + }, + "semver@7.6.3": { + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==" + }, + "send@0.18.0": { + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": [ + "debug@2.6.9", + "depd@2.0.0", + "destroy", + "encodeurl", + "escape-html", + "etag", + "fresh", + "http-errors@2.0.0", + "mime@1.6.0", + "ms@2.1.3", + "on-finished", + "range-parser", + "statuses@2.0.1" + ] + }, + "serve-static@1.15.0": { + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": [ + "encodeurl", + "escape-html", + "parseurl", + "send" + ] + }, + "set-blocking@2.0.0": { + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, + "set-cookie-parser@2.7.0": { + "integrity": "sha512-lXLOiqpkUumhRdFF3k1osNXCy9akgx/dyPZ5p8qAg9seJzXr5ZrlqZuWIMuY6ejOsVLE6flJ5/h3lsn57fQ/PQ==" + }, + "set-function-length@1.2.2": { + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dependencies": [ + "define-data-property", + "es-errors", + "function-bind", + "get-intrinsic", + "gopd", + "has-property-descriptors" + ] + }, + "set-function-name@2.0.2": { + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dependencies": [ + "define-data-property", + "es-errors", + "functions-have-names", + "has-property-descriptors" + ] + }, + "set-value@2.0.1": { + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dependencies": [ + "extend-shallow@2.0.1", + "is-extendable@0.1.1", + "is-plain-object@2.0.4", + "split-string" + ] + }, + "setprototypeof@1.2.0": { + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "shebang-command@2.0.0": { + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": [ + "shebang-regex" + ] + }, + "shebang-regex@3.0.0": { + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "shiki@0.14.7": { + "integrity": "sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==", + "dependencies": [ + "ansi-sequence-parser", + "jsonc-parser", + "vscode-oniguruma", + "vscode-textmate" + ] + }, + "shimmer@1.2.1": { + "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" + }, + "side-channel@1.0.6": { + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dependencies": [ + "call-bind", + "es-errors", + "get-intrinsic", + "object-inspect" + ] + }, + "siginfo@2.0.0": { + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==" + }, + "signal-exit@3.0.7": { + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "signal-exit@4.1.0": { + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==" + }, + "simple-swizzle@0.2.2": { + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": [ + "is-arrayish@0.3.2" + ] + }, + "slash@3.0.0": { + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" + }, + "slash@4.0.0": { + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==" + }, + "slice-ansi@0.0.4": { + "integrity": "sha512-up04hB2hR92PgjpyU3y/eg91yIBILyjVY26NvvciY3EVVPjybkMszMpXQ9QAkcS3I5rtJBDLoTxxg+qvW8c7rw==" + }, + "slice-ansi@5.0.0": { + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dependencies": [ + "ansi-styles@6.2.1", + "is-fullwidth-code-point@4.0.0" + ] + }, + "snapdragon-node@2.1.1": { + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dependencies": [ + "define-property@1.0.0", + "isobject@3.0.1", + "snapdragon-util" + ] + }, + "snapdragon-util@3.0.1": { + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dependencies": [ + "kind-of@3.2.2" + ] + }, + "snapdragon@0.8.2": { + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dependencies": [ + "base", + "debug@2.6.9", + "define-property@0.2.5", + "extend-shallow@2.0.1", + "map-cache", + "source-map@0.5.7", + "source-map-resolve", + "use" + ] + }, + "sonic-boom@3.8.1": { + "integrity": "sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==", + "dependencies": [ + "atomic-sleep" + ] + }, + "sort-keys-length@1.0.1": { + "integrity": "sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==", + "dependencies": [ + "sort-keys" + ] + }, + "sort-keys@1.1.2": { + "integrity": "sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==", + "dependencies": [ + "is-plain-obj@1.1.0" + ] + }, + "source-map-js@1.2.1": { + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==" + }, + "source-map-resolve@0.5.3": { + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dependencies": [ + "atob", + "decode-uri-component", + "resolve-url", + "source-map-url", + "urix" + ] + }, + "source-map-support@0.5.21": { + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dependencies": [ + "buffer-from", + "source-map@0.6.1" + ] + }, + "source-map-url@0.4.1": { + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==" + }, + "source-map@0.5.7": { + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==" + }, + "source-map@0.6.1": { + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "spdx-correct@3.2.0": { + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dependencies": [ + "spdx-expression-parse", + "spdx-license-ids" + ] + }, + "spdx-exceptions@2.5.0": { + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==" + }, + "spdx-expression-parse@3.0.1": { + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dependencies": [ + "spdx-exceptions", + "spdx-license-ids" + ] + }, + "spdx-license-ids@3.0.20": { + "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==" + }, + "split-string@3.1.0": { + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dependencies": [ + "extend-shallow@3.0.2" + ] + }, + "split2@1.1.1": { + "integrity": "sha512-cfurE2q8LamExY+lJ9Ex3ZfBwqAPduzOKVscPDXNCLLMvyaeD3DTz1yk7fVIs6Chco+12XeD0BB6HEoYzPYbXA==", + "dependencies": [ + "through2" + ] + }, + "split2@4.2.0": { + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==" + }, + "stack-generator@2.0.10": { + "integrity": "sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==", + "dependencies": [ + "stackframe" + ] + }, + "stack-trace@0.0.10": { + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==" + }, + "stackback@0.0.2": { + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==" + }, + "stackframe@1.3.4": { + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==" + }, + "static-extend@0.1.2": { + "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==", + "dependencies": [ + "define-property@0.2.5", + "object-copy" + ] + }, + "statuses@1.5.0": { + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==" + }, + "statuses@2.0.1": { + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + }, + "std-env@3.7.0": { + "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==" + }, + "stdin-discarder@0.1.0": { + "integrity": "sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==", + "dependencies": [ + "bl@5.1.0" + ] + }, + "stop-iteration-iterator@1.0.0": { + "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", + "dependencies": [ + "internal-slot" + ] + }, + "streamx@2.20.1": { + "integrity": "sha512-uTa0mU6WUC65iUvzKH4X9hEdvSW7rbPxPtwfWiLMSj3qTdQbAiUboZTxauKfpFuGIGa1C2BYijZ7wgdUXICJhA==", + "dependencies": [ + "bare-events", + "fast-fifo", + "queue-tick", + "text-decoder" + ] + }, + "string-template@0.2.1": { + "integrity": "sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw==" + }, + "string-width@1.0.2": { + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", + "dependencies": [ + "code-point-at", + "is-fullwidth-code-point@1.0.0", + "strip-ansi@3.0.1" + ] + }, + "string-width@2.1.1": { + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dependencies": [ + "is-fullwidth-code-point@2.0.0", + "strip-ansi@4.0.0" + ] + }, + "string-width@4.2.3": { + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": [ + "emoji-regex@8.0.0", + "is-fullwidth-code-point@3.0.0", + "strip-ansi@6.0.1" + ] + }, + "string-width@5.1.2": { + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dependencies": [ + "eastasianwidth", + "emoji-regex@9.2.2", + "strip-ansi@7.1.0" + ] + }, + "string.prototype.includes@2.0.0": { + "integrity": "sha512-E34CkBgyeqNDcrbU76cDjL5JLcVrtSdYq0MEh/B10r17pRP4ciHLwTgnuLV8Ay6cgEMLkcBkFCKyFZ43YldYzg==", + "dependencies": [ + "define-properties", + "es-abstract" + ] + }, + "string.prototype.matchall@4.0.11": { + "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", + "dependencies": [ + "call-bind", + "define-properties", + "es-abstract", + "es-errors", + "es-object-atoms", + "get-intrinsic", + "gopd", + "has-symbols", + "internal-slot", + "regexp.prototype.flags", + "set-function-name", + "side-channel" + ] + }, + "string.prototype.repeat@1.0.0": { + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dependencies": [ + "define-properties", + "es-abstract" + ] + }, + "string.prototype.trim@1.2.9": { + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "dependencies": [ + "call-bind", + "define-properties", + "es-abstract", + "es-object-atoms" + ] + }, + "string.prototype.trimend@1.0.8": { + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "dependencies": [ + "call-bind", + "define-properties", + "es-object-atoms" + ] + }, + "string.prototype.trimstart@1.0.8": { + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dependencies": [ + "call-bind", + "define-properties", + "es-object-atoms" + ] + }, + "string_decoder@1.1.1": { + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": [ + "safe-buffer@5.1.2" + ] + }, + "string_decoder@1.3.0": { + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": [ + "safe-buffer@5.2.1" + ] + }, + "strip-ansi-control-characters@2.0.0": { + "integrity": "sha512-Q0/k5orrVGeaOlIOUn1gybGU0IcAbgHQT1faLo5hik4DqClKVSaka5xOhNNoRgtfztHVxCYxi7j71mrWom0bIw==" + }, + "strip-ansi@3.0.1": { + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dependencies": [ + "ansi-regex@2.1.1" + ] + }, + "strip-ansi@4.0.0": { + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dependencies": [ + "ansi-regex@3.0.1" + ] + }, + "strip-ansi@5.2.0": { + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dependencies": [ + "ansi-regex@4.1.1" + ] + }, + "strip-ansi@6.0.1": { + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": [ + "ansi-regex@5.0.1" + ] + }, + "strip-ansi@7.1.0": { + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": [ + "ansi-regex@6.1.0" + ] + }, + "strip-bom@3.0.0": { + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==" + }, + "strip-dirs@3.0.0": { + "integrity": "sha512-I0sdgcFTfKQlUPZyAqPJmSG3HLO9rWDFnxonnIbskYNM3DwFOeTNB5KzVq3dA1GdRAc/25b5Y7UO2TQfKWw4aQ==", + "dependencies": [ + "inspect-with-kind", + "is-plain-obj@1.1.0" + ] + }, + "strip-final-newline@2.0.0": { + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" + }, + "strip-final-newline@3.0.0": { + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==" + }, + "strip-json-comments@2.0.1": { + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==" + }, + "strip-json-comments@3.1.1": { + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" + }, + "strip-outer@2.0.0": { + "integrity": "sha512-A21Xsm1XzUkK0qK1ZrytDUvqsQWict2Cykhvi0fBQntGG5JSprESasEyV1EZ/4CiR5WB5KjzLTrP/bO37B0wPg==" + }, + "strtok3@7.1.1": { + "integrity": "sha512-mKX8HA/cdBqMKUr0MMZAFssCkIGoZeSCMXgnt79yKxNFguMLVFgRe6wB+fsL0NmoHDbeyZXczy7vEPSoo3rkzg==", + "dependencies": [ + "@tokenizer/token", + "peek-readable" + ] + }, + "supports-color@2.0.0": { + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==" + }, + "supports-color@5.5.0": { + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": [ + "has-flag@3.0.0" + ] + }, + "supports-color@7.2.0": { + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": [ + "has-flag@4.0.0" + ] + }, + "supports-color@9.4.0": { + "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==" + }, + "supports-hyperlinks@2.3.0": { + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "dependencies": [ + "has-flag@4.0.0", + "supports-color@7.2.0" + ] + }, + "supports-preserve-symlinks-flag@1.0.0": { + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + }, + "symbol-observable@1.2.0": { + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" + }, + "synckit@0.9.1": { + "integrity": "sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==", + "dependencies": [ + "@pkgr/core", + "tslib@2.7.0" + ] + }, + "tabtab@3.0.2": { + "integrity": "sha512-jANKmUe0sIQc/zTALTBy186PoM/k6aPrh3A7p6AaAfF6WPSbTx1JYeGIGH162btpH+mmVEXln+UxwViZHO2Jhg==", + "dependencies": [ + "debug@4.3.7", + "es6-promisify", + "inquirer", + "minimist", + "mkdirp@0.5.6", + "untildify" + ] + }, + "tar-stream@2.2.0": { + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dependencies": [ + "bl@4.1.0", + "end-of-stream", + "fs-constants", + "inherits", + "readable-stream@3.6.2" + ] + }, + "tar-stream@3.1.7": { + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "dependencies": [ + "b4a", + "fast-fifo", + "streamx" + ] + }, + "tar@6.2.1": { + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "dependencies": [ + "chownr", + "fs-minipass", + "minipass@5.0.0", + "minizlib", + "mkdirp@1.0.4", + "yallist" + ] + }, + "temp-dir@2.0.0": { + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==" + }, + "tempy@3.0.0": { + "integrity": "sha512-B2I9X7+o2wOaW4r/CWMkpOO9mdiTRCxXNgob6iGvPmfPWgH/KyUD6Uy5crtWBxIBe3YrNZKR2lSzv1JJKWD4vA==", + "dependencies": [ + "is-stream@3.0.0", + "temp-dir", + "type-fest@2.19.0", + "unique-string" + ] + }, + "terminal-link@3.0.0": { + "integrity": "sha512-flFL3m4wuixmf6IfhFJd1YPiLiMuxEc8uHRM1buzIeZPm22Au2pDqBJQgdo7n1WfPU1ONFGv7YDwpFBmHGF6lg==", + "dependencies": [ + "ansi-escapes@5.0.0", + "supports-hyperlinks" + ] + }, + "text-decoder@1.2.0": { + "integrity": "sha512-n1yg1mOj9DNpk3NeZOx7T6jchTbyJS3i3cucbNN6FcdPriMZx7NsgrGpWWdWZZGxD7ES1XB+3uoqHMgOKaN+fg==", + "dependencies": [ + "b4a" + ] + }, + "text-hex@1.0.0": { + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + }, + "text-table@0.2.0": { + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" + }, + "thread-stream@2.7.0": { + "integrity": "sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==", + "dependencies": [ + "real-require" + ] + }, + "thriftrw@3.11.4": { + "integrity": "sha512-UcuBd3eanB3T10nXWRRMwfwoaC6VMk7qe3/5YIWP2Jtw+EbHqJ0p1/K3x8ixiR5dozKSSfcg1W+0e33G1Di3XA==", + "dependencies": [ + "bufrw", + "error", + "long@2.4.0" + ] + }, + "through2-filter@3.0.0": { + "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", + "dependencies": [ + "through2", + "xtend" + ] + }, + "through2-map@3.0.0": { + "integrity": "sha512-Ms68QPbSJKjRYY7fmqZHB0VGt+vD0/tjmDHUWgxltjifCof6hZWWeQAEi27Wjbs7jyNlIIyerQw/TVj7gHkd/Q==", + "dependencies": [ + "through2", + "xtend" + ] + }, + "through2@2.0.5": { + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dependencies": [ + "readable-stream@2.3.8", + "xtend" + ] + }, + "through@2.3.8": { + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" + }, + "time-zone@1.0.0": { + "integrity": "sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA==" + }, + "tiny-lru@11.2.11": { + "integrity": "sha512-27BIW0dIWTYYoWNnqSmoNMKe5WIbkXsc0xaCQHd3/3xT2XMuMJrzHdrO9QBFR14emBz1Bu0dOAs2sCBBrvgPQA==" + }, + "tinybench@2.9.0": { + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==" + }, + "tinyexec@0.3.0": { + "integrity": "sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==" + }, + "tinypool@1.0.1": { + "integrity": "sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==" + }, + "tinyrainbow@1.2.0": { + "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==" + }, + "tinyspy@3.0.2": { + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==" + }, + "tmp-promise@3.0.3": { + "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", + "dependencies": [ + "tmp@0.2.3" + ] + }, + "tmp@0.0.33": { + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dependencies": [ + "os-tmpdir" + ] + }, + "tmp@0.2.3": { + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==" + }, + "to-fast-properties@2.0.0": { + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==" + }, + "to-object-path@0.3.0": { + "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==", + "dependencies": [ + "kind-of@3.2.2" + ] + }, + "to-readable-stream@3.0.0": { + "integrity": "sha512-vD2LytT6DxPynBa1xbMtswY9gGqj27wNbh2uvI5OhBe+mrGLurRWRQZyQn3812sqlQRtUJwaKVshG+PoGwbPDQ==" + }, + "to-regex-range@2.1.1": { + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dependencies": [ + "is-number@3.0.0", + "repeat-string" + ] + }, + "to-regex-range@5.0.1": { + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": [ + "is-number@7.0.0" + ] + }, + "to-regex@3.0.2": { + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dependencies": [ + "define-property@2.0.2", + "extend-shallow@3.0.2", + "regex-not", + "safe-regex" + ] + }, + "toidentifier@1.0.1": { + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + }, + "token-types@5.0.1": { + "integrity": "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==", + "dependencies": [ + "@tokenizer/token", + "ieee754" + ] + }, + "toml@3.0.0": { + "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==" + }, + "tomlify-j0.4@3.0.0": { + "integrity": "sha512-2Ulkc8T7mXJ2l0W476YC/A209PR38Nw8PuaCNtk9uI3t1zzFdGQeWYGQvmj2PZkVvRC/Yoi4xQKMRnWc/N29tQ==" + }, + "tr46@0.0.3": { + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "trim-repeated@2.0.0": { + "integrity": "sha512-QUHBFTJGdOwmp0tbOG505xAgOp/YliZP/6UgafFXYZ26WT1bvQmSMJUvkeVSASuJJHbqsFbynTvkd5W8RBTipg==", + "dependencies": [ + "escape-string-regexp@5.0.0" + ] + }, + "triple-beam@1.4.1": { + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==" + }, + "ts-api-utils@1.3.0_typescript@5.6.2": { + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dependencies": [ + "typescript@5.6.2" + ] + }, + "ts-node@10.9.2_@types+node@22.5.4_typescript@5.6.2": { + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dependencies": [ + "@cspotcode/source-map-support", + "@tsconfig/node10", + "@tsconfig/node12", + "@tsconfig/node14", + "@tsconfig/node16", + "@types/node@22.5.4", + "acorn", + "acorn-walk", + "arg", + "create-require", + "diff", + "make-error", + "typescript@5.6.2", + "v8-compile-cache-lib", + "yn" + ] + }, + "tsconfig-paths@3.15.0": { + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dependencies": [ + "@types/json5", + "json5", + "minimist", + "strip-bom" + ] + }, + "tslib@1.14.1": { + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "tslib@2.7.0": { + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" + }, + "tsutils@3.21.0_typescript@5.6.2": { + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dependencies": [ + "tslib@1.14.1", + "typescript@5.6.2" + ] + }, + "type-check@0.4.0": { + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dependencies": [ + "prelude-ls" + ] + }, + "type-fest@0.20.2": { + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" + }, + "type-fest@0.21.3": { + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==" + }, + "type-fest@0.8.1": { + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" + }, + "type-fest@1.4.0": { + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==" + }, + "type-fest@2.19.0": { + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==" + }, + "type-fest@3.13.1": { + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==" + }, + "type-is@1.6.18": { + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": [ + "media-typer", + "mime-types" + ] + }, + "typed-array-buffer@1.0.2": { + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "dependencies": [ + "call-bind", + "es-errors", + "is-typed-array" + ] + }, + "typed-array-byte-length@1.0.1": { + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "dependencies": [ + "call-bind", + "for-each", + "gopd", + "has-proto", + "is-typed-array" + ] + }, + "typed-array-byte-offset@1.0.2": { + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "dependencies": [ + "available-typed-arrays", + "call-bind", + "for-each", + "gopd", + "has-proto", + "is-typed-array" + ] + }, + "typed-array-length@1.0.6": { + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "dependencies": [ + "call-bind", + "for-each", + "gopd", + "has-proto", + "is-typed-array", + "possible-typed-array-names" + ] + }, + "typedarray-to-buffer@3.1.5": { + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dependencies": [ + "is-typedarray" + ] + }, + "typedoc-plugin-missing-exports@2.3.0_typedoc@0.25.13__typescript@5.4.5": { + "integrity": "sha512-iI9ITNNLlbsLCBBeYDyu0Qqp3GN/9AGyWNKg8bctRXuZEPT7G1L+0+MNWG9MsHcf/BFmNbXL0nQ8mC/tXRicog==", + "dependencies": [ + "typedoc" + ] + }, + "typedoc@0.25.13_typescript@5.4.5": { + "integrity": "sha512-pQqiwiJ+Z4pigfOnnysObszLiU3mVLWAExSPf+Mu06G/qsc3wzbuM56SZQvONhHLncLUhYzOVkjFFpFfL5AzhQ==", + "dependencies": [ + "lunr", + "marked", + "minimatch@9.0.5", + "shiki", + "typescript@5.4.5" + ] + }, + "typescript@5.4.5": { + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==" + }, + "typescript@5.6.2": { + "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==" + }, + "uid-safe@2.1.5": { + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "dependencies": [ + "random-bytes" + ] + }, + "ulid@2.3.0": { + "integrity": "sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw==" + }, + "unbox-primitive@1.0.2": { + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dependencies": [ + "call-bind", + "has-bigints", + "has-symbols", + "which-boxed-primitive" + ] + }, + "unbzip2-stream@1.4.3": { + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "dependencies": [ + "buffer@5.7.1", + "through" + ] + }, + "undici-types@6.19.8": { + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" + }, + "union-value@1.0.1": { + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dependencies": [ + "arr-union", + "get-value", + "is-extendable@0.1.1", + "set-value" + ] + }, + "unique-string@3.0.0": { + "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", + "dependencies": [ + "crypto-random-string" + ] + }, + "unist-util-stringify-position@4.0.0": { + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dependencies": [ + "@types/unist" + ] + }, + "universal-user-agent@6.0.1": { + "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==" + }, + "unix-dgram@2.0.6": { + "integrity": "sha512-AURroAsb73BZ6CdAyMrTk/hYKNj3DuYYEuOaB8bYMOHGKupRNScw90Q5C71tWJc3uE7dIeXRyuwN0xLLq3vDTg==", + "dependencies": [ + "bindings", + "nan" + ] + }, + "unixify@1.0.0": { + "integrity": "sha512-6bc58dPYhCMHHuwxldQxO3RRNZ4eCogZ/st++0+fcC1nr0jiGUtAdBJ2qzmLQWSxbtz42pWt4QQMiZ9HvZf5cg==", + "dependencies": [ + "normalize-path@2.1.1" + ] + }, + "unpipe@1.0.0": { + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" + }, + "unset-value@1.0.0": { + "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==", + "dependencies": [ + "has-value@0.3.1", + "isobject@3.0.1" + ] + }, + "untildify@3.0.3": { + "integrity": "sha512-iSk/J8efr8uPT/Z4eSUywnqyrQU7DSdMfdqK4iWEaUVVmcP5JcnpRqmVMwcwcnmI1ATFNgC5V90u09tBynNFKA==" + }, + "update-notifier@6.0.2": { + "integrity": "sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==", + "dependencies": [ + "boxen", + "chalk@5.2.0", + "configstore", + "has-yarn", + "import-lazy", + "is-ci", + "is-installed-globally", + "is-npm", + "is-yarn-global", + "latest-version", + "pupa", + "semver@7.6.3", + "semver-diff", + "xdg-basedir" + ] + }, + "uri-js@4.4.1": { + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": [ + "punycode" + ] + }, + "urix@0.1.0": { + "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==" + }, + "urlpattern-polyfill@8.0.2": { + "integrity": "sha512-Qp95D4TPJl1kC9SKigDcqgyM2VDVO4RiJc2d4qe5GrYm+zbIQCWWKAFaJNQ4BhdFeDGwBmAxqJBwWSJDb9T3BQ==" + }, + "use@3.1.1": { + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" + }, + "util-deprecate@1.0.2": { + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "utils-merge@1.0.1": { + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" + }, + "uuid@8.3.2": { + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, + "uuid@9.0.0": { + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==" + }, + "v8-compile-cache-lib@3.0.1": { + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" + }, + "validate-npm-package-license@3.0.4": { + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dependencies": [ + "spdx-correct", + "spdx-expression-parse" + ] + }, + "validate-npm-package-name@4.0.0": { + "integrity": "sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q==", + "dependencies": [ + "builtins" + ] + }, + "vary@1.1.2": { + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" + }, + "vite-node@2.1.1_@types+node@20.16.10": { + "integrity": "sha512-N/mGckI1suG/5wQI35XeR9rsMsPqKXzq1CdUndzVstBj/HvyxxGctwnK6WX43NGt5L3Z5tcRf83g4TITKJhPrA==", + "dependencies": [ + "cac", + "debug@4.3.7", + "pathe", + "vite" + ] + }, + "vite@5.4.8_@types+node@20.16.10": { + "integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==", + "dependencies": [ + "@types/node@20.16.10", + "esbuild@0.21.5", + "fsevents", + "postcss", + "rollup" + ] + }, + "vitest@2.1.1_@types+node@20.16.10_@vitest+spy@2.1.1_vite@5.4.8__@types+node@20.16.10": { + "integrity": "sha512-97We7/VC0e9X5zBVkvt7SGQMGrRtn3KtySFQG5fpaMlS+l62eeXRQO633AYhSTC3z7IMebnPPNjGXVGNRFlxBA==", + "dependencies": [ + "@types/node@20.16.10", + "@vitest/expect", + "@vitest/mocker", + "@vitest/pretty-format@2.1.2", + "@vitest/runner", + "@vitest/snapshot", + "@vitest/spy", + "@vitest/utils", + "chai", + "debug@4.3.7", + "magic-string", + "pathe", + "std-env", + "tinybench", + "tinyexec", + "tinypool", + "tinyrainbow", + "vite", + "vite-node", + "why-is-node-running" + ] + }, + "vscode-oniguruma@1.7.0": { + "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==" + }, + "vscode-textmate@8.0.0": { + "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==" + }, + "wait-port@1.0.4": { + "integrity": "sha512-w8Ftna3h6XSFWWc2JC5gZEgp64nz8bnaTp5cvzbJSZ53j+omktWTDdwXxEF0jM8YveviLgFWvNGrSvRHnkyHyw==", + "dependencies": [ + "chalk@4.1.2", + "commander@9.5.0", + "debug@4.3.7" + ] + }, + "wcwidth@1.0.1": { + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dependencies": [ + "defaults" + ] + }, + "web-streams-polyfill@3.3.3": { + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==" + }, + "webidl-conversions@3.0.1": { + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "well-known-symbols@2.0.0": { + "integrity": "sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==" + }, + "whatwg-url@5.0.0": { + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": [ + "tr46", + "webidl-conversions" + ] + }, + "which-boxed-primitive@1.0.2": { + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dependencies": [ + "is-bigint", + "is-boolean-object", + "is-number-object", + "is-string", + "is-symbol" + ] + }, + "which-builtin-type@1.1.4": { + "integrity": "sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==", + "dependencies": [ + "function.prototype.name", + "has-tostringtag", + "is-async-function", + "is-date-object", + "is-finalizationregistry", + "is-generator-function", + "is-regex", + "is-weakref", + "isarray@2.0.5", + "which-boxed-primitive", + "which-collection", + "which-typed-array" + ] + }, + "which-collection@1.0.2": { + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dependencies": [ + "is-map", + "is-set", + "is-weakmap", + "is-weakset" + ] + }, + "which-typed-array@1.1.15": { + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "dependencies": [ + "available-typed-arrays", + "call-bind", + "for-each", + "gopd", + "has-tostringtag" + ] + }, + "which@2.0.2": { + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": [ + "isexe" + ] + }, + "why-is-node-running@2.3.0": { + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dependencies": [ + "siginfo", + "stackback" + ] + }, + "wide-align@1.1.5": { + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dependencies": [ + "string-width@4.2.3" + ] + }, + "widest-line@4.0.1": { + "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", + "dependencies": [ + "string-width@5.1.2" + ] + }, + "windows-release@5.1.1": { + "integrity": "sha512-NMD00arvqcq2nwqc5Q6KtrSRHK+fVD31erE5FEMahAw5PmVCgD7MUXodq3pdZSUkqA9Cda2iWx6s1XYwiJWRmw==", + "dependencies": [ + "execa@5.1.1" + ] + }, + "winston-transport@4.8.0": { + "integrity": "sha512-qxSTKswC6llEMZKgCQdaWgDuMJQnhuvF5f2Nk3SNXc4byfQ+voo2mX1Px9dkNOuR8p0KAjfPG29PuYUSIb+vSA==", + "dependencies": [ + "logform", + "readable-stream@4.5.2", + "triple-beam" + ] + }, + "winston@3.14.2": { + "integrity": "sha512-CO8cdpBB2yqzEf8v895L+GNKYJiEq8eKlHU38af3snQBQ+sdAIUepjMSguOIJC7ICbzm0ZI+Af2If4vIJrtmOg==", + "dependencies": [ + "@colors/colors@1.6.0", + "@dabh/diagnostics", + "async@3.2.6", + "is-stream@2.0.1", + "logform", + "one-time", + "readable-stream@3.6.2", + "safe-stable-stringify", + "stack-trace", + "triple-beam", + "winston-transport" + ] + }, + "winston@3.8.2": { + "integrity": "sha512-MsE1gRx1m5jdTTO9Ld/vND4krP2To+lgDoMEHGGa4HIlAUyXJtfc7CxQcGXVyz2IBpw5hbFkj2b/AtUdQwyRew==", + "dependencies": [ + "@colors/colors@1.5.0", + "@dabh/diagnostics", + "async@3.2.6", + "is-stream@2.0.1", + "logform", + "one-time", + "readable-stream@3.6.2", + "safe-stable-stringify", + "stack-trace", + "triple-beam", + "winston-transport" + ] + }, + "word-wrap@1.2.5": { + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==" + }, + "wrap-ansi@3.0.1": { + "integrity": "sha512-iXR3tDXpbnTpzjKSylUJRkLuOrEC7hwEB221cgn6wtF8wpmz28puFXAEfPT5zrjM3wahygB//VuWEr1vTkDcNQ==", + "dependencies": [ + "string-width@2.1.1", + "strip-ansi@4.0.0" + ] + }, + "wrap-ansi@7.0.0": { + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": [ + "ansi-styles@4.3.0", + "string-width@4.2.3", + "strip-ansi@6.0.1" + ] + }, + "wrap-ansi@8.1.0": { + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dependencies": [ + "ansi-styles@6.2.1", + "string-width@5.1.2", + "strip-ansi@7.1.0" + ] + }, + "wrappy@1.0.2": { + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "write-file-atomic@3.0.3": { + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dependencies": [ + "imurmurhash", + "is-typedarray", + "signal-exit@3.0.7", + "typedarray-to-buffer" + ] + }, + "write-file-atomic@4.0.2": { + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dependencies": [ + "imurmurhash", + "signal-exit@3.0.7" + ] + }, + "write-file-atomic@5.0.1": { + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dependencies": [ + "imurmurhash", + "signal-exit@4.1.0" + ] + }, + "xdg-basedir@5.1.0": { + "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==" + }, + "xorshift@1.2.0": { + "integrity": "sha512-iYgNnGyeeJ4t6U11NpA/QiKy+PXn5Aa3Azg5qkwIFz1tBLllQrjjsk9yzD7IAK0naNU4JxdeDgqW9ov4u/hc4g==" + }, + "xtend@4.0.2": { + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "y18n@5.0.8": { + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" + }, + "yallist@4.0.0": { + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "yaml@2.5.1": { + "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==" + }, + "yargs-parser@21.1.1": { + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==" + }, + "yargs@17.7.2": { + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": [ + "cliui", + "escalade", + "get-caller-file", + "require-directory", + "string-width@4.2.3", + "y18n", + "yargs-parser" + ] + }, + "yauzl@2.10.0": { + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dependencies": [ + "buffer-crc32@0.2.13", + "fd-slicer" + ] + }, + "yn@3.1.1": { + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==" + }, + "yocto-queue@0.1.0": { + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" + }, + "yocto-queue@1.1.1": { + "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==" + }, + "zip-stream@4.1.1": { + "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", + "dependencies": [ + "archiver-utils@3.0.4", + "compress-commons@4.1.2", + "readable-stream@3.6.2" + ] + }, + "zip-stream@6.0.1": { + "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==", + "dependencies": [ + "archiver-utils@5.0.2", + "compress-commons@6.0.2", + "readable-stream@4.5.2" + ] + }, + "zod@3.23.8": { + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==" + } + }, + "workspace": { + "packageJson": { + "dependencies": [ + "npm:@types/node@^20.11.5", + "npm:@typescript-eslint/eslint-plugin@^7.8.0", + "npm:@typescript-eslint/parser@^7.8.0", + "npm:eslint-config-prettier@^9.1.0", + "npm:eslint-plugin-import@^2.29.1", + "npm:eslint-plugin-jsx-a11y@^6.6.1", + "npm:eslint-plugin-prettier@^5.1.3", + "npm:eslint-plugin-react-hooks@^4.3.0", + "npm:eslint-plugin-react@^7.28.0", + "npm:eslint@^8.57.0", + "npm:husky@^8.0.3", + "npm:netlify-cli@16.2.0", + "npm:prettier-plugin-jsdoc@^1.3.0", + "npm:prettier@3.2.5", + "npm:typedoc-plugin-missing-exports@^2.1.0", + "npm:typedoc@~0.25.3", + "npm:typescript@^5.4.5", + "npm:vite@^5.0.12", + "npm:vitest@^2.0.5" + ] + } + } +} diff --git a/lib/src/db.rs b/lib/src/db.rs index 94d9760cb..ebb118e9a 100644 --- a/lib/src/db.rs +++ b/lib/src/db.rs @@ -213,6 +213,12 @@ impl Db { Ok(()) } + /// Closes the database. + pub fn flush(&self) -> AtomicResult<()> { + self.db.flush()?; + Ok(()) + } + /// Internal method for fetching Resource data. #[instrument(skip(self))] fn set_propvals(&self, subject: &str, propvals: &PropVals) -> AtomicResult<()> { diff --git a/lib/src/db/migrations.rs b/lib/src/db/migrations.rs index 92bdabdab..b572184ae 100644 --- a/lib/src/db/migrations.rs +++ b/lib/src/db/migrations.rs @@ -97,7 +97,7 @@ fn res_v1_to_v2(store: &Db) -> AtomicResult<()> { Ok(url) => url, Err(e) => { tracing::error!( - "Unable to parse subject URL of '{}', skipping: {}", + "Unable to parse subject URL of '{}', keeping original: {}", subject, e ); diff --git a/lib/src/db/query_index.rs b/lib/src/db/query_index.rs index 1bab4b761..9d9aea2b4 100644 --- a/lib/src/db/query_index.rs +++ b/lib/src/db/query_index.rs @@ -491,79 +491,95 @@ pub mod test { #[test] fn should_update_or_not() { - let store = &Db::init_temp("should_update_or_not").unwrap(); + let instant = std::time::Instant::now(); - let prop = urls::IS_A.to_string(); - let class = urls::AGENT; + { + let store = &Db::init_temp("should_update_or_not").unwrap(); - let qf_prop_val = QueryFilter { - property: Some(prop.clone()), - value: Some(Value::AtomicUrl(class.to_string())), - sort_by: None, - }; + let prop = urls::IS_A.to_string(); + let class = urls::AGENT; - let qf_prop = QueryFilter { - property: Some(prop.clone()), - value: None, - sort_by: None, - }; + let qf_prop_val = QueryFilter { + property: Some(prop.clone()), + value: Some(Value::AtomicUrl(class.to_string())), + sort_by: None, + }; - let qf_val = QueryFilter { - property: None, - value: Some(Value::AtomicUrl(class.to_string())), - sort_by: None, - }; + let qf_prop = QueryFilter { + property: Some(prop.clone()), + value: None, + sort_by: None, + }; - let resource_correct_class = Resource::new_instance(class, store).unwrap(); + let qf_val = QueryFilter { + property: None, + value: Some(Value::AtomicUrl(class.to_string())), + sort_by: None, + }; - let subject: String = "https://example.com/someAgent".into(); + let resource_correct_class = Resource::new_instance(class, store).unwrap(); - let index_atom = IndexAtom { - subject, - property: prop.clone(), - ref_value: class.to_string(), - sort_value: class.to_string(), - }; + let subject: String = "https://example.com/someAgent".into(); - // We should be able to find the resource by propval, val, and / or prop. - assert!(should_update_property(&qf_val, &index_atom, &resource_correct_class).is_some()); - assert!( - should_update_property(&qf_prop_val, &index_atom, &resource_correct_class,).is_some() - ); - assert!(should_update_property(&qf_prop, &index_atom, &resource_correct_class).is_some()); - - // Test when a different value is passed - let resource_wrong_class = Resource::new_instance(urls::PARAGRAPH, store).unwrap(); - assert!(should_update_property(&qf_prop, &index_atom, &resource_wrong_class).is_some()); - assert!(should_update_property(&qf_val, &index_atom, &resource_wrong_class).is_none()); - assert!(should_update_property(&qf_prop_val, &index_atom, &resource_wrong_class).is_none()); - - let qf_prop_val_sort = QueryFilter { - property: Some(prop.clone()), - value: Some(Value::AtomicUrl(class.to_string())), - sort_by: Some(urls::DESCRIPTION.to_string()), - }; - let qf_prop_sort = QueryFilter { - property: Some(prop.clone()), - value: None, - sort_by: Some(urls::DESCRIPTION.to_string()), - }; - let qf_val_sort = QueryFilter { - property: Some(prop), - value: Some(Value::AtomicUrl(class.to_string())), - sort_by: Some(urls::DESCRIPTION.to_string()), - }; + let index_atom = IndexAtom { + subject, + property: prop.clone(), + ref_value: class.to_string(), + sort_value: class.to_string(), + }; - // We should update with a sort_by attribute - assert!( - should_update_property(&qf_prop_val_sort, &index_atom, &resource_correct_class,) - .is_some() - ); - assert!( - should_update_property(&qf_prop_sort, &index_atom, &resource_correct_class,).is_some() - ); - assert!( - should_update_property(&qf_val_sort, &index_atom, &resource_correct_class,).is_some() - ); + // We should be able to find the resource by propval, val, and / or prop. + assert!( + should_update_property(&qf_val, &index_atom, &resource_correct_class).is_some() + ); + assert!( + should_update_property(&qf_prop_val, &index_atom, &resource_correct_class,) + .is_some() + ); + assert!( + should_update_property(&qf_prop, &index_atom, &resource_correct_class).is_some() + ); + + // Test when a different value is passed + let resource_wrong_class = Resource::new_instance(urls::PARAGRAPH, store).unwrap(); + assert!(should_update_property(&qf_prop, &index_atom, &resource_wrong_class).is_some()); + assert!(should_update_property(&qf_val, &index_atom, &resource_wrong_class).is_none()); + assert!( + should_update_property(&qf_prop_val, &index_atom, &resource_wrong_class).is_none() + ); + + let qf_prop_val_sort = QueryFilter { + property: Some(prop.clone()), + value: Some(Value::AtomicUrl(class.to_string())), + sort_by: Some(urls::DESCRIPTION.to_string()), + }; + let qf_prop_sort = QueryFilter { + property: Some(prop.clone()), + value: None, + sort_by: Some(urls::DESCRIPTION.to_string()), + }; + let qf_val_sort = QueryFilter { + property: Some(prop), + value: Some(Value::AtomicUrl(class.to_string())), + sort_by: Some(urls::DESCRIPTION.to_string()), + }; + + // We should update with a sort_by attribute + assert!(should_update_property( + &qf_prop_val_sort, + &index_atom, + &resource_correct_class, + ) + .is_some()); + assert!( + should_update_property(&qf_prop_sort, &index_atom, &resource_correct_class,) + .is_some() + ); + assert!( + should_update_property(&qf_val_sort, &index_atom, &resource_correct_class,) + .is_some() + ); + } + println!("Took {:?}", instant.elapsed()) } } diff --git a/lib/src/resources.rs b/lib/src/resources.rs index 1a751affc..b669689ea 100644 --- a/lib/src/resources.rs +++ b/lib/src/resources.rs @@ -697,7 +697,6 @@ mod test { validate_signature: true, validate_timestamp: true, validate_rights: false, - validate_subject_url_parent: true, validate_previous_commit: true, validate_for_agent: None, update_index: true, diff --git a/lib/src/serialize.rs b/lib/src/serialize.rs index f690ddd86..a171482f0 100644 --- a/lib/src/serialize.rs +++ b/lib/src/serialize.rs @@ -265,7 +265,7 @@ mod test { #[test] fn serialize_json_ad() { - let mut store = crate::Store::init().unwrap(); + let store = crate::Store::init().unwrap(); store.populate().unwrap(); let json = store .get_resource(crate::urls::AGENT) diff --git a/lib/src/store.rs b/lib/src/store.rs index cb7da5f04..4d32b86ca 100644 --- a/lib/src/store.rs +++ b/lib/src/store.rs @@ -265,7 +265,7 @@ mod test { use crate::{agents::ForAgent, urls, Value}; fn init_store() -> Store { - let mut store = Store::init().unwrap(); + let store = Store::init().unwrap(); store.populate().unwrap(); store } diff --git a/server/build.rs b/server/build.rs index b2dfe6bce..76f7e27f0 100644 --- a/server/build.rs +++ b/server/build.rs @@ -22,9 +22,6 @@ fn main() -> std::io::Result<()> { // return Ok(()); const BROWSER_ROOT: &str = "../browser/"; println!("cargo:rerun-if-changed={}", BROWSER_ROOT); - // Check if we're likely running in a check-like context - let opt_level = std::env::var("OPT_LEVEL").unwrap_or_else(|_| "0".to_string()); - let profile = std::env::var("PROFILE").unwrap_or_else(|_| "release".to_string()); let is_check_like = false; // profile == "debug" && opt_level == "0"; diff --git a/server/src/appstate.rs b/server/src/appstate.rs index 7c4666cd4..abd81155d 100644 --- a/server/src/appstate.rs +++ b/server/src/appstate.rs @@ -136,6 +136,7 @@ impl AppState { /// Cleanup code, writing buffers, committing changes, etc. fn exit(&self) -> AtomicServerResult<()> { self.search_state.writer.write()?.commit()?; + self.store.flush()?; Ok(()) } } diff --git a/server/src/commit_monitor.rs b/server/src/commit_monitor.rs index 27bf45569..1b41c9d49 100644 --- a/server/src/commit_monitor.rs +++ b/server/src/commit_monitor.rs @@ -160,7 +160,7 @@ impl CommitMonitor { impl Handler for CommitMonitor { type Result = (); - #[tracing::instrument(name = "handle_commit_message", skip_all, fields(subscriptions = &self.subscriptions.len(), s = %msg.commit_response.commit_resource.get_subject()))] + #[tracing::instrument(name = "handle_commit_message", level="debug", skip_all, fields(subscriptions = &self.subscriptions.len(), s = %msg.commit_response.commit_resource.get_subject()))] fn handle(&mut self, msg: CommitMessage, _: &mut Context) { // We have moved the logic to the `handle_internal` function for decent error handling match self.handle_internal(msg) { diff --git a/server/src/serve.rs b/server/src/serve.rs index 0fddb6613..7ce59e7bf 100644 --- a/server/src/serve.rs +++ b/server/src/serve.rs @@ -112,6 +112,7 @@ pub async fn serve(config: crate::config::Config) -> AtomicServerResult<()> { if let Some(guard) = tracing_chrome_flush_guard { guard.flush() } + drop(config); tracing::info!("Server stopped"); Ok(()) From 58cd95a514064d553610f771b6e491ea2ef24f90 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Tue, 4 Feb 2025 14:50:24 +0100 Subject: [PATCH 50/51] Simplified DB init --- lib/benches/benchmarks.rs | 2 +- lib/src/client/helpers.rs | 1 + lib/src/db.rs | 11 ++++++++--- lib/src/db/query_index.rs | 2 +- lib/src/db/test.rs | 30 ++++++++++++++++++++++-------- server/src/search.rs | 2 +- 6 files changed, 34 insertions(+), 14 deletions(-) diff --git a/lib/benches/benchmarks.rs b/lib/benches/benchmarks.rs index 2218d802a..524461c68 100644 --- a/lib/benches/benchmarks.rs +++ b/lib/benches/benchmarks.rs @@ -37,7 +37,7 @@ fn random_resource(atom: &Atom) -> Resource { } fn criterion_benchmark(c: &mut Criterion) { - let store = Db::init_temp("bench").unwrap(); + let store = Db::init_temp().unwrap(); c.bench_function("add_resource", |b| { b.iter(|| { diff --git a/lib/src/client/helpers.rs b/lib/src/client/helpers.rs index 8e65c0e74..ee7ec6a64 100644 --- a/lib/src/client/helpers.rs +++ b/lib/src/client/helpers.rs @@ -51,6 +51,7 @@ pub fn fetch_body( content_type: &str, client_agent: Option<&Agent>, ) -> AtomicResult { + println!("Fetching body from {}", url); if !url.starts_with("http") { return Err(format!("Could not fetch url '{}', must start with http.", url).into()); } diff --git a/lib/src/db.rs b/lib/src/db.rs index ebb118e9a..d2b264a0e 100644 --- a/lib/src/db.rs +++ b/lib/src/db.rs @@ -130,9 +130,9 @@ impl Db { /// Create a temporary Db in `.temp/db/{id}`. Useful for testing. /// Populates the database, creates a default agent, and sets the server_url to "http://localhost/". - pub fn init_temp(id: &str) -> AtomicResult { - let tmp_dir_path = format!(".temp/db/{}", id); - let _try_remove_existing = std::fs::remove_dir_all(&tmp_dir_path); + pub fn init_temp() -> AtomicResult { + let random_id = crate::utils::random_string(10); + let tmp_dir_path = format!(".temp/db/{}", random_id); let store = Db::init(std::path::Path::new(&tmp_dir_path), "https://localhost")?; let agent = store.create_agent(None)?; store.set_default_agent(agent); @@ -557,6 +557,11 @@ impl Db { impl Drop for Db { fn drop(&mut self) { + // Remove if it's a temporary test database + if cfg!(test) && self.path.to_string_lossy().contains(".temp/db/") { + let _ = std::fs::remove_dir_all(&self.path); + return; + } match self.db.flush() { Ok(..) => (), Err(e) => eprintln!("Failed to flush the database: {}", e), diff --git a/lib/src/db/query_index.rs b/lib/src/db/query_index.rs index 9d9aea2b4..0a65861af 100644 --- a/lib/src/db/query_index.rs +++ b/lib/src/db/query_index.rs @@ -494,7 +494,7 @@ pub mod test { let instant = std::time::Instant::now(); { - let store = &Db::init_temp("should_update_or_not").unwrap(); + let store = &Db::init_temp().unwrap(); let prop = urls::IS_A.to_string(); let class = urls::AGENT; diff --git a/lib/src/db/test.rs b/lib/src/db/test.rs index c8ba82997..0675b819e 100644 --- a/lib/src/db/test.rs +++ b/lib/src/db/test.rs @@ -9,7 +9,7 @@ use ntest::timeout; use lazy_static::lazy_static; // 1.4.0 use std::sync::Mutex; lazy_static! { - pub static ref DB: Mutex = Mutex::new(Db::init_temp("shared").unwrap()); + pub static ref DB: Mutex = Mutex::new(Db::init_temp().unwrap()); } #[test] @@ -59,7 +59,7 @@ fn basic() { #[test] fn populate_collections() { - let store = Db::init_temp("populate_collections").unwrap(); + let store = Db::init_temp().unwrap(); let subjects: Vec = store .all_resources(false) .map(|r| r.get_subject().into()) @@ -89,7 +89,7 @@ fn populate_collections() { /// Check if a resource is properly removed from the DB after a delete command. /// Also counts commits. fn destroy_resource_and_check_collection_and_commits() { - let store = Db::init_temp("counter").unwrap(); + let store = Db::init_temp().unwrap(); let agents_url = store.get_server_url().set_route(Routes::Agents).to_string(); let for_agent = &ForAgent::Public; let agents_collection_1 = store @@ -198,7 +198,7 @@ fn destroy_resource_and_check_collection_and_commits() { #[test] fn get_extended_resource_pagination() { - let store = Db::init_temp("get_extended_resource_pagination").unwrap(); + let store = Db::init_temp().unwrap(); let subject = format!( "{}collections/commits?current_page=2&page_size=99999", store.get_server_url() @@ -231,7 +231,7 @@ fn get_extended_resource_pagination() { fn queries() { // Re-using the same instance can cause issues with testing concurrently. // let store = &DB.lock().unwrap().clone(); - let store = &Db::init_temp("queries").unwrap(); + let store = &Db::init_temp().unwrap(); let demo_val = Value::Slug("myval".to_string()); let demo_reference = Value::AtomicUrl(urls::PARAGRAPH.into()); @@ -397,7 +397,7 @@ fn queries() { /// Check if `include_external` is respected. #[test] fn query_include_external() { - let store = &Db::init_temp("query_include_external").unwrap(); + let store = &Db::init_temp().unwrap(); let mut q = Query { property: Some(urls::DESCRIPTION.into()), @@ -423,9 +423,23 @@ fn query_include_external() { ); } +#[test] +fn drop_speed() { + let start = std::time::Instant::now(); + println!("Start: {:?}", start.elapsed()); + assert!( + std::time::Duration::from_secs(1) > start.elapsed(), + "initializing DB too slow" + ); + let store = Db::init_temp().unwrap(); + println!("Init db: {:?}", start.elapsed()); + drop(store); + println!("Drop db: {:?}", start.elapsed()); +} + #[test] fn test_db_resources_all() { - let store = &Db::init_temp("resources_all").unwrap(); + let store = &Db::init_temp().unwrap(); let res_no_include = store.all_resources(false).count(); let res_include = store.all_resources(true).count(); assert!( @@ -437,7 +451,7 @@ fn test_db_resources_all() { #[test] /// Changing these values actually correctly updates the index. fn index_invalidate_cache() { - let store = &Db::init_temp("invalidate_cache").unwrap(); + let store = &Db::init_temp().unwrap(); // Make sure to use Properties that are not in the default store diff --git a/server/src/search.rs b/server/src/search.rs index 0de28ca12..61ae75b65 100644 --- a/server/src/search.rs +++ b/server/src/search.rs @@ -250,7 +250,7 @@ mod tests { use super::resource_to_facet; #[test] fn facet_contains_subfacet() { - let store = atomic_lib::Db::init_temp("facet_contains").unwrap(); + let store = atomic_lib::Db::init_temp().unwrap(); let mut prev_subject: Option = None; let mut resources = Vec::new(); From 12556917712e1299bc573d8e82c2c0e0c0579226 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 13 Feb 2025 12:59:00 +0100 Subject: [PATCH 51/51] Remove duplicate email --- lib/src/email.rs | 2 +- server/src/appstate.rs | 9 --------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/lib/src/email.rs b/lib/src/email.rs index aa2d03149..a1e78fc0b 100644 --- a/lib/src/email.rs +++ b/lib/src/email.rs @@ -52,7 +52,7 @@ pub async fn get_smtp_client( .port(config.port) .connect() .await - .map_err(|e| format!("Error connecting to SMTP mail server: at {full_address}. Is it running? Error message: {e}"))?; + .map_err(|e| format!("Error connecting to SMTP mail server at {full_address}. Is it running? Error message: {e}"))?; Ok(connection) } diff --git a/server/src/appstate.rs b/server/src/appstate.rs index abd81155d..a6bc060e2 100644 --- a/server/src/appstate.rs +++ b/server/src/appstate.rs @@ -94,15 +94,6 @@ impl AppState { }; store.set_handle_commit(Box::new(send_commit)); - if let Some(host) = &config.opts.smpt_host { - store - .set_smtp_config(SmtpConfig { - host: host.clone(), - port: config.opts.smpt_port, - }) - .await?; - }; - // If the user changes their server_url, the drive will not exist. // In this situation, we should re-build a new drive from scratch. if should_initialize {