atagen

malice

public
Star0
Watch0

Commit dffd63b58af4

tarball

atagen <boss@atagen.co>2026-10-22 13:19 UTC

50 files changed+4787-1246
make progress
Modified file: .gitignore+1 additions−1 deletions
@@ -4,7 +4,7 @@
44 /web/dist
55 /web/dist-device
66 /web/src/worker/pkg
7-/web/.test-dist
7+/web/storybook-static
88 /result
99 /.direnv
1010 /.env
Added file: .mcp.json+13 additions
@@ -0,0 +1,13 @@
1+{
2+ "mcpServers": {
3+ "storybook": {
4+ "type": "http",
5+ "url": "http://localhost:6006/mcp"
6+ },
7+ "chrome-devtools": {
8+ "type": "stdio",
9+ "command": "npx",
10+ "args": ["-y", "chrome-devtools-mcp@latest", "--executablePath", "${CHROMIUM_PATH}"]
11+ }
12+ }
13+}
Modified file: Cargo.lock+15 additions−3 deletions
@@ -2071,9 +2071,9 @@ dependencies = [
20712071 "futures-util",
20722072 "hex",
20732073 "http-body-util",
2074- "listenfd",
20752074 "macltok",
20762075 "malice-core",
2076+ "malice-static",
20772077 "matrix-sdk",
20782078 "rand_core 0.6.4",
20792079 "reqwest",
@@ -2095,14 +2095,26 @@ name = "malice-core"
20952095 version = "0.1.0"
20962096 dependencies = [
20972097 "futures-util",
2098- "http",
20992098 "macltok",
2100- "ruma",
21012099 "serde",
21022100 "serde_json",
21032101 ]
21042102
21052103 [[package]]
2104+name = "malice-static"
2105+version = "0.1.0"
2106+dependencies = [
2107+ "axum",
2108+ "listenfd",
2109+ "reqwest",
2110+ "tempfile",
2111+ "tokio",
2112+ "tower-http",
2113+ "tracing",
2114+ "tracing-subscriber",
2115+]
2116+
2117+[[package]]
21062118 name = "maplit"
21072119 version = "1.0.2"
21082120 source = "registry+https://github.com/rust-lang/crates.io-index"
Modified file: Cargo.toml+1 additions−1 deletions
@@ -1,6 +1,6 @@
11 [workspace]
22 resolver = "3"
3-members = ["crates/malice-core", "crates/malice-server"]
3+members = ["crates/malice-core", "crates/malice-server", "crates/malice-static"]
44
55 [workspace.dependencies]
66 macltok = { path = "vendor/macltok" }
Added file: MACLTOK_PROTOCOL_LEAKS.md+103 additions
@@ -0,0 +1,103 @@
1+# macltok request, keep Matrix protocol knowledge out of hosts
2+
3+## Problem
4+
5+malice should know how to represent Matrix things (show a homeserver field, print ids as strings)
6+and nothing more. Today it carries protocol knowledge macltok should own, and some of it is written
7+twice, once in `malice-server` and once in `malice-worker`. Every other macltok host would have to
8+write the same code. Five requests, ordered by impact.
9+
10+## 1. Avatars as `MediaId`, not MXC URIs
11+
12+`RoomSummary::avatar` and the other `avatar` fields in `model.rs` are `Option<OwnedMxcUri>`, so a host
13+fetches them with `ClientHandle::mxc_media(&MxcUri, MediaSize)`. Attachments go through the opaque
14+`MediaId` and `ClientHandle::media`. Because of that split, malice validates raw `mxc://` strings
15+with `ruma::MxcUri::is_valid` and sends MXC URIs over its HTTP API to the browser and back.
16+
17+Request, register avatars like attachments and expose them as `Option<MediaId>`, or as
18+`MediaDescriptor` if the mimetype or size is known. Then hosts fetch every image through
19+`media(&MediaId, MediaSize)`. `mxc_media` can stay for hosts that want it, but no model field
20+should require it.
21+
22+`set_avatar(Option<OwnedMxcUri>)` is fine as it is if its input comes from an upload outcome
23+and not from the host.
24+
25+## 2. Soft-logout re-login decision
26+
27+malice runs the same flow in `malice-server/src/state.rs` (`login`) and
28+`malice-worker/src/lib.rs` (`Host::login`). It restores the store, and on
29+`Restore::SoftLoggedOut(soft)` checks whether the submitted username is that user. If so it calls
30+`soft.password_login(password)` to keep the device and its keys. Otherwise it does a fresh
31+`PasswordLogin::new(..)`. The username check is copied in both crates.
32+
33+```rust
34+fn username_matches(submitted: &str, user_id: &ruma::OwnedUserId) -> bool {
35+ let submitted = submitted.trim();
36+ submitted == user_id.as_str() || submitted == user_id.localpart()
37+}
38+```
39+
40+That check is Matrix knowledge (a localpart or a full MXID names the same account), and it is
41+only correct when the homeserver also matches.
42+
43+Request, either shape works.
44+
45+```rust
46+impl SoftLogout {
47+ /// whether a login form's homeserver and username name this soft-logged-out account
48+ pub fn matches(&self, homeserver: &str, username: &str) -> bool;
49+}
50+```
51+
52+Or have `Session::login(PasswordLogin, sessions)` resume a soft-logged-out record in `sessions`
53+by itself when it names the same account, so hosts never branch on it. The second shape is
54+preferred because both malice call sites would shrink to one call.
55+
56+## 3. Homeserver input validation, and a discovery bug it causes
57+
58+macltok builds the client with `server_name_or_homeserver_url(login.homeserver.trim())`, which
59+does `.well-known` discovery for a bare server name. malice runs `normalise_homeserver`
60+(`malice-core/src/login.rs`) first, and that prefixes `https://` onto anything without a scheme.
61+So a user typing `example.org`, whose homeserver is `matrix.example.org`, gets
62+`https://example.org` treated as a homeserver URL and discovery never runs. malice will drop its
63+normaliser once macltok covers what it checks.
64+
65+- reject empty input and input containing whitespace
66+- reject schemes other than http and https
67+- reject a userinfo part (`user@host`) and a missing host
68+- reject plain http unless the host opts in, and even then only for loopback hosts (`localhost`,
69+ `127.0.0.1`, `::1`)
70+
71+Suggested shape is a builder option plus a typed error.
72+
73+```rust
74+PasswordLogin::new(homeserver, username, password)
75+ .allow_insecure_loopback(true)
76+```
77+
78+with a distinct `AccountError` variant (or `ErrorKind`) for invalid homeserver input, so a host can
79+answer 400 with the message instead of a generic login failure. The same validation should apply
80+to the OAuth login path.
81+
82+## 4. `AccountError::Operation` hides the homeserver's answer
83+
84+`Operation` displays as `"{context} failed"` and the useful part (the homeserver's errcode and
85+message) is only in `source()`. malice has a `describe_error` helper that walks the source chain to
86+build a readable message. Every host would need it.
87+
88+Request, include the innermost meaningful cause in `Display`, or add an accessor such as
89+`AccountError::user_message() -> String` that renders the chain.
90+
91+## 5. Re-export `ruma`
92+
93+macltok's public API is typed with ruma (`OwnedRoomId`, `OwnedEventId`, `OwnedUserId`,
94+`OwnedRoomOrAliasId`, `OwnedMxcUri`), so hosts must depend on ruma at exactly macltok's version.
95+malice pins `ruma = "0.16"` in two places to match.
96+
97+Request, `pub use ruma;` in `lib.rs`, same as `imbl`. malice will then parse ids through
98+`macltok::ruma` and drop its own ruma dependency.
99+
100+## Not needed
101+
102+`VerificationAction` already derives `Deserialize` under the `serde` feature, so malice will drop its
103+hand-written string match on its own.
Modified file: README.md+23 additions−3 deletions
@@ -75,9 +75,13 @@ From the repository root:
7575
7676 ```sh
7777 nix develop
78-npm --prefix web ci
7978 ```
8079
80+The dev shell links `web/node_modules` from a nix build of `web/package-lock.json`,
81+so there is no install step. After changing dependencies in `web/package.json`,
82+refresh the lockfile with `npm --prefix web install --package-lock-only` and
83+re-enter the shell.
84+
8185 For live frontend changes, start these in separate terminals inside `nix develop`:
8286
8387 ```sh
@@ -102,15 +106,31 @@ points at another built frontend directory. Local session data defaults to
102106 `local-data/`; set `MALICE_DATA_DIR` to use another directory. Keep its
103107 `master.key` and session files private.
104108
109+### Storybook
110+
111+Component stories live next to their components as `web/src/**/*.stories.ts`.
112+Storybook runs inside `nix develop`:
113+
114+```sh
115+cd web
116+npm run storybook
117+npm run build-storybook
118+```
119+
120+Open `http://localhost:6006/`. While it runs, `@storybook/addon-mcp` serves an
121+MCP endpoint at `http://localhost:6006/mcp`, which the repository `.mcp.json`
122+registers for Claude Code in this project only.
123+
105124 ## Testing
106125
107-In `nix develop`, after `npm --prefix web ci`:
126+In `nix develop`:
108127
109128 ```sh
110129 cargo fmt -- --check
111130 cargo clippy --all-targets -- -D warnings
112131 cargo test --all-targets
113-npm --prefix web exec -- tsc --noEmit -p web/tsconfig.json
132+npm --prefix web run check
133+npm --prefix web test
114134 npm --prefix web run build
115135 ```
116136
Modified file: crates/malice-core/Cargo.toml−2 deletions
@@ -9,10 +9,8 @@ name = "malice_core"
99 [dependencies]
1010 macltok = { workspace = true, features = ["serde", "mirror"] }
1111 futures-util = { version = "0.3", default-features = false, features = ["std"] }
12-ruma = { workspace = true }
1312 serde = { workspace = true }
1413 serde_json = { workspace = true }
15-http = { workspace = true }
1614
1715 [lints]
1816 workspace = true
Modified file: crates/malice-core/src/error.rs+1 additions
@@ -3,6 +3,7 @@ use serde::Serialize;
33 #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
44 pub enum Failure {
55 BadRequest,
6+ Unauthorized,
67 NotFound,
78 Conflict,
89 Busy,
Modified file: crates/malice-core/src/login.rs+22 additions−61 deletions
@@ -1,6 +1,7 @@
1+use macltok::{AccountError, PasswordLogin};
12 use serde::Deserialize;
23
3-use crate::error::CoreError;
4+use crate::error::{CoreError, Failure};
45
56 #[derive(Debug, Deserialize)]
67 pub struct LoginInput {
@@ -13,8 +14,7 @@ pub struct LoginInput {
1314 }
1415
1516 impl LoginInput {
16- pub fn validate(self, allow_http: bool) -> Result<LoginInput, CoreError> {
17- let homeserver = normalise_homeserver(&self.homeserver, allow_http)?;
17+ pub fn into_login(self, allow_insecure_loopback: bool) -> Result<PasswordLogin, CoreError> {
1818 let username = self.username.trim();
1919 if username.is_empty() {
2020 return Err(CoreError::bad_request("username is required"));
@@ -22,70 +22,31 @@ impl LoginInput {
2222 if self.password.is_empty() {
2323 return Err(CoreError::bad_request("password is required"));
2424 }
25- Ok(LoginInput {
26- homeserver,
27- username: username.to_owned(),
28- password: self.password,
29- })
25+ Ok(
26+ PasswordLogin::new(self.homeserver.trim(), username, self.password)
27+ .with_device_display_name("malice web")
28+ .allow_insecure_loopback(allow_insecure_loopback),
29+ )
3030 }
3131 }
3232
33-pub fn normalise_homeserver(raw: &str, allow_http: bool) -> Result<String, CoreError> {
34- let trimmed = raw.trim();
35- if trimmed.is_empty() {
36- return Err(CoreError::bad_request("homeserver is required"));
37- }
38- if trimmed.contains(char::is_whitespace) {
39- return Err(CoreError::bad_request("homeserver must not contain spaces"));
40- }
41- let with_scheme = if trimmed.contains("://") {
42- trimmed.to_owned()
43- } else {
44- format!("https://{trimmed}")
33+pub fn login_error(error: &AccountError, password: &str) -> CoreError {
34+ let (kind, message) = match error {
35+ AccountError::InvalidHomeserver(_) => (Failure::BadRequest, error.to_string()),
36+ AccountError::AlreadyLoggedIn(_) => (Failure::Conflict, error.to_string()),
37+ AccountError::StoreInUse => (Failure::Busy, error.to_string()),
38+ error => match error.server() {
39+ Some(server) if matches!(server.status, 401 | 403) => {
40+ (Failure::Unauthorized, server.message.clone())
41+ }
42+ Some(server) => (Failure::Upstream, server.message.clone()),
43+ None => (Failure::Upstream, error.to_string()),
44+ },
4545 };
46- let uri: http::Uri = with_scheme
47- .parse()
48- .map_err(|_| CoreError::bad_request("invalid homeserver"))?;
49- let scheme = uri
50- .scheme_str()
51- .ok_or_else(|| CoreError::bad_request("invalid homeserver"))?;
52- if !scheme.eq_ignore_ascii_case("http") && !scheme.eq_ignore_ascii_case("https") {
53- return Err(CoreError::bad_request("homeserver must use http(s)"));
54- }
55- let authority = uri
56- .authority()
57- .ok_or_else(|| CoreError::bad_request("homeserver must include a host"))?;
58- if authority.as_str().contains('@') || authority.host().is_empty() {
59- return Err(CoreError::bad_request("homeserver must include a host"));
60- }
61- if scheme.eq_ignore_ascii_case("http") {
62- let host = authority.host().trim_end_matches('.');
63- let loopback = host.eq_ignore_ascii_case("localhost")
64- || host == "127.0.0.1"
65- || host == "[::1]"
66- || host == "::1";
67- if !(allow_http && loopback) {
68- return Err(CoreError::bad_request(
69- "insecure http homeservers are not allowed",
70- ));
71- }
72- }
73- Ok(with_scheme)
74-}
75-
76-/// macltok's `AccountError` displays only its context, the homeserver's answer is in the sources
77-pub fn describe_error(error: &(dyn std::error::Error + 'static)) -> String {
78- let mut message = error.to_string();
79- let mut source = error.source();
80- while let Some(cause) = source {
81- message.push_str(", ");
82- message.push_str(&cause.to_string());
83- source = cause.source();
84- }
85- message
46+ CoreError::new(kind, sanitise_auth_error(&message, password))
8647 }
8748
88-pub fn sanitise_auth_error(message: &str, password: &str) -> String {
49+fn sanitise_auth_error(message: &str, password: &str) -> String {
8950 let message = if password.is_empty() {
9051 message.to_owned()
9152 } else {
Modified file: crates/malice-core/src/parse.rs+8 additions−6 deletions
@@ -1,25 +1,27 @@
1+use macltok::ruma::{OwnedEventId, OwnedRoomId, OwnedRoomOrAliasId};
2+
13 use crate::error::CoreError;
24
3-pub fn parse_room_id(raw: &str) -> Result<ruma::OwnedRoomId, CoreError> {
5+pub fn parse_room_id(raw: &str) -> Result<OwnedRoomId, CoreError> {
46 let trimmed = raw.trim();
57 if trimmed.is_empty() {
68 return Err(CoreError::bad_request("room_id is required"));
79 }
8- ruma::OwnedRoomId::try_from(trimmed).map_err(|_| CoreError::bad_request("invalid room_id"))
10+ OwnedRoomId::try_from(trimmed).map_err(|_| CoreError::bad_request("invalid room_id"))
911 }
1012
11-pub fn parse_event_id(raw: &str) -> Result<ruma::OwnedEventId, CoreError> {
13+pub fn parse_event_id(raw: &str) -> Result<OwnedEventId, CoreError> {
1214 let trimmed = raw.trim();
1315 if trimmed.is_empty() {
1416 return Err(CoreError::bad_request("event_id is required"));
1517 }
16- ruma::OwnedEventId::try_from(trimmed).map_err(|_| CoreError::bad_request("invalid event_id"))
18+ OwnedEventId::try_from(trimmed).map_err(|_| CoreError::bad_request("invalid event_id"))
1719 }
1820
19-pub fn parse_room_or_alias(raw: &str) -> Result<ruma::OwnedRoomOrAliasId, CoreError> {
21+pub fn parse_room_or_alias(raw: &str) -> Result<OwnedRoomOrAliasId, CoreError> {
2022 let trimmed = raw.trim();
2123 if trimmed.is_empty() {
2224 return Err(CoreError::bad_request("room is required"));
2325 }
24- ruma::OwnedRoomOrAliasId::try_from(trimmed).map_err(|_| CoreError::bad_request("invalid room"))
26+ OwnedRoomOrAliasId::try_from(trimmed).map_err(|_| CoreError::bad_request("invalid room"))
2527 }
Modified file: crates/malice-core/src/request.rs+5 additions−20 deletions
@@ -277,30 +277,18 @@ pub async fn dispatch(
277277 Ok(Reply::Done)
278278 }
279279 Request::Verify { action } => {
280- let action = match action.as_str() {
281- "Accept" => VerificationAction::Accept,
282- "Confirm" => VerificationAction::Confirm,
283- "Reject" => VerificationAction::Reject,
284- "Cancel" => VerificationAction::Cancel,
285- _ => return Err(CoreError::bad_request("invalid verification action")),
286- };
287280 handle.verify(action).await?;
288281 Ok(Reply::Done)
289282 }
290- Request::Avatar { mxc } => {
291- let mxc_ref = <&ruma::MxcUri>::from(mxc.as_str());
292- if !mxc_ref.is_valid() {
293- return Err(CoreError::bad_request("invalid mxc URI"));
294- }
283+ Request::Avatar { media_id } => {
284+ let media_id = parse_media_id(media_id)?;
295285 let size = MediaSize::Thumbnail {
296286 width: AVATAR_SIZE,
297287 height: AVATAR_SIZE,
298288 };
299- let bytes = handle.mxc_media(mxc_ref, size).await?.bytes.to_vec();
289+ let bytes = handle.media(&media_id, size).await?.bytes.to_vec();
300290 let Some(content_type) = image_content_type(&bytes) else {
301- return Err(CoreError::unsupported(
302- "MXC content is not a supported image",
303- ));
291+ return Err(CoreError::unsupported("avatar is not a supported image"));
304292 };
305293 Ok(Reply::Avatar {
306294 bytes,
@@ -308,10 +296,7 @@ pub async fn dispatch(
308296 })
309297 }
310298 Request::Media { media_id, size } => {
311- if media_id.is_empty() || media_id.len() > 512 {
312- return Err(CoreError::bad_request("invalid media_id"));
313- }
314- let media_id = MediaId::from(media_id);
299+ let media_id = parse_media_id(media_id)?;
315300 let size = match size.as_str() {
316301 "Thumbnail" => MediaSize::Thumbnail {
317302 width: THUMBNAIL_SIZE,
Modified file: crates/malice-server/Cargo.toml+1 additions−1 deletions
@@ -13,11 +13,11 @@ path = "src/lib.rs"
1313
1414 [dependencies]
1515 malice-core = { path = "../malice-core" }
16+malice-static = { path = "../malice-static" }
1617 macltok = { workspace = true, features = ["sqlite", "serde", "mirror"] }
1718 axum = { version = "0.8", features = ["macros", "multipart"] }
1819 futures-util = { version = "0.3", default-features = false, features = ["std"] }
1920 hex = "0.4"
20-listenfd = "1"
2121 rand_core = { version = "0.6", features = ["getrandom"] }
2222 ruma = { workspace = true }
2323 serde = { workspace = true }
Deleted file: crates/malice-server/src/idle.rs−73 deletions
@@ -1,73 +0,0 @@
1-//! Idle-exit tracking for the socket-activated static-only server: counts
2-//! in-flight requests and the time of the last one, so the process can shut
3-//! itself down once nothing has happened for `MALICE_IDLE_EXIT_SECS`.
4-
5-use std::{
6- sync::{
7- Arc,
8- atomic::{AtomicUsize, Ordering},
9- },
10- time::{Duration, Instant},
11-};
12-
13-use axum::{extract::State, middleware::Next};
14-
15-#[derive(Clone)]
16-pub struct IdleTracker {
17- in_flight: Arc<AtomicUsize>,
18- last_activity: Arc<std::sync::Mutex<Instant>>,
19-}
20-
21-impl IdleTracker {
22- pub fn new() -> Self {
23- Self {
24- in_flight: Arc::new(AtomicUsize::new(0)),
25- last_activity: Arc::new(std::sync::Mutex::new(Instant::now())),
26- }
27- }
28-
29- fn touch(&self) {
30- *self
31- .last_activity
32- .lock()
33- .expect("idle tracker mutex poisoned") = Instant::now();
34- }
35-
36- /// Resolves once no request has started or finished, and none is still
37- /// in flight, for at least `idle`.
38- pub async fn wait_idle(&self, idle: Duration) {
39- loop {
40- tokio::time::sleep(idle.min(Duration::from_millis(200))).await;
41- if self.in_flight.load(Ordering::SeqCst) != 0 {
42- continue;
43- }
44- let elapsed = self
45- .last_activity
46- .lock()
47- .expect("idle tracker mutex poisoned")
48- .elapsed();
49- if elapsed >= idle {
50- return;
51- }
52- }
53- }
54-}
55-
56-impl Default for IdleTracker {
57- fn default() -> Self {
58- Self::new()
59- }
60-}
61-
62-pub async fn track(
63- State(idle): State<IdleTracker>,
64- request: axum::extract::Request,
65- next: Next,
66-) -> axum::response::Response {
67- idle.in_flight.fetch_add(1, Ordering::SeqCst);
68- idle.touch();
69- let response = next.run(request).await;
70- idle.touch();
71- idle.in_flight.fetch_sub(1, Ordering::SeqCst);
72- response
73-}
Modified file: crates/malice-server/src/lib.rs−1 deletions
@@ -1,5 +1,4 @@
11 pub mod csrf;
22 pub mod error;
3-pub mod idle;
43 pub mod routes;
54 pub mod state;
Modified file: crates/malice-server/src/main.rs+13 additions−101 deletions
@@ -1,12 +1,10 @@
1-use std::{future::Future, net::SocketAddr, path::PathBuf, time::Duration};
1+use std::path::PathBuf;
22
3-use axum::{Router, middleware};
4-use listenfd::ListenFd;
53 use malice::{
6- idle::IdleTracker,
7- routes::{router, static_router},
4+ routes::router,
85 state::{AppState, ensure_dirs, load_or_create_master_key},
96 };
7+use malice_static::{bind_listener, serve};
108
119 fn env_flag(name: &str, default: bool) -> bool {
1210 match std::env::var(name) {
@@ -28,19 +26,6 @@ fn data_dir() -> PathBuf {
2826 .unwrap_or_else(|_| PathBuf::from("local-data"))
2927 }
3028
31-fn bind_addr() -> SocketAddr {
32- std::env::var("MALICE_BIND")
33- .unwrap_or_else(|_| "127.0.0.1:3000".to_owned())
34- .parse()
35- .expect("MALICE_BIND must be host:port")
36-}
37-
38-fn idle_exit_secs() -> Option<u64> {
39- std::env::var("MALICE_IDLE_EXIT_SECS")
40- .ok()
41- .and_then(|value| value.trim().parse().ok())
42-}
43-
4429 fn web_dir() -> Option<String> {
4530 if let Ok(dir) = std::env::var("MALICE_WEB_DIR") {
4631 assert!(
@@ -57,66 +42,17 @@ fn web_dir() -> Option<String> {
5742 .then(|| local.to_string_lossy().into_owned())
5843 }
5944
60-/// Takes over a systemd-activated listener from fd 3 when present, so the
61-/// socket unit can hold the port before the process ever starts. Falls back
62-/// to a plain bind otherwise.
63-async fn bind_listener() -> tokio::net::TcpListener {
64- let mut listenfd = ListenFd::from_env();
65- if let Some(listener) = listenfd
66- .take_tcp_listener(0)
67- .expect("inspecting inherited sockets")
68- {
69- listener
70- .set_nonblocking(true)
71- .expect("setting inherited socket nonblocking");
72- return tokio::net::TcpListener::from_std(listener)
73- .expect("adopting inherited socket into tokio");
74- }
75- let addr = bind_addr();
76- tracing::info!("listening on {addr}");
77- tokio::net::TcpListener::bind(addr)
78- .await
79- .expect("binding the listener must succeed")
80-}
81-
82-/// Serves `app` on `listener`, exiting on ctrl-c or, when
83-/// `MALICE_IDLE_EXIT_SECS` is set, after that long with no request in flight
84-/// or started. `on_shutdown` runs after the exit signal either way.
85-async fn serve(
86- listener: tokio::net::TcpListener,
87- app: Router,
88- on_shutdown: impl Future<Output = ()> + Send + 'static,
89-) {
90- let idle = idle_exit_secs().map(|secs| (IdleTracker::new(), secs));
91- let app = match &idle {
92- Some((tracker, _)) => app.layer(middleware::from_fn_with_state(
93- tracker.clone(),
94- malice::idle::track,
95- )),
96- None => app,
97- };
98- axum::serve(listener, app)
99- .with_graceful_shutdown(async move {
100- match idle {
101- Some((tracker, secs)) => {
102- tokio::select! {
103- _ = tokio::signal::ctrl_c() => {}
104- _ = tracker.wait_idle(Duration::from_secs(secs)) => {
105- tracing::info!("idle for {secs}s with no requests, exiting");
106- }
107- }
108- }
109- None => {
110- let _ = tokio::signal::ctrl_c().await;
111- }
112- }
113- on_shutdown.await;
114- })
115- .await
116- .expect("serving must succeed");
117-}
45+#[tokio::main]
46+async fn main() {
47+ tracing_subscriber::fmt()
48+ .with_env_filter(
49+ tracing_subscriber::EnvFilter::try_from_default_env()
50+ .unwrap_or_else(|_| "malice=info,tower_http=info".into()),
51+ )
52+ .with_target(false)
53+ .compact()
54+ .init();
11855
119-async fn run_hosted() {
12056 let data_dir = data_dir();
12157 ensure_dirs(&data_dir);
12258 let master_key =
@@ -133,27 +69,3 @@ async fn run_hosted() {
13369 )
13470 .await;
13571 }
136-
137-async fn run_static_only() {
138- let app = static_router(web_dir());
139- let listener = bind_listener().await;
140- serve(listener, app, async {}).await;
141-}
142-
143-#[tokio::main]
144-async fn main() {
145- tracing_subscriber::fmt()
146- .with_env_filter(
147- tracing_subscriber::EnvFilter::try_from_default_env()
148- .unwrap_or_else(|_| "malice=info,tower_http=info".into()),
149- )
150- .with_target(false)
151- .compact()
152- .init();
153-
154- if env_flag("MALICE_STATIC_ONLY", false) {
155- run_static_only().await;
156- } else {
157- run_hosted().await;
158- }
159-}
Modified file: crates/malice-server/src/routes.rs+2 additions−47 deletions
@@ -3,11 +3,10 @@ use std::sync::Arc;
33 use axum::{
44 Router,
55 extract::{DefaultBodyLimit, Multipart, Query, State},
6- http::{HeaderMap, HeaderName, HeaderValue, StatusCode, header},
6+ http::{HeaderMap, StatusCode},
77 middleware,
8- middleware::Next,
98 response::{IntoResponse, Response, Sse, sse::Event as SseEvent},
10- routing::{any, get, post},
9+ routing::{get, post},
1110 };
1211 use futures_util::StreamExt;
1312 use macltok::Retain;
@@ -789,47 +788,3 @@ pub fn router(state: AppState, web_dir: Option<String>) -> Router {
789788 None => router.fallback(static_shell),
790789 }
791790 }
792-
793-async fn api_not_found() -> StatusCode {
794- StatusCode::NOT_FOUND
795-}
796-
797-fn is_no_cache_path(path: &str) -> bool {
798- matches!(path, "/" | "/index.html" | "/sw.js")
799-}
800-
801-/// index.html and sw.js must never be cached, or a stale service worker or
802-/// app shell survives a rebuild; the rest of the bundle is hashed and can be
803-/// cached freely.
804-async fn static_headers(request: axum::extract::Request, next: Next) -> Response {
805- let no_cache = is_no_cache_path(request.uri().path());
806- let mut response = next.run(request).await;
807- if no_cache {
808- let headers = response.headers_mut();
809- headers.insert(header::CACHE_CONTROL, HeaderValue::from_static("no-cache"));
810- headers.insert(
811- HeaderName::from_static("service-worker-allowed"),
812- HeaderValue::from_static("/"),
813- );
814- }
815- response
816-}
817-
818-/// Serves only the built frontend: no `/api` routes, no session state, no
819-/// data dir. Used for the socket-activated on-device build.
820-pub fn static_router(web_dir: Option<String>) -> Router {
821- let router = Router::new()
822- .route("/api", any(api_not_found))
823- .route("/api/{*rest}", any(api_not_found))
824- .layer(TraceLayer::new_for_http());
825- match web_dir {
826- Some(dir) => {
827- let files =
828- ServeDir::new(&dir).not_found_service(ServeFile::new(format!("{dir}/index.html")));
829- router
830- .fallback_service(files)
831- .layer(middleware::from_fn(static_headers))
832- }
833- None => router.fallback(static_shell),
834- }
835-}
Deleted file: crates/malice-server/tests/static_only.rs−139 deletions
@@ -1,139 +0,0 @@
1-use std::{
2- net::TcpListener,
3- path::PathBuf,
4- process::{Child, Command, Stdio},
5- time::Duration,
6-};
7-
8-use reqwest::{Client, StatusCode, header};
9-use tempfile::TempDir;
10-
11-struct Server(Child);
12-
13-impl Drop for Server {
14- fn drop(&mut self) {
15- let _ = self.0.kill();
16- let _ = self.0.wait();
17- }
18-}
19-
20-fn server_binary() -> PathBuf {
21- option_env!("CARGO_BIN_EXE_malice")
22- .map(PathBuf::from)
23- .unwrap_or_else(|| {
24- std::env::current_exe()
25- .expect("integration test executable")
26- .parent()
27- .expect("cargo deps directory")
28- .parent()
29- .expect("cargo target directory")
30- .join("malice")
31- })
32-}
33-
34-fn unused_port() -> u16 {
35- TcpListener::bind("127.0.0.1:0")
36- .expect("bind ephemeral port")
37- .local_addr()
38- .expect("read ephemeral port")
39- .port()
40-}
41-
42-async fn start(port: u16, web_dir: &TempDir, idle_secs: u64, client: &Client) -> Server {
43- let child = Command::new(server_binary())
44- .env("MALICE_BIND", format!("127.0.0.1:{port}"))
45- .env("MALICE_STATIC_ONLY", "1")
46- .env("MALICE_WEB_DIR", web_dir.path())
47- .env("MALICE_IDLE_EXIT_SECS", idle_secs.to_string())
48- .stdout(Stdio::null())
49- .stderr(Stdio::inherit())
50- .spawn()
51- .expect("start Malice backend");
52- let mut server = Server(child);
53- tokio::time::timeout(Duration::from_secs(30), async {
54- loop {
55- if let Some(status) = server.0.try_wait().expect("poll backend") {
56- panic!("Malice exited before it started serving: {status}");
57- }
58- if client
59- .get(format!("http://127.0.0.1:{port}/"))
60- .send()
61- .await
62- .is_ok_and(|response| response.status().is_success())
63- {
64- break;
65- }
66- tokio::time::sleep(Duration::from_millis(100)).await;
67- }
68- })
69- .await
70- .expect("Malice did not start");
71- server
72-}
73-
74-#[tokio::test]
75-async fn static_only_serves_the_bundle_and_exits_when_idle() {
76- let web_dir = tempfile::tempdir().expect("temporary web dir");
77- std::fs::write(web_dir.path().join("index.html"), "<html>malice</html>")
78- .expect("write index.html");
79- std::fs::write(web_dir.path().join("sw.js"), "// service worker").expect("write sw.js");
80- std::fs::write(web_dir.path().join("style.css"), "body {}").expect("write style.css");
81-
82- let client = Client::new();
83- let port = unused_port();
84- let base = format!("http://127.0.0.1:{port}");
85- let mut server = start(port, &web_dir, 1, &client).await;
86-
87- let index = client.get(&base).send().await.unwrap();
88- assert_eq!(index.status(), StatusCode::OK);
89- assert_eq!(
90- index.headers().get(header::CACHE_CONTROL).unwrap(),
91- "no-cache"
92- );
93- assert_eq!(index.headers().get("service-worker-allowed").unwrap(), "/");
94- assert_eq!(index.text().await.unwrap(), "<html>malice</html>");
95-
96- let sw = client.get(format!("{base}/sw.js")).send().await.unwrap();
97- assert_eq!(sw.status(), StatusCode::OK);
98- assert_eq!(sw.headers().get(header::CACHE_CONTROL).unwrap(), "no-cache");
99- assert_eq!(sw.headers().get("service-worker-allowed").unwrap(), "/");
100-
101- let asset = client
102- .get(format!("{base}/style.css"))
103- .send()
104- .await
105- .unwrap();
106- assert_eq!(asset.status(), StatusCode::OK);
107- assert!(asset.headers().get(header::CACHE_CONTROL).is_none());
108-
109- assert_eq!(
110- client
111- .get(format!("{base}/api/rooms"))
112- .send()
113- .await
114- .unwrap()
115- .status(),
116- StatusCode::NOT_FOUND,
117- );
118- assert_eq!(
119- client
120- .get(format!("{base}/api/"))
121- .send()
122- .await
123- .unwrap()
124- .status(),
125- StatusCode::NOT_FOUND,
126- );
127-
128- let status = tokio::time::timeout(Duration::from_secs(10), async {
129- loop {
130- if let Some(status) = server.0.try_wait().expect("poll backend") {
131- return status;
132- }
133- tokio::time::sleep(Duration::from_millis(50)).await;
134- }
135- })
136- .await
137- .expect("Malice did not exit once idle");
138- assert!(status.success(), "idle exit should be clean: {status}");
139-}
Added file: crates/malice-static/Cargo.toml+27 additions
@@ -0,0 +1,27 @@
1+[package]
2+name = "malice-static"
3+version = "0.1.0"
4+edition = "2024"
5+
6+[[bin]]
7+name = "malice-static"
8+path = "src/main.rs"
9+
10+[lib]
11+name = "malice_static"
12+path = "src/lib.rs"
13+
14+[dependencies]
15+axum = { version = "0.8", default-features = false, features = ["http1", "tokio", "tracing"] }
16+listenfd = "1"
17+tokio = { workspace = true, features = ["macros", "net", "rt-multi-thread", "signal", "time"] }
18+tower-http = { version = "0.6", features = ["fs", "trace"] }
19+tracing = { workspace = true }
20+tracing-subscriber = { version = "0.3", features = ["env-filter"] }
21+
22+[dev-dependencies]
23+reqwest = { version = "0.13", default-features = false }
24+tempfile = "3"
25+
26+[lints]
27+workspace = true
Added file: crates/malice-static/src/idle.rs+73 additions
@@ -0,0 +1,73 @@
1+//! Idle-exit tracking for the socket-activated static-only server: counts
2+//! in-flight requests and the time of the last one, so the process can shut
3+//! itself down once nothing has happened for `MALICE_IDLE_EXIT_SECS`.
4+
5+use std::{
6+ sync::{
7+ Arc,
8+ atomic::{AtomicUsize, Ordering},
9+ },
10+ time::{Duration, Instant},
11+};
12+
13+use axum::{extract::State, middleware::Next};
14+
15+#[derive(Clone)]
16+pub struct IdleTracker {
17+ in_flight: Arc<AtomicUsize>,
18+ last_activity: Arc<std::sync::Mutex<Instant>>,
19+}
20+
21+impl IdleTracker {
22+ pub fn new() -> Self {
23+ Self {
24+ in_flight: Arc::new(AtomicUsize::new(0)),
25+ last_activity: Arc::new(std::sync::Mutex::new(Instant::now())),
26+ }
27+ }
28+
29+ fn touch(&self) {
30+ *self
31+ .last_activity
32+ .lock()
33+ .expect("idle tracker mutex poisoned") = Instant::now();
34+ }
35+
36+ /// Resolves once no request has started or finished, and none is still
37+ /// in flight, for at least `idle`.
38+ pub async fn wait_idle(&self, idle: Duration) {
39+ loop {
40+ tokio::time::sleep(idle.min(Duration::from_millis(200))).await;
41+ if self.in_flight.load(Ordering::SeqCst) != 0 {
42+ continue;
43+ }
44+ let elapsed = self
45+ .last_activity
46+ .lock()
47+ .expect("idle tracker mutex poisoned")
48+ .elapsed();
49+ if elapsed >= idle {
50+ return;
51+ }
52+ }
53+ }
54+}
55+
56+impl Default for IdleTracker {
57+ fn default() -> Self {
58+ Self::new()
59+ }
60+}
61+
62+pub async fn track(
63+ State(idle): State<IdleTracker>,
64+ request: axum::extract::Request,
65+ next: Next,
66+) -> axum::response::Response {
67+ idle.in_flight.fetch_add(1, Ordering::SeqCst);
68+ idle.touch();
69+ let response = next.run(request).await;
70+ idle.touch();
71+ idle.in_flight.fetch_sub(1, Ordering::SeqCst);
72+ response
73+}
Added file: crates/malice-static/src/lib.rs+142 additions
@@ -0,0 +1,142 @@
1+pub mod idle;
2+
3+use std::{future::Future, net::SocketAddr, time::Duration};
4+
5+use axum::{
6+ Router,
7+ http::{HeaderName, HeaderValue, StatusCode, header},
8+ middleware,
9+ middleware::Next,
10+ response::{IntoResponse, Response},
11+ routing::any,
12+};
13+use listenfd::ListenFd;
14+use tower_http::{
15+ services::{ServeDir, ServeFile},
16+ trace::TraceLayer,
17+};
18+
19+use crate::idle::IdleTracker;
20+
21+fn bind_addr() -> SocketAddr {
22+ std::env::var("MALICE_BIND")
23+ .unwrap_or_else(|_| "127.0.0.1:3000".to_owned())
24+ .parse()
25+ .expect("MALICE_BIND must be host:port")
26+}
27+
28+fn idle_exit_secs() -> Option<u64> {
29+ std::env::var("MALICE_IDLE_EXIT_SECS")
30+ .ok()
31+ .and_then(|value| value.trim().parse().ok())
32+}
33+
34+/// Takes over a systemd-activated listener from fd 3 when present, so the
35+/// socket unit can hold the port before the process ever starts. Falls back
36+/// to a plain bind otherwise.
37+pub async fn bind_listener() -> tokio::net::TcpListener {
38+ let mut listenfd = ListenFd::from_env();
39+ if let Some(listener) = listenfd
40+ .take_tcp_listener(0)
41+ .expect("inspecting inherited sockets")
42+ {
43+ listener
44+ .set_nonblocking(true)
45+ .expect("setting inherited socket nonblocking");
46+ return tokio::net::TcpListener::from_std(listener)
47+ .expect("adopting inherited socket into tokio");
48+ }
49+ let addr = bind_addr();
50+ tracing::info!("listening on {addr}");
51+ tokio::net::TcpListener::bind(addr)
52+ .await
53+ .expect("binding the listener must succeed")
54+}
55+
56+/// Serves `app` on `listener`, exiting on ctrl-c or, when
57+/// `MALICE_IDLE_EXIT_SECS` is set, after that long with no request in flight
58+/// or started. `on_shutdown` runs after the exit signal either way.
59+pub async fn serve(
60+ listener: tokio::net::TcpListener,
61+ app: Router,
62+ on_shutdown: impl Future<Output = ()> + Send + 'static,
63+) {
64+ let idle = idle_exit_secs().map(|secs| (IdleTracker::new(), secs));
65+ let app = match &idle {
66+ Some((tracker, _)) => {
67+ app.layer(middleware::from_fn_with_state(tracker.clone(), idle::track))
68+ }
69+ None => app,
70+ };
71+ axum::serve(listener, app)
72+ .with_graceful_shutdown(async move {
73+ match idle {
74+ Some((tracker, secs)) => {
75+ tokio::select! {
76+ _ = tokio::signal::ctrl_c() => {}
77+ _ = tracker.wait_idle(Duration::from_secs(secs)) => {
78+ tracing::info!("idle for {secs}s with no requests, exiting");
79+ }
80+ }
81+ }
82+ None => {
83+ let _ = tokio::signal::ctrl_c().await;
84+ }
85+ }
86+ on_shutdown.await;
87+ })
88+ .await
89+ .expect("serving must succeed");
90+}
91+
92+async fn api_not_found() -> StatusCode {
93+ StatusCode::NOT_FOUND
94+}
95+
96+async fn missing_frontend() -> Response {
97+ (
98+ StatusCode::SERVICE_UNAVAILABLE,
99+ "frontend assets missing; set MALICE_WEB_DIR or use the Nix package",
100+ )
101+ .into_response()
102+}
103+
104+fn is_no_cache_path(path: &str) -> bool {
105+ matches!(path, "/" | "/index.html" | "/sw.js")
106+}
107+
108+/// index.html and sw.js must never be cached, or a stale service worker or
109+/// app shell survives a rebuild; the rest of the bundle is hashed and can be
110+/// cached freely.
111+async fn static_headers(request: axum::extract::Request, next: Next) -> Response {
112+ let no_cache = is_no_cache_path(request.uri().path());
113+ let mut response = next.run(request).await;
114+ if no_cache {
115+ let headers = response.headers_mut();
116+ headers.insert(header::CACHE_CONTROL, HeaderValue::from_static("no-cache"));
117+ headers.insert(
118+ HeaderName::from_static("service-worker-allowed"),
119+ HeaderValue::from_static("/"),
120+ );
121+ }
122+ response
123+}
124+
125+/// Serves only the built frontend: no `/api` routes, no session state, no
126+/// data dir. Used for the socket-activated on-device build.
127+pub fn static_router(web_dir: Option<String>) -> Router {
128+ let router = Router::new()
129+ .route("/api", any(api_not_found))
130+ .route("/api/{*rest}", any(api_not_found))
131+ .layer(TraceLayer::new_for_http());
132+ match web_dir {
133+ Some(dir) => {
134+ let files =
135+ ServeDir::new(&dir).not_found_service(ServeFile::new(format!("{dir}/index.html")));
136+ router
137+ .fallback_service(files)
138+ .layer(middleware::from_fn(static_headers))
139+ }
140+ None => router.fallback(missing_frontend),
141+ }
142+}
Added file: crates/malice-static/src/main.rs+25 additions
@@ -0,0 +1,25 @@
1+use malice_static::{bind_listener, serve, static_router};
2+
3+fn web_dir() -> Option<String> {
4+ let dir = std::env::var("MALICE_WEB_DIR").ok()?;
5+ assert!(
6+ std::path::Path::new(&dir).join("index.html").is_file(),
7+ "MALICE_WEB_DIR must point to built frontend assets containing index.html"
8+ );
9+ Some(dir)
10+}
11+
12+#[tokio::main]
13+async fn main() {
14+ tracing_subscriber::fmt()
15+ .with_env_filter(
16+ tracing_subscriber::EnvFilter::try_from_default_env()
17+ .unwrap_or_else(|_| "malice_static=info,tower_http=info".into()),
18+ )
19+ .with_target(false)
20+ .compact()
21+ .init();
22+
23+ let app = static_router(web_dir());
24+ serve(bind_listener().await, app, async {}).await;
25+}
Added file: crates/malice-static/tests/static_only.rs+138 additions
@@ -0,0 +1,138 @@
1+use std::{
2+ net::TcpListener,
3+ path::PathBuf,
4+ process::{Child, Command, Stdio},
5+ time::Duration,
6+};
7+
8+use reqwest::{Client, StatusCode, header};
9+use tempfile::TempDir;
10+
11+struct Server(Child);
12+
13+impl Drop for Server {
14+ fn drop(&mut self) {
15+ let _ = self.0.kill();
16+ let _ = self.0.wait();
17+ }
18+}
19+
20+fn server_binary() -> PathBuf {
21+ option_env!("CARGO_BIN_EXE_malice-static")
22+ .map(PathBuf::from)
23+ .unwrap_or_else(|| {
24+ std::env::current_exe()
25+ .expect("integration test executable")
26+ .parent()
27+ .expect("cargo deps directory")
28+ .parent()
29+ .expect("cargo target directory")
30+ .join("malice-static")
31+ })
32+}
33+
34+fn unused_port() -> u16 {
35+ TcpListener::bind("127.0.0.1:0")
36+ .expect("bind ephemeral port")
37+ .local_addr()
38+ .expect("read ephemeral port")
39+ .port()
40+}
41+
42+async fn start(port: u16, web_dir: &TempDir, idle_secs: u64, client: &Client) -> Server {
43+ let child = Command::new(server_binary())
44+ .env("MALICE_BIND", format!("127.0.0.1:{port}"))
45+ .env("MALICE_WEB_DIR", web_dir.path())
46+ .env("MALICE_IDLE_EXIT_SECS", idle_secs.to_string())
47+ .stdout(Stdio::null())
48+ .stderr(Stdio::inherit())
49+ .spawn()
50+ .expect("start Malice backend");
51+ let mut server = Server(child);
52+ tokio::time::timeout(Duration::from_secs(30), async {
53+ loop {
54+ if let Some(status) = server.0.try_wait().expect("poll backend") {
55+ panic!("Malice exited before it started serving: {status}");
56+ }
57+ if client
58+ .get(format!("http://127.0.0.1:{port}/"))
59+ .send()
60+ .await
61+ .is_ok_and(|response| response.status().is_success())
62+ {
63+ break;
64+ }
65+ tokio::time::sleep(Duration::from_millis(100)).await;
66+ }
67+ })
68+ .await
69+ .expect("Malice did not start");
70+ server
71+}
72+
73+#[tokio::test]
74+async fn static_only_serves_the_bundle_and_exits_when_idle() {
75+ let web_dir = tempfile::tempdir().expect("temporary web dir");
76+ std::fs::write(web_dir.path().join("index.html"), "<html>malice</html>")
77+ .expect("write index.html");
78+ std::fs::write(web_dir.path().join("sw.js"), "// service worker").expect("write sw.js");
79+ std::fs::write(web_dir.path().join("style.css"), "body {}").expect("write style.css");
80+
81+ let client = Client::new();
82+ let port = unused_port();
83+ let base = format!("http://127.0.0.1:{port}");
84+ let mut server = start(port, &web_dir, 1, &client).await;
85+
86+ let index = client.get(&base).send().await.unwrap();
87+ assert_eq!(index.status(), StatusCode::OK);
88+ assert_eq!(
89+ index.headers().get(header::CACHE_CONTROL).unwrap(),
90+ "no-cache"
91+ );
92+ assert_eq!(index.headers().get("service-worker-allowed").unwrap(), "/");
93+ assert_eq!(index.text().await.unwrap(), "<html>malice</html>");
94+
95+ let sw = client.get(format!("{base}/sw.js")).send().await.unwrap();
96+ assert_eq!(sw.status(), StatusCode::OK);
97+ assert_eq!(sw.headers().get(header::CACHE_CONTROL).unwrap(), "no-cache");
98+ assert_eq!(sw.headers().get("service-worker-allowed").unwrap(), "/");
99+
100+ let asset = client
101+ .get(format!("{base}/style.css"))
102+ .send()
103+ .await
104+ .unwrap();
105+ assert_eq!(asset.status(), StatusCode::OK);
106+ assert!(asset.headers().get(header::CACHE_CONTROL).is_none());
107+
108+ assert_eq!(
109+ client
110+ .get(format!("{base}/api/rooms"))
111+ .send()
112+ .await
113+ .unwrap()
114+ .status(),
115+ StatusCode::NOT_FOUND,
116+ );
117+ assert_eq!(
118+ client
119+ .get(format!("{base}/api/"))
120+ .send()
121+ .await
122+ .unwrap()
123+ .status(),
124+ StatusCode::NOT_FOUND,
125+ );
126+
127+ let status = tokio::time::timeout(Duration::from_secs(10), async {
128+ loop {
129+ if let Some(status) = server.0.try_wait().expect("poll backend") {
130+ return status;
131+ }
132+ tokio::time::sleep(Duration::from_millis(50)).await;
133+ }
134+ })
135+ .await
136+ .expect("Malice did not exit once idle");
137+ assert!(status.success(), "idle exit should be clean: {status}");
138+}
Modified file: flake.nix+117 additions−34 deletions
@@ -10,16 +10,36 @@
1010 };
1111
1212 outputs =
13- { self, nixpkgs, fenix }:
13+ {
14+ self,
15+ nixpkgs,
16+ fenix,
17+ }:
1418 let
1519 systems = [
1620 "x86_64-linux"
1721 "aarch64-linux"
1822 ];
1923 forAllSystems = nixpkgs.lib.genAttrs systems;
24+ cleanSrc =
25+ root:
26+ nixpkgs.lib.cleanSourceWith {
27+ src = root;
28+ filter =
29+ path: type:
30+ nixpkgs.lib.cleanSourceFilter path type
31+ && !(
32+ type == "directory"
33+ && builtins.elem (baseNameOf path) [
34+ "target"
35+ "node_modules"
36+ "dist"
37+ "dist-device"
38+ ]
39+ );
40+ };
2041 workerLock = builtins.fromTOML (builtins.readFile ./crates/malice-worker/Cargo.lock);
21- lockedVersion =
22- name: (nixpkgs.lib.findFirst (p: p.name == name) null workerLock.package).version;
42+ lockedVersion = name: (nixpkgs.lib.findFirst (p: p.name == name) null workerLock.package).version;
2343 # wasm-bindgen-test-runner refuses a module built by any other wasm-bindgen version
2444 wasmBindgenCli =
2545 pkgs:
@@ -48,6 +68,42 @@
4868 toolchains.stable.rust-src
4969 toolchains.targets.wasm32-unknown-unknown.stable.rust-std
5070 ];
71+ webNodeModules =
72+ pkgs:
73+ pkgs.importNpmLock.buildNodeModules {
74+ npmRoot = ./web;
75+ nodejs = pkgs.nodejs_22;
76+ };
77+ buildWeb =
78+ pkgs:
79+ {
80+ pname,
81+ task,
82+ outDir,
83+ postPatch ? "",
84+ }:
85+ pkgs.stdenvNoCC.mkDerivation {
86+ inherit pname postPatch;
87+ version = "0.1.0";
88+ src = cleanSrc ./web;
89+ npmDeps = webNodeModules pkgs;
90+ nativeBuildInputs = [
91+ pkgs.nodejs_22
92+ pkgs.importNpmLock.hooks.linkNodeModulesHook
93+ ];
94+ buildPhase = ''
95+ runHook preBuild
96+ export HOME=$TMPDIR
97+ npm run ${task}
98+ runHook postBuild
99+ '';
100+ installPhase = ''
101+ runHook preInstall
102+ mkdir -p $out/share/malice
103+ cp -R ${outDir}/. $out/share/malice/
104+ runHook postInstall
105+ '';
106+ };
51107 in
52108 {
53109 packages = forAllSystems (
@@ -58,31 +114,47 @@
58114 cargo = wasmToolchain system;
59115 rustc = wasmToolchain system;
60116 };
61- web = pkgs.buildNpmPackage {
117+ web = buildWeb pkgs {
62118 pname = "malice-web";
63- version = "0.1.0";
64- src = ./web;
65- npmDepsHash = "sha256-Q00XwHH/+kfZTAPBKUlkx3hAtYvSpDw2opj22nGTIMY=";
66- installPhase = ''
67- runHook preInstall
68- mkdir -p $out/share/malice
69- cp -R dist/. $out/share/malice/
70- runHook postInstall
71- '';
119+ task = "build";
120+ outDir = "dist";
72121 };
73122 backend = pkgs.rustPlatform.buildRustPackage {
74123 pname = "malice-server";
75124 version = "0.1.0";
76- src = ./.;
125+ src = cleanSrc ./.;
77126 cargoLock.lockFile = ./Cargo.lock;
78127 nativeBuildInputs = [ pkgs.pkg-config ];
79128 buildInputs = [ pkgs.sqlite ];
129+ cargoBuildFlags = [
130+ "-p"
131+ "malice"
132+ ];
133+ cargoTestFlags = [
134+ "-p"
135+ "malice"
136+ ];
80137 # preBuild = "export NIX_BUILD_CORES=2";
81138 preCheck = ''
82139 cargo build --release --bin malice --target ${pkgs.stdenv.hostPlatform.rust.rustcTarget} --offline --locked
83140 '';
84141 SSL_CERT_FILE = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
85142 };
143+ staticServer = pkgs.rustPlatform.buildRustPackage {
144+ pname = "malice-static";
145+ version = "0.1.0";
146+ src = cleanSrc ./.;
147+ cargoLock.lockFile = ./Cargo.lock;
148+ cargoBuildFlags = [
149+ "-p"
150+ "malice-static"
151+ ];
152+ cargoTestFlags = [
153+ "-p"
154+ "malice-static"
155+ ];
156+ meta.mainProgram = "malice-static";
157+ };
86158 server = pkgs.symlinkJoin {
87159 name = "malice";
88160 paths = [
@@ -99,10 +171,13 @@
99171 workerWasm = wasmPlatform.buildRustPackage {
100172 pname = "malice-worker-wasm";
101173 version = "0.1.0";
102- src = self;
174+ src = cleanSrc ./.;
103175 cargoRoot = "crates/malice-worker";
104176 cargoLock.lockFile = ./crates/malice-worker/Cargo.lock;
105- nativeBuildInputs = [ (wasmBindgenCli pkgs) ];
177+ nativeBuildInputs = [
178+ (wasmBindgenCli pkgs)
179+ pkgs.removeReferencesTo
180+ ];
106181 buildPhase = ''
107182 runHook preBuild
108183 cd crates/malice-worker
@@ -110,44 +185,42 @@
110185 mkdir -p $out
111186 wasm-bindgen --target web --out-dir $out --out-name malice_worker \
112187 target/wasm32-unknown-unknown/release/malice_worker.wasm
188+ remove-references-to -t ${wasmToolchain system} $out/*.wasm
113189 runHook postBuild
114190 '';
115191 doCheck = false;
116192 installPhase = "true";
193+ disallowedReferences = [ (wasmToolchain system) ];
117194 };
118- web-device = pkgs.buildNpmPackage {
195+ web-device = buildWeb pkgs {
119196 pname = "malice-web-device";
120- version = "0.1.0";
121- src = ./web;
122- npmDepsHash = "sha256-Q00XwHH/+kfZTAPBKUlkx3hAtYvSpDw2opj22nGTIMY=";
197+ task = "build:device";
198+ outDir = "dist-device";
123199 postPatch = ''
124200 mkdir -p src/worker/pkg
125201 cp -R ${workerWasm}/. src/worker/pkg/
126202 '';
127- npmBuildScript = "build:device";
128- installPhase = ''
129- runHook preInstall
130- mkdir -p $out/share/malice
131- cp -R dist-device/. $out/share/malice/
132- runHook postInstall
133- '';
134203 };
135204 local = pkgs.symlinkJoin {
136205 name = "malice-local";
137206 paths = [
138- backend
207+ staticServer
139208 web-device
140209 ];
141210 nativeBuildInputs = [ pkgs.makeWrapper ];
142211 postBuild = ''
143- wrapProgram $out/bin/malice \
144- --set MALICE_WEB_DIR ${web-device}/share/malice \
145- --set MALICE_STATIC_ONLY 1
212+ wrapProgram $out/bin/malice-static \
213+ --set MALICE_WEB_DIR ${web-device}/share/malice
146214 '';
147215 };
148216 in
149217 {
150- inherit web server web-device local;
218+ inherit
219+ web
220+ server
221+ web-device
222+ local
223+ ;
151224 default = server;
152225 }
153226 );
@@ -164,16 +237,26 @@
164237 (wasmBindgenCli pkgs)
165238 pkgs.rust-analyzer
166239 pkgs.nodejs_22
240+ pkgs.importNpmLock.hooks.linkNodeModulesHook
167241 pkgs.pkg-config
168242 pkgs.sqlite
169243 pkgs.firefox
170244 pkgs.geckodriver
245+ pkgs.chromium
171246 pkgs.matrix-continuwuity
172247 pkgs.nushell
173248 ];
249+ CHROMIUM_PATH = nixpkgs.lib.getExe pkgs.chromium;
250+ npmDeps = webNodeModules pkgs;
251+ # the hook's own npmRoot would be a store path, so link into the checkout's web/ instead
252+ shellHook = ''
253+ npmRoot="$(git rev-parse --show-toplevel 2>/dev/null || pwd)/web" linkNodeModulesHook
254+ '';
174255 # fenix's rustc links through its bundled rust-lld, which skips nix's ld-wrapper and so
175256 # the store rpaths, leaving the native server unable to find libsqlite3 at run time
176- "CARGO_TARGET_${nixpkgs.lib.toUpper (builtins.replaceStrings [ "-" ] [ "_" ] pkgs.stdenv.hostPlatform.config)}_RUSTFLAGS" =
257+ "CARGO_TARGET_${
258+ nixpkgs.lib.toUpper (builtins.replaceStrings [ "-" ] [ "_" ] pkgs.stdenv.hostPlatform.config)
259+ }_RUSTFLAGS" =
177260 "-C linker-features=-lld";
178261 };
179262 }
@@ -205,7 +288,7 @@
205288 worker-browser = wasmPlatform.buildRustPackage {
206289 pname = "malice-worker-browser-check";
207290 version = "0.1.0";
208- src = self;
291+ src = cleanSrc ./.;
209292 cargoRoot = "crates/malice-worker";
210293 cargoLock.lockFile = ./crates/malice-worker/Cargo.lock;
211294 nativeBuildInputs = [
Modified file: nix/module.nix+7 additions−8 deletions
@@ -72,7 +72,7 @@ in
7272 package = mkOption {
7373 type = types.package;
7474 default = self.packages.${system}.local;
75- description = "The malice binary wrapped for the on-device build (MALICE_WEB_DIR and MALICE_STATIC_ONLY set).";
75+ description = "The malice-static binary wrapped for the on-device build (MALICE_WEB_DIR set).";
7676 };
7777
7878 port = mkOption {
@@ -141,18 +141,17 @@ in
141141 };
142142 systemd.user.services.malice = {
143143 description = "Malice on-device Matrix client";
144- environment = {
145- MALICE_STATIC_ONLY = "1";
146- MALICE_IDLE_EXIT_SECS = "300";
147- };
144+ environment.MALICE_IDLE_EXIT_SECS = "300";
148145 serviceConfig = {
149- ExecStart = lib.getExe' deviceCfg.package "malice";
146+ ExecStart = lib.getExe' deviceCfg.package "malice-static";
150147 };
151148 };
152149 environment.systemPackages = [
153150 malicOpen
154- malicDesktopItem
155- ];
151+ ]
152+ ++ (
153+ if ((builtins.stringLength deviceCfg.browser) != 0) then (lib.singleton malicDesktopItem) else [ ]
154+ );
156155 })
157156 ];
158157 }
Modified file: vendor/macltok/src/account.rs+149 additions−4 deletions
@@ -25,7 +25,8 @@ use tokio::sync::{broadcast, mpsc};
2525 use url::Url;
2626
2727 use crate::{
28- ClientHandle, Engine, EngineConfig, EngineError, ErrorKind, EventStream, SessionStore,
28+ ClientHandle, Engine, EngineConfig, EngineError, ErrorKind, EventStream, ServerError,
29+ SessionStore,
2930 engine::{Persistence, StoreLock},
3031 error::SourceError,
3132 };
@@ -54,6 +55,8 @@ pub struct PasswordLogin {
5455 pub device_display_name: Option<String>,
5556 /// set after a soft logout so the store's keys still belong to us
5657 pub device_id: Option<OwnedDeviceId>,
58+ /// let plain http through, for loopback hosts only
59+ pub allow_insecure_loopback: bool,
5760 }
5861
5962 impl PasswordLogin {
@@ -69,6 +72,7 @@ impl PasswordLogin {
6972 password: password.into(),
7073 device_display_name: None,
7174 device_id: None,
75+ allow_insecure_loopback: false,
7276 }
7377 }
7478
@@ -83,6 +87,12 @@ impl PasswordLogin {
8387 self.device_id = Some(device_id);
8488 self
8589 }
90+
91+ #[must_use]
92+ pub fn allow_insecure_loopback(mut self, allow: bool) -> Self {
93+ self.allow_insecure_loopback = allow;
94+ self
95+ }
8696 }
8797
8898 #[non_exhaustive]
@@ -94,6 +104,8 @@ pub struct OAuthLogin {
94104 pub client_id: Option<String>,
95105 pub device_id: Option<OwnedDeviceId>,
96106 pub application_type: OAuthApplicationType,
107+ /// let plain http through, for loopback hosts only
108+ pub allow_insecure_loopback: bool,
97109 }
98110
99111 impl OAuthLogin {
@@ -112,6 +124,7 @@ impl OAuthLogin {
112124 client_id: None,
113125 device_id: None,
114126 application_type,
127+ allow_insecure_loopback: false,
115128 }
116129 }
117130
@@ -133,6 +146,12 @@ impl OAuthLogin {
133146 self.device_id = Some(device_id);
134147 self
135148 }
149+
150+ #[must_use]
151+ pub fn allow_insecure_loopback(mut self, allow: bool) -> Self {
152+ self.allow_insecure_loopback = allow;
153+ self
154+ }
136155 }
137156
138157 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -297,6 +316,28 @@ impl SessionRecord {
297316 &self.homeserver
298317 }
299318
319+ pub(crate) fn user_id(&self) -> &OwnedUserId {
320+ match &self.authentication {
321+ AuthenticationRecord::Matrix(session) => &session.meta.user_id,
322+ AuthenticationRecord::OAuth { user, .. } => &user.meta.user_id,
323+ AuthenticationRecord::SoftLoggedOut { meta, .. } => &meta.user_id,
324+ }
325+ }
326+
327+ pub(crate) fn soft_logout(&self) -> Option<SoftLogout> {
328+ let AuthenticationRecord::SoftLoggedOut { meta, client_id } = &self.authentication else {
329+ return None;
330+ };
331+ Some(SoftLogout {
332+ homeserver: self.homeserver.clone(),
333+ user_id: meta.user_id.clone(),
334+ device_id: meta.device_id.clone(),
335+ client_id: client_id
336+ .as_ref()
337+ .map(|client_id| client_id.as_str().to_owned()),
338+ })
339+ }
340+
300341 pub(crate) fn soft_logged_out(self) -> Self {
301342 let authentication = match self.authentication {
302343 AuthenticationRecord::Matrix(session) => AuthenticationRecord::SoftLoggedOut {
@@ -340,6 +381,28 @@ impl SoftLogout {
340381 pub fn password_login(&self, password: impl Into<String>) -> PasswordLogin {
341382 PasswordLogin::new(self.homeserver.clone(), self.user_id.as_str(), password)
342383 .with_device_id(self.device_id.clone())
384+ .allow_insecure_loopback(true)
385+ }
386+
387+ #[must_use]
388+ pub fn oauth_login(
389+ &self,
390+ redirect_uri: impl Into<String>,
391+ client_uri: impl Into<String>,
392+ application_type: OAuthApplicationType,
393+ ) -> OAuthLogin {
394+ let login = OAuthLogin::new(
395+ self.homeserver.clone(),
396+ redirect_uri,
397+ client_uri,
398+ application_type,
399+ )
400+ .with_device_id(self.device_id.clone())
401+ .allow_insecure_loopback(true);
402+ match &self.client_id {
403+ Some(client_id) => login.with_client_id(client_id.clone()),
404+ None => login,
405+ }
343406 }
344407 }
345408
@@ -348,6 +411,8 @@ pub enum AccountError {
348411 #[error("{context} failed")]
349412 Operation {
350413 context: &'static str,
414+ kind: ErrorKind,
415+ server: Option<ServerError>,
351416 #[source]
352417 source: Box<SourceError>,
353418 },
@@ -357,6 +422,12 @@ pub enum AccountError {
357422 UnsupportedAuthentication,
358423 #[error("the OAuth callback was already submitted")]
359424 OAuthCallbackAlreadySubmitted,
425+ /// what was typed can't name a homeserver, so nothing was sent anywhere
426+ #[error("that isn't a usable homeserver, {0}")]
427+ InvalidHomeserver(&'static str),
428+ /// `Accounts` already has a live login for them. a second would be another device
429+ #[error("{0} is already logged in")]
430+ AlreadyLoggedIn(OwnedUserId),
360431 /// an IndexedDB delete is waiting on the store's other connections. it goes through
361432 /// once they close, and opening the store again waits behind it
362433 #[error("the store is still open elsewhere, it will be deleted once that closes")]
@@ -376,6 +447,18 @@ pub enum AccountError {
376447 Engine(#[from] EngineError),
377448 }
378449
450+impl AccountError {
451+ /// what the homeserver said, when it refused
452+ #[must_use]
453+ pub fn server(&self) -> Option<&ServerError> {
454+ match self {
455+ Self::Operation { server, .. } => server.as_ref(),
456+ Self::Unsaved { source, .. } => source.server(),
457+ _ => None,
458+ }
459+ }
460+}
461+
379462 /// a live login whose record never reached its `SessionStore`
380463 pub struct UnsavedSession {
381464 session: Session,
@@ -416,9 +499,19 @@ impl Session {
416499 login: PasswordLogin,
417500 sessions: Arc<dyn SessionStore>,
418501 ) -> Result<Self, AccountError> {
502+ homeserver_input(&login.homeserver, login.allow_insecure_loopback)?;
419503 let store_lock = sessions.store().lock().await?;
504+ Self::login_password_locked(login, sessions, store_lock).await
505+ }
506+
507+ pub(crate) async fn login_password_locked(
508+ login: PasswordLogin,
509+ sessions: Arc<dyn SessionStore>,
510+ store_lock: StoreLock,
511+ ) -> Result<Self, AccountError> {
512+ let homeserver = homeserver_input(&login.homeserver, login.allow_insecure_loopback)?;
420513 let client = matrix_client_builder(sessions.store())
421- .server_name_or_homeserver_url(login.homeserver.trim())
514+ .server_name_or_homeserver_url(homeserver)
422515 .build()
423516 .await
424517 .map_err(|error| account_error("building the Matrix client", error))?;
@@ -445,13 +538,23 @@ impl Session {
445538 login: OAuthLogin,
446539 sessions: Arc<dyn SessionStore>,
447540 ) -> Result<PendingOAuthLogin, AccountError> {
541+ homeserver_input(&login.homeserver, login.allow_insecure_loopback)?;
542+ let store_lock = sessions.store().lock().await?;
543+ Self::begin_oauth_locked(login, sessions, store_lock).await
544+ }
545+
546+ pub(crate) async fn begin_oauth_locked(
547+ login: OAuthLogin,
548+ sessions: Arc<dyn SessionStore>,
549+ store_lock: StoreLock,
550+ ) -> Result<PendingOAuthLogin, AccountError> {
448551 let redirect_uri = Url::parse(login.redirect_uri.trim())
449552 .map_err(|error| account_error("parsing the OAuth redirect URI", error))?;
450553 let client_uri = Url::parse(login.client_uri.trim())
451554 .map_err(|error| account_error("parsing the OAuth client URI", error))?;
452- let store_lock = sessions.store().lock().await?;
555+ let homeserver = homeserver_input(&login.homeserver, login.allow_insecure_loopback)?;
453556 let client = matrix_client_builder(sessions.store())
454- .server_name_or_homeserver_url(login.homeserver.trim())
557+ .server_name_or_homeserver_url(homeserver)
455558 .build()
456559 .await
457560 .map_err(|error| account_error("building the Matrix client", error))?;
@@ -678,12 +781,54 @@ fn matrix_client_builder(store: &StoreConfig) -> ClientBuilder {
678781 }
679782 }
680783
784+/// a bare server name goes through untouched, since that's what makes the sdk try `.well-known`
785+pub(crate) fn homeserver_input(
786+ input: &str,
787+ allow_insecure_loopback: bool,
788+) -> Result<&str, AccountError> {
789+ let input = input.trim();
790+ let invalid = |reason| Err(AccountError::InvalidHomeserver(reason));
791+ if input.is_empty() {
792+ return invalid("it's empty");
793+ }
794+ if input.contains(char::is_whitespace) {
795+ return invalid("it has spaces in it");
796+ }
797+ if !input.contains("://") {
798+ return match <&ruma::ServerName>::try_from(input) {
799+ Ok(_) => Ok(input),
800+ Err(_) => invalid("it's neither a server name nor a url"),
801+ };
802+ }
803+ let Ok(url) = Url::parse(input) else {
804+ return invalid("it isn't a valid url");
805+ };
806+ if !url.username().is_empty() || url.password().is_some() {
807+ return invalid("it has a username in it");
808+ }
809+ let loopback = match url.host() {
810+ None => return invalid("it has no host"),
811+ Some(url::Host::Domain(domain)) => domain.eq_ignore_ascii_case("localhost"),
812+ Some(url::Host::Ipv4(address)) => address.is_loopback(),
813+ Some(url::Host::Ipv6(address)) => address.is_loopback(),
814+ };
815+ match url.scheme() {
816+ "https" => Ok(input),
817+ "http" if allow_insecure_loopback && loopback => Ok(input),
818+ "http" if allow_insecure_loopback => invalid("plain http only reaches loopback hosts"),
819+ "http" => invalid("it isn't https"),
820+ _ => invalid("only http and https are spoken"),
821+ }
822+}
823+
681824 pub(crate) fn account_error(
682825 context: &'static str,
683826 source: impl std::error::Error + SendOutsideWasm + SyncOutsideWasm + 'static,
684827 ) -> AccountError {
685828 AccountError::Operation {
686829 context,
830+ kind: crate::error::classify(&source),
831+ server: ServerError::find(&source),
687832 source: Box::new(source),
688833 }
689834 }
Added file: vendor/macltok/src/accounts.rs+407 additions
@@ -0,0 +1,407 @@
1+use std::sync::Arc;
2+#[cfg(feature = "sqlite")]
3+use std::{collections::BTreeSet, path::PathBuf};
4+
5+use ruma::TransactionId;
6+
7+use crate::{
8+ AccountError, EncryptedSessionStore, OAuthLogin, PasswordLogin, PendingOAuthLogin, Restore,
9+ Session, SessionRecord, SessionStore, SoftLogout, StoreConfig, account::homeserver_input,
10+ engine::StoreLock,
11+};
12+
13+/// every account one owner has, a store each under one root and one passphrase. whoever holds
14+/// the set can resume any soft-logged-out device in it with just its password, and the device's
15+/// store has its e2ee keys, so a set belongs to one person and never to a whole machine
16+pub struct Accounts {
17+ root: Root,
18+ passphrase: String,
19+}
20+
21+enum Root {
22+ #[cfg(feature = "sqlite")]
23+ Directory(PathBuf),
24+ #[cfg(feature = "indexeddb")]
25+ Prefix(String),
26+}
27+
28+/// one account's record and store
29+#[derive(Clone)]
30+pub struct Account {
31+ id: String,
32+ sessions: Arc<EncryptedSessionStore>,
33+}
34+
35+impl Account {
36+ /// stable for the account's life, and safe in a cookie or a url
37+ #[must_use]
38+ pub fn id(&self) -> &str {
39+ &self.id
40+ }
41+
42+ /// for `Termination::retain`. a discarded account drops out of `Accounts::list`
43+ #[must_use]
44+ pub fn sessions(&self) -> Arc<dyn SessionStore> {
45+ self.sessions.clone()
46+ }
47+
48+ pub async fn restore(&self) -> Result<Option<Restore>, AccountError> {
49+ Session::restore(self.sessions()).await
50+ }
51+}
52+
53+impl std::fmt::Debug for Account {
54+ fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55+ formatter
56+ .debug_struct("Account")
57+ .field("id", &self.id)
58+ .finish_non_exhaustive()
59+ }
60+}
61+
62+enum Found {
63+ Nobody,
64+ SoftLoggedOut(Account, SoftLogout),
65+}
66+
67+impl Accounts {
68+ /// each account is a store directory under `root` with its record beside it
69+ #[cfg(feature = "sqlite")]
70+ #[must_use]
71+ pub fn sqlite(root: impl Into<PathBuf>, passphrase: impl Into<String>) -> Self {
72+ Self {
73+ root: Root::Directory(root.into()),
74+ passphrase: passphrase.into(),
75+ }
76+ }
77+
78+ /// each account is a store named `<prefix>::<id>`, listed in `<prefix>::macltok-accounts`
79+ #[cfg(feature = "indexeddb")]
80+ #[must_use]
81+ pub fn indexeddb(prefix: impl Into<String>, passphrase: impl Into<String>) -> Self {
82+ Self {
83+ root: Root::Prefix(prefix.into()),
84+ passphrase: passphrase.into(),
85+ }
86+ }
87+
88+ /// `None` for anything `Account::id` couldn't have been
89+ #[must_use]
90+ pub fn account(&self, id: &str) -> Option<Account> {
91+ let valid =
92+ !id.is_empty() && id.len() <= 64 && id.bytes().all(|b| b.is_ascii_alphanumeric());
93+ valid.then(|| self.open(id.to_owned()))
94+ }
95+
96+ /// every account with a record. a store left without one, by a login that died halfway or a
97+ /// discard that couldn't finish, is deleted on the way past
98+ pub async fn list(&self) -> Result<Vec<Account>, AccountError> {
99+ let mut accounts = Vec::new();
100+ for account in self.slots().await? {
101+ match account.sessions.load().await {
102+ Ok(None) => self.sweep(&account).await,
103+ Ok(Some(_)) | Err(_) => accounts.push(account),
104+ }
105+ }
106+ Ok(accounts)
107+ }
108+
109+ /// a soft-logged-out account the login names is resumed as its own device, keeping its keys.
110+ /// anyone else gets a new account
111+ pub async fn login_password(
112+ &self,
113+ login: PasswordLogin,
114+ ) -> Result<(Account, Session), AccountError> {
115+ homeserver_input(&login.homeserver, login.allow_insecure_loopback)?;
116+ let found = self
117+ .find(|record| record_names(record, &login.homeserver, &login.username))
118+ .await?;
119+ match found {
120+ Found::SoftLoggedOut(account, soft) => {
121+ let store_lock = account.sessions.store().lock().await?;
122+ let login = PasswordLogin {
123+ device_display_name: login.device_display_name,
124+ ..soft.password_login(login.password)
125+ };
126+ let session =
127+ Session::login_password_locked(login, account.sessions(), store_lock).await?;
128+ Ok((account, session))
129+ }
130+ Found::Nobody => {
131+ let (account, store_lock) = self.fresh().await?;
132+ let login = PasswordLogin {
133+ device_id: None,
134+ ..login
135+ };
136+ match Session::login_password_locked(login, account.sessions(), store_lock).await {
137+ Ok(session) => Ok((account, session)),
138+ Err(error) => Err(self.abandon(&account, error).await),
139+ }
140+ }
141+ }
142+ }
143+
144+ /// resumes the soft-logged-out account whose device `login` names, as
145+ /// `SoftLogout::oauth_login` sets it. without one it's a new account, since which account an
146+ /// OAuth login is for isn't known until it finishes
147+ pub async fn begin_oauth(
148+ &self,
149+ login: OAuthLogin,
150+ ) -> Result<(Account, PendingOAuthLogin), AccountError> {
151+ homeserver_input(&login.homeserver, login.allow_insecure_loopback)?;
152+ let device_id = login.device_id.clone();
153+ let found = self
154+ .find(|record| {
155+ record
156+ .soft_logout()
157+ .is_some_and(|soft| device_id.as_ref() == Some(&soft.device_id))
158+ })
159+ .await?;
160+ match found {
161+ Found::SoftLoggedOut(account, soft) => {
162+ let store_lock = account.sessions.store().lock().await?;
163+ let login = OAuthLogin {
164+ homeserver: soft.homeserver,
165+ client_id: login.client_id.or(soft.client_id),
166+ allow_insecure_loopback: true,
167+ ..login
168+ };
169+ let pending =
170+ Session::begin_oauth_locked(login, account.sessions(), store_lock).await?;
171+ Ok((account, pending))
172+ }
173+ Found::Nobody => {
174+ let (account, store_lock) = self.fresh().await?;
175+ let login = OAuthLogin {
176+ device_id: None,
177+ ..login
178+ };
179+ match Session::begin_oauth_locked(login, account.sessions(), store_lock).await {
180+ Ok(pending) => Ok((account, pending)),
181+ Err(error) => Err(self.abandon(&account, error).await),
182+ }
183+ }
184+ }
185+ }
186+
187+ /// a live login for the same person is refused rather than given a second device
188+ async fn find(&self, resumes: impl Fn(&SessionRecord) -> bool) -> Result<Found, AccountError> {
189+ for account in self.slots().await? {
190+ let Ok(Some(record)) = account.sessions.load().await else {
191+ continue;
192+ };
193+ if !resumes(&record) {
194+ continue;
195+ }
196+ return match record.soft_logout() {
197+ Some(soft) => Ok(Found::SoftLoggedOut(account, soft)),
198+ None => Err(AccountError::AlreadyLoggedIn(record.user_id().clone())),
199+ };
200+ }
201+ Ok(Found::Nobody)
202+ }
203+
204+ /// locked before anything else can see it, so `list` never sweeps a login still under way
205+ async fn fresh(&self) -> Result<(Account, StoreLock), AccountError> {
206+ let account = self.open(TransactionId::new().to_string());
207+ let store_lock = account.sessions.store().lock().await?;
208+ #[cfg(feature = "indexeddb")]
209+ {
210+ let Root::Prefix(prefix) = &self.root;
211+ index::add(prefix, &account.id).await?;
212+ }
213+ Ok((account, store_lock))
214+ }
215+
216+ /// a failed first login leaves nothing behind. one whose record didn't save has a live device
217+ /// on the server, so that stays for `UnsavedSession::save`
218+ async fn abandon(&self, account: &Account, error: AccountError) -> AccountError {
219+ if !matches!(error, AccountError::Unsaved { .. }) {
220+ self.sweep(account).await;
221+ }
222+ error
223+ }
224+
225+ /// best effort, since whatever it leaves is found and swept again by the next `list`
226+ async fn sweep(&self, account: &Account) {
227+ let Ok(_store_lock) = account.sessions.store().lock().await else {
228+ return;
229+ };
230+ if !matches!(account.sessions.load().await, Ok(None)) {
231+ return;
232+ }
233+ if account.sessions.discard().await.is_err() {
234+ return;
235+ }
236+ #[cfg(feature = "indexeddb")]
237+ {
238+ let Root::Prefix(prefix) = &self.root;
239+ let _ = index::remove(prefix, &account.id).await;
240+ }
241+ }
242+
243+ fn open(&self, id: String) -> Account {
244+ let sessions = match &self.root {
245+ #[cfg(feature = "sqlite")]
246+ Root::Directory(root) => EncryptedSessionStore::file(
247+ root.join(format!("{id}.session")),
248+ StoreConfig::sqlite(root.join(&id), self.passphrase.clone()),
249+ ),
250+ #[cfg(feature = "indexeddb")]
251+ Root::Prefix(prefix) => EncryptedSessionStore::indexeddb(StoreConfig::indexeddb(
252+ format!("{prefix}::{id}"),
253+ self.passphrase.clone(),
254+ )),
255+ };
256+ Account {
257+ id,
258+ sessions: Arc::new(sessions),
259+ }
260+ }
261+
262+ async fn slots(&self) -> Result<Vec<Account>, AccountError> {
263+ let ids = match &self.root {
264+ #[cfg(feature = "sqlite")]
265+ Root::Directory(root) => directory_slots(root)?,
266+ #[cfg(feature = "indexeddb")]
267+ Root::Prefix(prefix) => index::list(prefix).await?,
268+ };
269+ Ok(ids.iter().filter_map(|id| self.account(id)).collect())
270+ }
271+}
272+
273+/// a localpart or a full user id names the same account, but only on the same server. the server
274+/// is compared by name as well as by url, so a delegated `example.org` still finds the account
275+/// discovery took to `matrix.example.org`
276+fn record_names(record: &SessionRecord, homeserver: &str, username: &str) -> bool {
277+ let user_id = record.user_id();
278+ let username = username.trim();
279+ let user = if username.starts_with('@') {
280+ username.eq_ignore_ascii_case(user_id.as_str())
281+ } else {
282+ username.eq_ignore_ascii_case(user_id.localpart())
283+ };
284+ let typed = authority(homeserver);
285+ user && (typed.eq_ignore_ascii_case(user_id.server_name().as_str())
286+ || typed.eq_ignore_ascii_case(authority(record.homeserver())))
287+}
288+
289+fn authority(homeserver: &str) -> &str {
290+ let homeserver = homeserver.trim();
291+ let rest = homeserver
292+ .split_once("://")
293+ .map_or(homeserver, |(_, rest)| rest);
294+ rest.split('/').next().unwrap_or(rest)
295+}
296+
297+#[cfg(feature = "sqlite")]
298+fn directory_slots(root: &std::path::Path) -> Result<BTreeSet<String>, AccountError> {
299+ use std::io::ErrorKind;
300+
301+ let entries = match std::fs::read_dir(root) {
302+ Ok(entries) => entries,
303+ Err(error) if error.kind() == ErrorKind::NotFound => return Ok(BTreeSet::new()),
304+ Err(error) => return Err(crate::account::account_error("listing the accounts", error)),
305+ };
306+ let mut ids = BTreeSet::new();
307+ for entry in entries {
308+ let entry =
309+ entry.map_err(|error| crate::account::account_error("listing the accounts", error))?;
310+ let name = entry.file_name();
311+ let Some(name) = name.to_str() else {
312+ continue;
313+ };
314+ if entry.path().is_dir() {
315+ ids.insert(name.to_owned());
316+ } else if let Some(id) = name.strip_suffix(".session") {
317+ ids.insert(id.to_owned());
318+ }
319+ }
320+ Ok(ids)
321+}
322+
323+/// a key per account, so two tabs adding at once can't lose one to the other
324+#[cfg(feature = "indexeddb")]
325+mod index {
326+ use std::collections::BTreeSet;
327+
328+ use indexed_db_futures::{
329+ Build,
330+ database::Database,
331+ error::{Error, OpenDbError},
332+ prelude::*,
333+ transaction::{Transaction, TransactionMode},
334+ };
335+
336+ use crate::{AccountError, account::account_error};
337+
338+ const ACCOUNTS: &str = "accounts";
339+
340+ fn name(prefix: &str) -> String {
341+ format!("{prefix}::macltok-accounts")
342+ }
343+
344+ async fn open(prefix: &str) -> Result<Database, OpenDbError> {
345+ Database::open(&name(prefix))
346+ .with_version(1u32)
347+ .with_on_upgrade_needed(|_, transaction: &Transaction<'_>| -> Result<(), Error> {
348+ transaction.db().create_object_store(ACCOUNTS).build()?;
349+ Ok(())
350+ })
351+ .build()?
352+ .await
353+ }
354+
355+ pub(super) async fn list(prefix: &str) -> Result<BTreeSet<String>, AccountError> {
356+ let failed = |error| account_error("listing the accounts", error);
357+ let database = open(prefix).await.map_err(failed)?;
358+ let ids = keys(&database).await;
359+ database.close();
360+ ids.map_err(|error| account_error("listing the accounts", error))
361+ }
362+
363+ pub(super) async fn add(prefix: &str, id: &str) -> Result<(), AccountError> {
364+ let failed = |error| account_error("adding the account", error);
365+ let database = open(prefix).await.map_err(failed)?;
366+ let added = write(&database, |store| {
367+ store.put(true).with_key(id).build()?;
368+ Ok(())
369+ })
370+ .await;
371+ database.close();
372+ added.map_err(|error| account_error("adding the account", error))
373+ }
374+
375+ pub(super) async fn remove(prefix: &str, id: &str) -> Result<(), AccountError> {
376+ let failed = |error| account_error("removing the account", error);
377+ let database = open(prefix).await.map_err(failed)?;
378+ let removed = write(&database, |store| {
379+ store.delete(id).build()?;
380+ Ok(())
381+ })
382+ .await;
383+ database.close();
384+ removed.map_err(|error| account_error("removing the account", error))
385+ }
386+
387+ async fn keys(database: &Database) -> Result<BTreeSet<String>, Error> {
388+ let transaction = database
389+ .transaction(ACCOUNTS)
390+ .with_mode(TransactionMode::Readonly)
391+ .build()?;
392+ let store = transaction.object_store(ACCOUNTS)?;
393+ store.get_all_keys::<String>().await?.collect()
394+ }
395+
396+ async fn write(
397+ database: &Database,
398+ change: impl FnOnce(&indexed_db_futures::object_store::ObjectStore<'_>) -> Result<(), Error>,
399+ ) -> Result<(), Error> {
400+ let transaction = database
401+ .transaction(ACCOUNTS)
402+ .with_mode(TransactionMode::Readwrite)
403+ .build()?;
404+ change(&transaction.object_store(ACCOUNTS)?)?;
405+ transaction.commit().await
406+ }
407+}
Modified file: vendor/macltok/src/engine/media.rs+20 additions−19 deletions
@@ -35,7 +35,7 @@ use super::executor::EngineBoxFuture;
3535 use crate::{
3636 AttachmentEncryption, AttachmentUpload, Error, ErrorKind, Media, MediaFile, MediaId, MediaKind,
3737 MediaLocation, MediaSize, TimelineId,
38- normalise::{MediaRegistration, MediaScope},
38+ normalise::{MediaRegistration, MediaScope, MediaSourceInfo},
3939 upload::prepare_upload,
4040 };
4141
@@ -137,6 +137,9 @@ impl MediaService {
137137 }
138138
139139 async fn registered(&self, media_id: &MediaId) -> Result<MediaRegistration, Error> {
140+ if let Some(uri) = media_id.as_plain() {
141+ return plain_registration(media_id, uri);
142+ }
140143 self.registry
141144 .lock()
142145 .await
@@ -150,24 +153,6 @@ impl MediaService {
150153 })
151154 }
152155
153- pub(crate) async fn mxc_media(&self, uri: &MxcUri, size: MediaSize) -> Result<Media, Error> {
154- if !uri.is_valid() {
155- return Err(Error::new(ErrorKind::Invalid, "mxc uri is malformed"));
156- }
157- let format = match size {
158- MediaSize::Full => MediaFormat::File,
159- MediaSize::Thumbnail { width, height } => thumbnail_format(width, height),
160- };
161- self.fetch_or_file(
162- MediaRequestParameters {
163- source: MediaSource::Plain(uri.to_owned()),
164- format,
165- },
166- None,
167- )
168- .await
169- }
170-
171156 /// plenty of servers won't thumbnail everything, and the file itself is the next best
172157 /// thing. the byte cap still holds either way
173158 async fn fetch_or_file(
@@ -233,6 +218,22 @@ struct ChosenSource {
233218 declared_size: Option<u64>,
234219 }
235220
221+fn plain_registration(media_id: &MediaId, uri: &MxcUri) -> Result<MediaRegistration, Error> {
222+ if !uri.is_valid() {
223+ return Err(Error::new(ErrorKind::Invalid, "mxc uri is malformed"));
224+ }
225+ Ok(MediaRegistration {
226+ id: media_id.clone(),
227+ kind: MediaKind::Image,
228+ full: MediaSourceInfo {
229+ source: MediaSource::Plain(uri.to_owned()),
230+ mimetype: None,
231+ declared_size: None,
232+ },
233+ thumbnail: None,
234+ })
235+}
236+
236237 fn choose_source(media: MediaRegistration, size: MediaSize) -> Result<ChosenSource, Error> {
237238 let (source, format, mimetype, declared_size) = match size {
238239 MediaSize::Full => (
Modified file: vendor/macltok/src/error.rs+68 additions
@@ -33,6 +33,8 @@ pub struct Error {
3333 pub message: String,
3434 /// set when the server wants interactive auth before it'll do the thing
3535 pub challenge: Option<Box<InteractiveAuthChallenge>>,
36+ /// set when the homeserver refused with a matrix error
37+ pub server: Option<ServerError>,
3638 #[source]
3739 #[cfg_attr(feature = "serde", serde(skip))]
3840 source: Option<Arc<SourceError>>,
@@ -68,12 +70,76 @@ pub enum ErrorKind {
6870 Other,
6971 }
7072
73+/// the homeserver's own answer, worth showing a person as it stands
74+#[derive(Clone, Debug, Eq, PartialEq)]
75+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
76+#[non_exhaustive]
77+pub struct ServerError {
78+ pub status: u16,
79+ pub errcode: String,
80+ pub message: String,
81+}
82+
83+impl ServerError {
84+ pub(crate) fn find(source: &(dyn std::error::Error + 'static)) -> Option<Self> {
85+ let mut current = Some(source);
86+ while let Some(error) = current {
87+ let api = if let Some(error) = error.downcast_ref::<matrix_sdk::Error>() {
88+ error.as_client_api_error()
89+ } else if let Some(error) = error.downcast_ref::<HttpError>() {
90+ error.as_client_api_error()
91+ } else {
92+ error.downcast_ref::<ruma::api::error::Error>()
93+ };
94+ if let Some(api) = api
95+ && let ruma::api::error::ErrorBody::Standard(body) = &api.body
96+ {
97+ return Some(Self {
98+ status: api.status_code.as_u16(),
99+ errcode: body.kind.errcode().to_string(),
100+ message: body.message.clone(),
101+ });
102+ }
103+ current = error.source();
104+ }
105+ None
106+ }
107+}
108+
109+impl std::fmt::Display for ServerError {
110+ fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111+ write!(formatter, "{} ({})", self.message, self.errcode)
112+ }
113+}
114+
115+#[cfg(any(feature = "sqlite", feature = "indexeddb"))]
116+pub(crate) fn classify(source: &(dyn std::error::Error + 'static)) -> ErrorKind {
117+ let mut current = Some(source);
118+ while let Some(error) = current {
119+ if let Some(error) = error.downcast_ref::<matrix_sdk::Error>() {
120+ return classify_sdk_error(error);
121+ }
122+ if let Some(error) = error.downcast_ref::<HttpError>() {
123+ return classify_http_error(error);
124+ }
125+ if let Some(error) = error.downcast_ref::<OAuthError>() {
126+ return classify_oauth_error(error);
127+ }
128+ if let Some(error) = error.downcast_ref::<RefreshTokenError>() {
129+ return classify_refresh_token_error(error);
130+ }
131+ current = error.source();
132+ }
133+ ErrorKind::Other
134+}
135+
71136 impl Error {
72137 pub(crate) fn new(kind: ErrorKind, message: impl Into<String>) -> Self {
73138 Self {
74139 kind,
75140 message: message.into(),
76141 challenge: None,
142+ server: None,
77143 source: None,
78144 }
79145 }
@@ -84,6 +150,7 @@ impl Error {
84150 source: impl std::error::Error + SendOutsideWasm + SyncOutsideWasm + 'static,
85151 ) -> Self {
86152 Self {
153+ server: ServerError::find(&source),
87154 source: Some(Arc::new(source)),
88155 ..Self::new(kind, message)
89156 }
@@ -174,6 +241,7 @@ impl PartialEq for Error {
174241 self.kind == other.kind
175242 && self.message == other.message
176243 && self.challenge == other.challenge
244+ && self.server == other.server
177245 }
178246 }
179247
Modified file: vendor/macltok/src/lib.rs+6 additions−1 deletions
@@ -21,6 +21,8 @@ compile_error!("indexeddb only exists in a browser, use sqlite");
2121 #[cfg(any(feature = "sqlite", feature = "indexeddb"))]
2222 mod account;
2323 #[cfg(any(feature = "sqlite", feature = "indexeddb"))]
24+mod accounts;
25+#[cfg(any(feature = "sqlite", feature = "indexeddb"))]
2426 mod session_store;
2527 #[cfg(any(feature = "sqlite", feature = "indexeddb"))]
2628 mod store_lock;
@@ -31,10 +33,12 @@ pub use account::{
3133 Session, SessionRecord, SoftLogout, StoreConfig, UnsavedSession,
3234 };
3335 #[cfg(any(feature = "sqlite", feature = "indexeddb"))]
36+pub use accounts::{Account, Accounts};
37+#[cfg(any(feature = "sqlite", feature = "indexeddb"))]
3438 pub use session_store::{EncryptedSessionStore, SessionStore};
3539
3640 pub use engine::{Engine, EngineConfig, EngineError};
37-pub use error::{Error, ErrorKind};
41+pub use error::{Error, ErrorKind, ServerError};
3842 pub use imbl;
3943 #[cfg(feature = "mirror")]
4044 pub use mirror::{
@@ -58,6 +62,7 @@ pub use model::{
5862 UploadOutcome, UploadThumbnail, UtdCause, VerificationAction, VerificationFailure,
5963 VerificationState, VerificationTarget,
6064 };
65+pub use ruma;
6166 pub use transport::{ClientHandle, EventStream, Pending, SpaceHierarchy};
6267 pub use uiaa::{
6368 InteractiveAuth, InteractiveAuthChallenge, InteractiveAuthFailure, InteractiveAuthFlow,
Modified file: vendor/macltok/src/model.rs+21 additions−12 deletions
@@ -2,8 +2,8 @@ use std::{fmt, sync::Arc};
22
33 use imbl::Vector;
44 use ruma::{
5- OwnedDeviceId, OwnedEventId, OwnedMxcUri, OwnedRoomAliasId, OwnedRoomId, OwnedRoomOrAliasId,
6- OwnedServerName, OwnedTransactionId, OwnedUserId,
5+ MxcUri, OwnedDeviceId, OwnedEventId, OwnedMxcUri, OwnedRoomAliasId, OwnedRoomId,
6+ OwnedRoomOrAliasId, OwnedServerName, OwnedTransactionId, OwnedUserId,
77 events::{
88 AnyGlobalAccountDataEventContent, AnyRoomAccountDataEventContent, AnySyncTimelineEvent,
99 GlobalAccountDataEventType, RoomAccountDataEventType,
@@ -786,7 +786,7 @@ pub struct RoomSummary {
786786 pub direct: bool,
787787 pub favourite: bool,
788788 pub low_priority: bool,
789- pub avatar: Option<OwnedMxcUri>,
789+ pub avatar: Option<MediaId>,
790790 pub latest_event_timestamp_ms: Option<u64>,
791791 pub latest_event: Option<LatestEvent>,
792792 pub notification_mode: Option<RoomNotificationMode>,
@@ -812,7 +812,7 @@ pub struct RoomSummary {
812812 pub struct RoomHero {
813813 pub user_id: OwnedUserId,
814814 pub display_name: Option<String>,
815- pub avatar: Option<OwnedMxcUri>,
815+ pub avatar: Option<MediaId>,
816816 }
817817
818818 /// enough for a room list preview line. `kind` and `body` are only set for messages,
@@ -834,7 +834,7 @@ pub struct LatestEvent {
834834 pub struct Profile {
835835 pub user_id: OwnedUserId,
836836 pub display_name: Option<String>,
837- pub avatar: Option<OwnedMxcUri>,
837+ pub avatar: Option<MediaId>,
838838 }
839839
840840 /// one room in a space's hierarchy, as the server describes it, which may be a room you're not in
@@ -845,7 +845,7 @@ pub struct SpaceChild {
845845 pub room_id: OwnedRoomId,
846846 pub name: String,
847847 pub topic: Option<String>,
848- pub avatar: Option<OwnedMxcUri>,
848+ pub avatar: Option<MediaId>,
849849 pub canonical_alias: Option<OwnedRoomAliasId>,
850850 pub joined_members: u64,
851851 pub space: bool,
@@ -903,7 +903,7 @@ pub struct PushNotification {
903903 pub struct PushNotificationSender {
904904 pub user_id: OwnedUserId,
905905 pub display_name: Option<String>,
906- pub avatar_url: Option<OwnedMxcUri>,
906+ pub avatar_url: Option<MediaId>,
907907 pub name_ambiguous: bool,
908908 }
909909
@@ -913,7 +913,7 @@ pub struct PushNotificationSender {
913913 pub struct PushNotificationRoom {
914914 pub room_id: OwnedRoomId,
915915 pub name: String,
916- pub avatar_url: Option<OwnedMxcUri>,
916+ pub avatar_url: Option<MediaId>,
917917 pub canonical_alias: Option<OwnedRoomAliasId>,
918918 pub topic: Option<String>,
919919 pub encrypted: Option<bool>,
@@ -1107,7 +1107,7 @@ pub struct MemberSummary {
11071107 pub user_id: OwnedUserId,
11081108 pub name: String,
11091109 pub display_name: Option<String>,
1110- pub avatar: Option<OwnedMxcUri>,
1110+ pub avatar: Option<MediaId>,
11111111 pub membership: MemberMembership,
11121112 pub own: bool,
11131113 pub name_ambiguous: bool,
@@ -1422,7 +1422,7 @@ impl From<OwnedEventId> for EventRef {
14221422 pub struct Sender {
14231423 pub user_id: OwnedUserId,
14241424 pub display_name: Option<String>,
1425- pub avatar: Option<OwnedMxcUri>,
1425+ pub avatar: Option<MediaId>,
14261426 /// someone else in the room goes by the same name, so show the user id too
14271427 pub name_ambiguous: bool,
14281428 }
@@ -1458,12 +1458,12 @@ pub enum TimelineContent {
14581458 Profile {
14591459 user_id: OwnedUserId,
14601460 display_name: Option<Change<Option<String>>>,
1461- avatar: Option<Change<Option<OwnedMxcUri>>>,
1461+ avatar: Option<Change<Option<MediaId>>>,
14621462 },
14631463 /// `None` means it was cleared, or the event was redacted
14641464 RoomName(Option<String>),
14651465 RoomTopic(Option<String>),
1466- RoomAvatar(Option<OwnedMxcUri>),
1466+ RoomAvatar(Option<MediaId>),
14671467 EncryptionEnabled,
14681468 RoomCreated,
14691469 RoomUpgraded {
@@ -1635,6 +1635,15 @@ impl MediaId {
16351635 Self(value)
16361636 }
16371637
1638+ /// plain media carries no keys, so it needs no registering and never expires
1639+ pub(crate) fn plain<U: AsRef<str> + ?Sized>(uri: &U) -> Self {
1640+ Self(uri.as_ref().to_owned())
1641+ }
1642+
1643+ pub(crate) fn as_plain(&self) -> Option<&MxcUri> {
1644+ self.0.starts_with("mxc://").then(|| self.0.as_str().into())
1645+ }
1646+
16381647 #[must_use]
16391648 pub fn as_str(&self) -> &str {
16401649 &self.0
Modified file: vendor/macltok/src/normalise/member.rs+2 additions−2 deletions
@@ -1,14 +1,14 @@
11 use matrix_sdk::room::RoomMember;
22 use ruma::events::room::member::MembershipState;
33
4-use crate::{MemberMembership, MemberSummary};
4+use crate::{MediaId, MemberMembership, MemberSummary};
55
66 pub(crate) fn member_summary(member: RoomMember) -> MemberSummary {
77 MemberSummary {
88 user_id: member.user_id().to_owned(),
99 name: member.name().to_owned(),
1010 display_name: member.display_name().map(ToOwned::to_owned),
11- avatar: member.avatar_url().map(ToOwned::to_owned),
11+ avatar: member.avatar_url().map(MediaId::plain),
1212 membership: match member.membership() {
1313 MembershipState::Join => MemberMembership::Joined,
1414 MembershipState::Invite => MemberMembership::Invited,
Modified file: vendor/macltok/src/normalise/mod.rs+1 additions−1 deletions
@@ -3,7 +3,7 @@ mod member;
33 mod room;
44 mod timeline;
55
6-pub(crate) use media::{MediaRegistration, MediaScope};
6+pub(crate) use media::{MediaRegistration, MediaScope, MediaSourceInfo};
77 pub(crate) use member::member_summary;
88 pub(crate) use room::notification_mode;
99 pub(crate) use room::{room_summaries, space_child};
Modified file: vendor/macltok/src/normalise/room.rs+4 additions−4 deletions
@@ -14,7 +14,7 @@ use ruma::OwnedRoomId;
1414
1515 use super::message_kind;
1616 use crate::{
17- LatestEvent, MessageKind, RoomHero, RoomMembership, RoomNotificationMode, RoomSummary,
17+ LatestEvent, MediaId, MessageKind, RoomHero, RoomMembership, RoomNotificationMode, RoomSummary,
1818 SendState, SpaceChild,
1919 };
2020
@@ -70,7 +70,7 @@ async fn room_summary(room: &Room, spaces: &SpaceService) -> RoomSummary {
7070 direct: room.is_direct().await.unwrap_or(false),
7171 favourite: room.is_favourite(),
7272 low_priority: room.is_low_priority(),
73- avatar: room.avatar_url(),
73+ avatar: room.avatar_url().as_deref().map(MediaId::plain),
7474 latest_event_timestamp_ms: room
7575 .latest_event_timestamp()
7676 .map(|value| value.get().into()),
@@ -87,7 +87,7 @@ async fn room_summary(room: &Room, spaces: &SpaceService) -> RoomSummary {
8787 .map(|hero| RoomHero {
8888 user_id: hero.user_id,
8989 display_name: hero.display_name,
90- avatar: hero.avatar_url,
90+ avatar: hero.avatar_url.as_deref().map(MediaId::plain),
9191 })
9292 .collect(),
9393 successor: room.successor_room().map(|successor| successor.room_id),
@@ -197,7 +197,7 @@ pub(crate) fn space_child(room: SpaceRoom) -> SpaceChild {
197197 name: room.display_name,
198198 room_id: room.room_id,
199199 topic: room.topic,
200- avatar: room.avatar_url,
200+ avatar: room.avatar_url.as_deref().map(MediaId::plain),
201201 canonical_alias: room.canonical_alias,
202202 joined_members: room.num_joined_members,
203203 space: room.room_type == Some(ruma::room::RoomType::Space),
Modified file: vendor/macltok/src/normalise/timeline.rs+7 additions−7 deletions
@@ -19,9 +19,9 @@ use ruma::{
1919 };
2020
2121 use crate::{
22- Change, MembershipChange, Message, MessageKind, MessageMentions, Reaction, ReplyDetails,
23- ReplyReference, SendState, Sender, ThreadSummary, TimelineContent, TimelineDiff, TimelineEvent,
24- TimelineItem, TimelineItemId, UtdCause,
22+ Change, MediaId, MembershipChange, Message, MessageKind, MessageMentions, Reaction,
23+ ReplyDetails, ReplyReference, SendState, Sender, ThreadSummary, TimelineContent, TimelineDiff,
24+ TimelineEvent, TimelineItem, TimelineItemId, UtdCause,
2525 };
2626
2727 use super::media::{
@@ -198,8 +198,8 @@ fn content(content: &TimelineItemContent, media_identity: Option<&str>) -> Timel
198198 new: change.new.clone(),
199199 }),
200200 avatar: change.avatar_url_change().map(|change| Change {
201- old: change.old.clone(),
202- new: change.new.clone(),
201+ old: change.old.as_deref().map(MediaId::plain),
202+ new: change.new.as_deref().map(MediaId::plain),
203203 }),
204204 },
205205 TimelineItemContent::OtherState(state) => match state.content() {
@@ -210,7 +210,7 @@ fn content(content: &TimelineItemContent, media_identity: Option<&str>) -> Timel
210210 original(change).and_then(|content| non_empty(&content.topic)),
211211 ),
212212 AnyOtherStateEventContentChange::RoomAvatar(change) => TimelineContent::RoomAvatar(
213- original(change).and_then(|content| content.url.clone()),
213+ original(change).and_then(|content| content.url.as_deref().map(MediaId::plain)),
214214 ),
215215 AnyOtherStateEventContentChange::RoomEncryption(_) => {
216216 TimelineContent::EncryptionEnabled
@@ -380,7 +380,7 @@ fn sender(user_id: &UserId, profile: &TimelineDetails<Profile>) -> Sender {
380380 Sender {
381381 user_id: user_id.to_owned(),
382382 display_name: profile.and_then(|profile| profile.display_name.clone()),
383- avatar: profile.and_then(|profile| profile.avatar_url.clone()),
383+ avatar: profile.and_then(|profile| profile.avatar_url.as_deref().map(MediaId::plain)),
384384 name_ambiguous: profile.is_some_and(|profile| profile.display_name_ambiguous),
385385 }
386386 }
Modified file: vendor/macltok/src/transport.rs+10 additions−10 deletions
@@ -15,7 +15,7 @@ use matrix_sdk_ui::{
1515 sync_service::SyncService,
1616 };
1717 use ruma::{
18- EventId, MxcUri, OwnedDeviceId, OwnedEventId, OwnedMxcUri, OwnedRoomId, OwnedRoomOrAliasId,
18+ EventId, OwnedDeviceId, OwnedEventId, OwnedMxcUri, OwnedRoomId, OwnedRoomOrAliasId,
1919 OwnedServerName, OwnedTransactionId, OwnedUserId, RoomId, UserId,
2020 events::{
2121 AnyGlobalAccountDataEventContent, AnyRoomAccountDataEvent, AnyRoomAccountDataEventContent,
@@ -555,7 +555,8 @@ impl ClientHandle {
555555 .await
556556 }
557557
558- /// bytes for media a retained timeline showed you. concurrent asks share one download
558+ /// bytes for an avatar, or for media a retained timeline showed you. concurrent asks share
559+ /// one download
559560 pub async fn media(&self, media_id: &MediaId, size: MediaSize) -> Result<Media, Error> {
560561 self.media.timeline_media(media_id, size).await
561562 }
@@ -570,11 +571,6 @@ impl ClientHandle {
570571 self.media.media_location(media_id, size).await
571572 }
572573
573- /// bytes for any plain mxc uri, avatars mostly. same byte cap as everything else
574- pub async fn mxc_media(&self, uri: &MxcUri, size: MediaSize) -> Result<Media, Error> {
575- self.media.mxc_media(uri, size).await
576- }
577-
578574 /// the space graph builds on first use and then keeps itself current
579575 async fn spaces(&self) -> &SpaceService {
580576 self.spaces
@@ -908,7 +904,11 @@ fn profile(
908904 Ok(Profile {
909905 user_id,
910906 display_name: response.get_static::<DisplayName>().map_err(malformed)?,
911- avatar: response.get_static::<AvatarUrl>().map_err(malformed)?,
907+ avatar: response
908+ .get_static::<AvatarUrl>()
909+ .map_err(malformed)?
910+ .as_deref()
911+ .map(MediaId::plain),
912912 })
913913 }
914914
@@ -1069,13 +1069,13 @@ fn normalise_push_notification(
10691069 sender: PushNotificationSender {
10701070 user_id: item.event.sender().to_owned(),
10711071 display_name: item.sender_display_name,
1072- avatar_url: item.sender_avatar_url.map(Into::into),
1072+ avatar_url: item.sender_avatar_url.as_deref().map(MediaId::plain),
10731073 name_ambiguous: item.is_sender_name_ambiguous,
10741074 },
10751075 room: PushNotificationRoom {
10761076 room_id: room_id.to_owned(),
10771077 name: item.room_computed_display_name,
1078- avatar_url: item.room_avatar_url.map(Into::into),
1078+ avatar_url: item.room_avatar_url.as_deref().map(MediaId::plain),
10791079 canonical_alias: item
10801080 .room_canonical_alias
10811081 .as_deref()
Added file: web/.storybook/main.ts+10 additions
@@ -0,0 +1,10 @@
1+import type { StorybookConfig } from '@storybook/svelte-vite';
2+
3+const config: StorybookConfig = {
4+ stories: ['../src/**/*.stories.ts'],
5+ addons: ['@storybook/addon-docs', '@storybook/addon-mcp'],
6+ framework: '@storybook/svelte-vite',
7+ core: { disableTelemetry: true }
8+};
9+
10+export default config;
Added file: web/.storybook/preview.ts+10 additions
@@ -0,0 +1,10 @@
1+import type { Preview } from '@storybook/svelte-vite';
2+import '../src/app.css';
3+
4+const preview: Preview = {
5+ parameters: {
6+ layout: 'padded'
7+ }
8+};
9+
10+export default preview;
Added file: web/debug-storybook.log+10 additions
@@ -0,0 +1,10 @@
1+[14:57:10.321] [INFO] storybook v10.6.1
2+[14:57:11.856] [ERROR] Error: Unable to find a usable package manager within NPM, PNPM, Yarn and Yarn 2
3+ at JsPackageManagerFactory.getPackageManagerType (file:///nix/store/2dvvvgj00fsbkrkpfnc20fh8rf6q2sjg-malice-web-node-modules-0.1.0/node_modules/storybook/dist/_node-chunks/chunk-A5M3SXLF.js:17262:11)
4+ at JsPackageManagerFactory.getPackageManager (file:///nix/store/2dvvvgj00fsbkrkpfnc20fh8rf6q2sjg-malice-web-node-modules-0.1.0/node_modules/storybook/dist/_node-chunks/chunk-A5M3SXLF.js:17281:35)
5+ at buildDevStandalone (file:///nix/store/2dvvvgj00fsbkrkpfnc20fh8rf6q2sjg-malice-web-node-modules-0.1.0/node_modules/storybook/dist/core-server/index.js:9242:48)
6+ at async withTelemetry (file:///nix/store/2dvvvgj00fsbkrkpfnc20fh8rf6q2sjg-malice-web-node-modules-0.1.0/node_modules/storybook/dist/_node-chunks/chunk-IRB26IIA.js:142:12)
7+ at async dev (file:///nix/store/2dvvvgj00fsbkrkpfnc20fh8rf6q2sjg-malice-web-node-modules-0.1.0/node_modules/storybook/dist/bin/core.js:4877:3)
8+ at async _Command.<anonymous> (file:///nix/store/2dvvvgj00fsbkrkpfnc20fh8rf6q2sjg-malice-web-node-modules-0.1.0/node_modules/storybook/dist/bin/core.js:4985:3)
9+[14:57:11.857] [WARN] Broken build, fix the error above.
10+You may need to refresh the browser.
Modified file: web/package-lock.json+3137 additions−634 deletions
@@ -8,31 +8,98 @@
88 "name": "malice-web",
99 "version": "0.1.0",
1010 "dependencies": {
11- "svelte": "^4.2.19"
11+ "svelte": "^5.39.0"
1212 },
1313 "devDependencies": {
14- "@sveltejs/vite-plugin-svelte": "^3.1.2",
15- "typescript": "~5.6.3",
14+ "@storybook/addon-docs": "^10.6.1",
15+ "@storybook/addon-mcp": "^10.6.1",
16+ "@storybook/svelte-vite": "^10.6.1",
17+ "@sveltejs/vite-plugin-svelte": "^4.0.4",
18+ "storybook": "^10.6.1",
19+ "typescript": "~6.0.3",
1620 "vite": "^5.4.11"
1721 }
1822 },
19- "node_modules/@ampproject/remapping": {
20- "version": "2.3.0",
21- "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
22- "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
23- "license": "Apache-2.0",
23+ "node_modules/@adobe/css-tools": {
24+ "version": "4.5.0",
25+ "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.5.0.tgz",
26+ "integrity": "sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q==",
27+ "dev": true,
28+ "license": "MIT"
29+ },
30+ "node_modules/@babel/code-frame": {
31+ "version": "7.29.7",
32+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
33+ "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
34+ "dev": true,
35+ "license": "MIT",
2436 "dependencies": {
25- "@jridgewell/gen-mapping": "^0.3.5",
26- "@jridgewell/trace-mapping": "^0.3.24"
37+ "@babel/helper-validator-identifier": "^7.29.7",
38+ "js-tokens": "^4.0.0",
39+ "picocolors": "^1.1.1"
2740 },
2841 "engines": {
29- "node": ">=6.0.0"
42+ "node": ">=6.9.0"
43+ }
44+ },
45+ "node_modules/@babel/helper-validator-identifier": {
46+ "version": "7.29.7",
47+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
48+ "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
49+ "dev": true,
50+ "license": "MIT",
51+ "engines": {
52+ "node": ">=6.9.0"
53+ }
54+ },
55+ "node_modules/@babel/runtime": {
56+ "version": "7.29.10",
57+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.10.tgz",
58+ "integrity": "sha512-NpugTzbKgGrNe45Ui32cN8vCFFMxVtP/9rtfwv0nP5FRDAjvYBxJcLVpYA9mr1jxP6cnIT7x9LUzuDTbWYYNRA==",
59+ "dev": true,
60+ "license": "MIT",
61+ "engines": {
62+ "node": ">=6.9.0"
63+ }
64+ },
65+ "node_modules/@emnapi/core": {
66+ "version": "1.9.2",
67+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz",
68+ "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==",
69+ "dev": true,
70+ "license": "MIT",
71+ "optional": true,
72+ "dependencies": {
73+ "@emnapi/wasi-threads": "1.2.1",
74+ "tslib": "^2.4.0"
75+ }
76+ },
77+ "node_modules/@emnapi/runtime": {
78+ "version": "1.9.2",
79+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz",
80+ "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==",
81+ "dev": true,
82+ "license": "MIT",
83+ "optional": true,
84+ "dependencies": {
85+ "tslib": "^2.4.0"
86+ }
87+ },
88+ "node_modules/@emnapi/wasi-threads": {
89+ "version": "1.2.1",
90+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
91+ "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
92+ "dev": true,
93+ "license": "MIT",
94+ "optional": true,
95+ "dependencies": {
96+ "tslib": "^2.4.0"
3097 }
3198 },
3299 "node_modules/@esbuild/aix-ppc64": {
33- "version": "0.21.5",
34- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
35- "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
100+ "version": "0.28.2",
101+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz",
102+ "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==",
36103 "cpu": [
37104 "ppc64"
38105 ],
@@ -43,13 +110,13 @@
43110 "aix"
44111 ],
45112 "engines": {
46- "node": ">=12"
113+ "node": ">=18"
47114 }
48115 },
49116 "node_modules/@esbuild/android-arm": {
50- "version": "0.21.5",
51- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
52- "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
117+ "version": "0.28.2",
118+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz",
119+ "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==",
53120 "cpu": [
54121 "arm"
55122 ],
@@ -60,13 +127,13 @@
60127 "android"
61128 ],
62129 "engines": {
63- "node": ">=12"
130+ "node": ">=18"
64131 }
65132 },
66133 "node_modules/@esbuild/android-arm64": {
67- "version": "0.21.5",
68- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
69- "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
134+ "version": "0.28.2",
135+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz",
136+ "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==",
70137 "cpu": [
71138 "arm64"
72139 ],
@@ -77,13 +144,13 @@
77144 "android"
78145 ],
79146 "engines": {
80- "node": ">=12"
147+ "node": ">=18"
81148 }
82149 },
83150 "node_modules/@esbuild/android-x64": {
84- "version": "0.21.5",
85- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
86- "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
151+ "version": "0.28.2",
152+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz",
153+ "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==",
87154 "cpu": [
88155 "x64"
89156 ],
@@ -94,13 +161,13 @@
94161 "android"
95162 ],
96163 "engines": {
97- "node": ">=12"
164+ "node": ">=18"
98165 }
99166 },
100167 "node_modules/@esbuild/darwin-arm64": {
101- "version": "0.21.5",
102- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
103- "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
168+ "version": "0.28.2",
169+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz",
170+ "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==",
104171 "cpu": [
105172 "arm64"
106173 ],
@@ -111,13 +178,13 @@
111178 "darwin"
112179 ],
113180 "engines": {
114- "node": ">=12"
181+ "node": ">=18"
115182 }
116183 },
117184 "node_modules/@esbuild/darwin-x64": {
118- "version": "0.21.5",
119- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
120- "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
185+ "version": "0.28.2",
186+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz",
187+ "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==",
121188 "cpu": [
122189 "x64"
123190 ],
@@ -128,13 +195,13 @@
128195 "darwin"
129196 ],
130197 "engines": {
131- "node": ">=12"
198+ "node": ">=18"
132199 }
133200 },
134201 "node_modules/@esbuild/freebsd-arm64": {
135- "version": "0.21.5",
136- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
137- "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
202+ "version": "0.28.2",
203+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz",
204+ "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==",
138205 "cpu": [
139206 "arm64"
140207 ],
@@ -145,13 +212,13 @@
145212 "freebsd"
146213 ],
147214 "engines": {
148- "node": ">=12"
215+ "node": ">=18"
149216 }
150217 },
151218 "node_modules/@esbuild/freebsd-x64": {
152- "version": "0.21.5",
153- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
154- "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
219+ "version": "0.28.2",
220+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz",
221+ "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==",
155222 "cpu": [
156223 "x64"
157224 ],
@@ -162,13 +229,13 @@
162229 "freebsd"
163230 ],
164231 "engines": {
165- "node": ">=12"
232+ "node": ">=18"
166233 }
167234 },
168235 "node_modules/@esbuild/linux-arm": {
169- "version": "0.21.5",
170- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
171- "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
236+ "version": "0.28.2",
237+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz",
238+ "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==",
172239 "cpu": [
173240 "arm"
174241 ],
@@ -179,13 +246,13 @@
179246 "linux"
180247 ],
181248 "engines": {
182- "node": ">=12"
249+ "node": ">=18"
183250 }
184251 },
185252 "node_modules/@esbuild/linux-arm64": {
186- "version": "0.21.5",
187- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
188- "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
253+ "version": "0.28.2",
254+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz",
255+ "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==",
189256 "cpu": [
190257 "arm64"
191258 ],
@@ -196,13 +263,13 @@
196263 "linux"
197264 ],
198265 "engines": {
199- "node": ">=12"
266+ "node": ">=18"
200267 }
201268 },
202269 "node_modules/@esbuild/linux-ia32": {
203- "version": "0.21.5",
204- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
205- "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
270+ "version": "0.28.2",
271+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz",
272+ "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==",
206273 "cpu": [
207274 "ia32"
208275 ],
@@ -213,13 +280,13 @@
213280 "linux"
214281 ],
215282 "engines": {
216- "node": ">=12"
283+ "node": ">=18"
217284 }
218285 },
219286 "node_modules/@esbuild/linux-loong64": {
220- "version": "0.21.5",
221- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
222- "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
287+ "version": "0.28.2",
288+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz",
289+ "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==",
223290 "cpu": [
224291 "loong64"
225292 ],
@@ -230,13 +297,13 @@
230297 "linux"
231298 ],
232299 "engines": {
233- "node": ">=12"
300+ "node": ">=18"
234301 }
235302 },
236303 "node_modules/@esbuild/linux-mips64el": {
237- "version": "0.21.5",
238- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
239- "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
304+ "version": "0.28.2",
305+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz",
306+ "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==",
240307 "cpu": [
241308 "mips64el"
242309 ],
@@ -247,13 +314,13 @@
247314 "linux"
248315 ],
249316 "engines": {
250- "node": ">=12"
317+ "node": ">=18"
251318 }
252319 },
253320 "node_modules/@esbuild/linux-ppc64": {
254- "version": "0.21.5",
255- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
256- "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
321+ "version": "0.28.2",
322+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz",
323+ "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==",
257324 "cpu": [
258325 "ppc64"
259326 ],
@@ -264,13 +331,13 @@
264331 "linux"
265332 ],
266333 "engines": {
267- "node": ">=12"
334+ "node": ">=18"
268335 }
269336 },
270337 "node_modules/@esbuild/linux-riscv64": {
271- "version": "0.21.5",
272- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
273- "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
338+ "version": "0.28.2",
339+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz",
340+ "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==",
274341 "cpu": [
275342 "riscv64"
276343 ],
@@ -281,13 +348,13 @@
281348 "linux"
282349 ],
283350 "engines": {
284- "node": ">=12"
351+ "node": ">=18"
285352 }
286353 },
287354 "node_modules/@esbuild/linux-s390x": {
288- "version": "0.21.5",
289- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
290- "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
355+ "version": "0.28.2",
356+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz",
357+ "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==",
291358 "cpu": [
292359 "s390x"
293360 ],
@@ -298,13 +365,13 @@
298365 "linux"
299366 ],
300367 "engines": {
301- "node": ">=12"
368+ "node": ">=18"
302369 }
303370 },
304371 "node_modules/@esbuild/linux-x64": {
305- "version": "0.21.5",
306- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
307- "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
372+ "version": "0.28.2",
373+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz",
374+ "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==",
308375 "cpu": [
309376 "x64"
310377 ],
@@ -315,13 +382,30 @@
315382 "linux"
316383 ],
317384 "engines": {
318- "node": ">=12"
385+ "node": ">=18"
386+ }
387+ },
388+ "node_modules/@esbuild/netbsd-arm64": {
389+ "version": "0.28.2",
390+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz",
391+ "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==",
392+ "cpu": [
393+ "arm64"
394+ ],
395+ "dev": true,
396+ "license": "MIT",
397+ "optional": true,
398+ "os": [
399+ "netbsd"
400+ ],
401+ "engines": {
402+ "node": ">=18"
319403 }
320404 },
321405 "node_modules/@esbuild/netbsd-x64": {
322- "version": "0.21.5",
323- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
324- "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
406+ "version": "0.28.2",
407+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz",
408+ "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==",
325409 "cpu": [
326410 "x64"
327411 ],
@@ -332,13 +416,30 @@
332416 "netbsd"
333417 ],
334418 "engines": {
335- "node": ">=12"
419+ "node": ">=18"
420+ }
421+ },
422+ "node_modules/@esbuild/openbsd-arm64": {
423+ "version": "0.28.2",
424+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz",
425+ "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==",
426+ "cpu": [
427+ "arm64"
428+ ],
429+ "dev": true,
430+ "license": "MIT",
431+ "optional": true,
432+ "os": [
433+ "openbsd"
434+ ],
435+ "engines": {
436+ "node": ">=18"
336437 }
337438 },
338439 "node_modules/@esbuild/openbsd-x64": {
339- "version": "0.21.5",
340- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
341- "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
440+ "version": "0.28.2",
441+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz",
442+ "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==",
342443 "cpu": [
343444 "x64"
344445 ],
@@ -349,13 +450,30 @@
349450 "openbsd"
350451 ],
351452 "engines": {
352- "node": ">=12"
453+ "node": ">=18"
454+ }
455+ },
456+ "node_modules/@esbuild/openharmony-arm64": {
457+ "version": "0.28.2",
458+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz",
459+ "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==",
460+ "cpu": [
461+ "arm64"
462+ ],
463+ "dev": true,
464+ "license": "MIT",
465+ "optional": true,
466+ "os": [
467+ "openharmony"
468+ ],
469+ "engines": {
470+ "node": ">=18"
353471 }
354472 },
355473 "node_modules/@esbuild/sunos-x64": {
356- "version": "0.21.5",
357- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
358- "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
474+ "version": "0.28.2",
475+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz",
476+ "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==",
359477 "cpu": [
360478 "x64"
361479 ],
@@ -366,13 +484,13 @@
366484 "sunos"
367485 ],
368486 "engines": {
369- "node": ">=12"
487+ "node": ">=18"
370488 }
371489 },
372490 "node_modules/@esbuild/win32-arm64": {
373- "version": "0.21.5",
374- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
375- "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
491+ "version": "0.28.2",
492+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz",
493+ "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==",
376494 "cpu": [
377495 "arm64"
378496 ],
@@ -383,13 +501,13 @@
383501 "win32"
384502 ],
385503 "engines": {
386- "node": ">=12"
504+ "node": ">=18"
387505 }
388506 },
389507 "node_modules/@esbuild/win32-ia32": {
390- "version": "0.21.5",
391- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
392- "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
508+ "version": "0.28.2",
509+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz",
510+ "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==",
393511 "cpu": [
394512 "ia32"
395513 ],
@@ -400,13 +518,13 @@
400518 "win32"
401519 ],
402520 "engines": {
403- "node": ">=12"
521+ "node": ">=18"
404522 }
405523 },
406524 "node_modules/@esbuild/win32-x64": {
407- "version": "0.21.5",
408- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
409- "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
525+ "version": "0.28.2",
526+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz",
527+ "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==",
410528 "cpu": [
411529 "x64"
412530 ],
@@ -417,7 +535,7 @@
417535 "win32"
418536 ],
419537 "engines": {
420- "node": ">=12"
538+ "node": ">=18"
421539 }
422540 },
423541 "node_modules/@jridgewell/gen-mapping": {
@@ -430,6 +548,16 @@
430548 "@jridgewell/trace-mapping": "^0.3.24"
431549 }
432550 },
551+ "node_modules/@jridgewell/remapping": {
552+ "version": "2.3.5",
553+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
554+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
555+ "license": "MIT",
556+ "dependencies": {
557+ "@jridgewell/gen-mapping": "^0.3.5",
558+ "@jridgewell/trace-mapping": "^0.3.24"
559+ }
560+ },
433561 "node_modules/@jridgewell/resolve-uri": {
434562 "version": "3.1.2",
435563 "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
@@ -455,6 +583,24 @@
455583 "@jridgewell/sourcemap-codec": "^1.4.14"
456584 }
457585 },
586+ "node_modules/@mdx-js/react": {
587+ "version": "3.1.1",
588+ "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.1.tgz",
589+ "integrity": "sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw==",
590+ "dev": true,
591+ "license": "MIT",
592+ "dependencies": {
593+ "@types/mdx": "^2.0.0"
594+ },
595+ "funding": {
596+ "type": "opencollective",
597+ "url": "https://opencollective.com/unified"
598+ },
599+ "peerDependencies": {
600+ "@types/react": ">=16",
601+ "react": ">=16"
602+ }
603+ },
458604 "node_modules/@napi-rs/lzma-linux-x64-gnu": {
459605 "version": "1.5.1",
460606 "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz",
@@ -472,10 +618,32 @@
472618 "node": "^22.20 || ^24.12 || >=25"
473619 }
474620 },
475- "node_modules/@rollup/rollup-android-arm-eabi": {
476- "version": "4.63.4",
477- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.4.tgz",
478- "integrity": "sha512-I+BSHzTAhKN2n7ZwGZsegGcZjDpLqFOMAtJz/u6uFGe0pUFbq56dEHjqJV/ZUdRJtNXNxA+hREUatZBvMR3Oiw==",
621+ "node_modules/@napi-rs/wasm-runtime": {
622+ "version": "1.2.5",
623+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.5.tgz",
624+ "integrity": "sha512-HshSvXzmBxNzqejd3k/KemgQ3hdREUYFRRrgxOZ+gPljDIsO2SDZrVZMd4Z/HsxudQbE62OVyVFE8z9BCurfjA==",
625+ "dev": true,
626+ "license": "MIT",
627+ "optional": true,
628+ "dependencies": {
629+ "@tybys/wasm-util": "^0.10.3"
630+ },
631+ "engines": {
632+ "node": "^20.19.0 || ^22.13.0 || >=23.5.0"
633+ },
634+ "funding": {
635+ "type": "github",
636+ "url": "https://github.com/sponsors/Brooooooklyn"
637+ },
638+ "peerDependencies": {
639+ "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4",
640+ "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4"
641+ }
642+ },
643+ "node_modules/@oxc-parser/binding-android-arm-eabi": {
644+ "version": "0.127.0",
645+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.127.0.tgz",
646+ "integrity": "sha512-0LC7ye4hvqbIKxAzThzvswgHLFu2AURKzYLeSVvLdu2TBOYWQDmHnTqPLeA597BcUCxiLqLsS4CJ5uoI5WYWCQ==",
479647 "cpu": [
480648 "arm"
481649 ],
@@ -484,12 +652,15 @@
484652 "optional": true,
485653 "os": [
486654 "android"
487- ]
655+ ],
656+ "engines": {
657+ "node": "^20.19.0 || >=22.12.0"
658+ }
488659 },
489- "node_modules/@rollup/rollup-android-arm64": {
490- "version": "4.63.4",
491- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.63.4.tgz",
492- "integrity": "sha512-pu3BdjS2LtEzRu2elmGzS3fIeWSZy4BMDIaLNwjorO76+k2d0LMluijhsDx3KQyQBQ/lLUZCQA9/s6csvUfuhw==",
660+ "node_modules/@oxc-parser/binding-android-arm64": {
661+ "version": "0.127.0",
662+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.127.0.tgz",
663+ "integrity": "sha512-b5jtVTH6AU5CJXHNdj7Jj9IEiR9yVjjnwHzPJhGyHGPdcsZSzBCkS9GBbV33niRMvKthDwQRFRJfI4a+k4PvYg==",
493664 "cpu": [
494665 "arm64"
495666 ],
@@ -498,12 +669,15 @@
498669 "optional": true,
499670 "os": [
500671 "android"
501- ]
672+ ],
673+ "engines": {
674+ "node": "^20.19.0 || >=22.12.0"
675+ }
502676 },
503- "node_modules/@rollup/rollup-darwin-arm64": {
504- "version": "4.63.4",
505- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.63.4.tgz",
506- "integrity": "sha512-xfSrj9MHnWK9GaSqT9U0ImHtH/N8WZlHLx4cZHiuLcqs640hvZ3hLPd5UR2AZS57FaE8HrRUSpltbZdWRxHiDA==",
677+ "node_modules/@oxc-parser/binding-darwin-arm64": {
678+ "version": "0.127.0",
679+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.127.0.tgz",
680+ "integrity": "sha512-obCE8B7ISKkJidjlhv9xRGJPOSDG2Yu6PRga9Ruaz35uintHxbp1Ki/Yc71wx4rj3Edrm0a1kzG1TAwit0wFpg==",
507681 "cpu": [
508682 "arm64"
509683 ],
@@ -512,12 +686,15 @@
512686 "optional": true,
513687 "os": [
514688 "darwin"
515- ]
689+ ],
690+ "engines": {
691+ "node": "^20.19.0 || >=22.12.0"
692+ }
516693 },
517- "node_modules/@rollup/rollup-darwin-x64": {
518- "version": "4.63.4",
519- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.63.4.tgz",
520- "integrity": "sha512-bqU99PLJb/dqb3S0GIMdeuyAEETSUgZBoqXYd3Sd+WCsV+MmPhnN6JrotWyir31+QgH7EvvE5/mwGJlEoci8Fw==",
694+ "node_modules/@oxc-parser/binding-darwin-x64": {
695+ "version": "0.127.0",
696+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.127.0.tgz",
697+ "integrity": "sha512-JL6Xb5IwPQT8rUzlpsX7E+AgfcdNklXNPFp8pjCQQ5MQOQo5rtEB2ui+3Hgg9Sn7Y9Egj6YOLLiHhLpdAe12Aw==",
521698 "cpu": [
522699 "x64"
523700 ],
@@ -526,26 +703,15 @@
526703 "optional": true,
527704 "os": [
528705 "darwin"
529- ]
530- },
531- "node_modules/@rollup/rollup-freebsd-arm64": {
532- "version": "4.63.4",
533- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.63.4.tgz",
534- "integrity": "sha512-JinsFZ5G40oXQb+sUuiA5x689vhr6dDYK0H0NL+rwKdL6CqnmYN8PE4ZwfRSoIjrCxqTQG/SLfTtSvHeGxoVlw==",
535- "cpu": [
536- "arm64"
537706 ],
538- "dev": true,
539- "license": "MIT",
540- "optional": true,
541- "os": [
542- "freebsd"
543- ]
707+ "engines": {
708+ "node": "^20.19.0 || >=22.12.0"
709+ }
544710 },
545- "node_modules/@rollup/rollup-freebsd-x64": {
546- "version": "4.63.4",
547- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.63.4.tgz",
548- "integrity": "sha512-GAdA4UxpiNm27cLHr2GqXBpAD0x9FqwYBY7/YSP0Ss0/PNi4k8gbviqpIpYbVSRBaS2ZcegXEzgTQMbRNCwxCw==",
711+ "node_modules/@oxc-parser/binding-freebsd-x64": {
712+ "version": "0.127.0",
713+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.127.0.tgz",
714+ "integrity": "sha512-SDQ/3MQFw58fqQz3Z1PhSKFF3JoCF4gmlNjziDm8X02tTahCw0qJbd7FGPDKw1i4VTBZene9JPyC3mHtSvi+wA==",
549715 "cpu": [
550716 "x64"
551717 ],
@@ -554,12 +720,15 @@
554720 "optional": true,
555721 "os": [
556722 "freebsd"
557- ]
723+ ],
724+ "engines": {
725+ "node": "^20.19.0 || >=22.12.0"
726+ }
558727 },
559- "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
560- "version": "4.63.4",
561- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.63.4.tgz",
562- "integrity": "sha512-qDd6NoA1znaLjp4jR5U/KWCdLAKDJNB8W9ChbbDaKbo0xA+Atln5HK6LFCZ4oJQpemtRZA288DCirFRjrspptw==",
728+ "node_modules/@oxc-parser/binding-linux-arm-gnueabihf": {
729+ "version": "0.127.0",
730+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.127.0.tgz",
731+ "integrity": "sha512-Av+D1MIqzV0YMGPT9we2SIZaMKD7Cxs4CvXSx/yxaWHewZjYEjScpOf5igc8IILASViw4WTnjlwUdI1KzVtDHQ==",
563732 "cpu": [
564733 "arm"
565734 ],
@@ -568,12 +737,15 @@
568737 "optional": true,
569738 "os": [
570739 "linux"
571- ]
740+ ],
741+ "engines": {
742+ "node": "^20.19.0 || >=22.12.0"
743+ }
572744 },
573- "node_modules/@rollup/rollup-linux-arm-musleabihf": {
574- "version": "4.63.4",
575- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.63.4.tgz",
576- "integrity": "sha512-WtB5Tz5KTNINb8ZA+8sQ7bmjuS1JrRT7YverYIhUGdWWDlpzVWmIwuZE+jidkEXUn1l0zrEkaIMa8dHF3NGcsA==",
745+ "node_modules/@oxc-parser/binding-linux-arm-musleabihf": {
746+ "version": "0.127.0",
747+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.127.0.tgz",
748+ "integrity": "sha512-Cs2fdJ8cPpFdeebj6p4dag8A4+56hPvZ0AhQQzlaLswGz1tz7bXt1nETLeorrM9+AMcWFFkqxcXwDGfTVidY8g==",
577749 "cpu": [
578750 "arm"
579751 ],
@@ -582,12 +754,15 @@
582754 "optional": true,
583755 "os": [
584756 "linux"
585- ]
757+ ],
758+ "engines": {
759+ "node": "^20.19.0 || >=22.12.0"
760+ }
586761 },
587- "node_modules/@rollup/rollup-linux-arm64-gnu": {
588- "version": "4.63.4",
589- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.63.4.tgz",
590- "integrity": "sha512-VcQ3L1tjnkKzWjryAVaFhHEWcqOfICX9uxVVoDzm2t0DpgKRHd2zOpVrJc0xsWeBZcBFyYROCIBdyR/fS174pg==",
762+ "node_modules/@oxc-parser/binding-linux-arm64-gnu": {
763+ "version": "0.127.0",
764+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.127.0.tgz",
765+ "integrity": "sha512-qdOfTcT6SY8gsJrrV92uyEUyjqMGPpIB5JZUG6QN5dukYd+7/j0kX6MwK1DgQj39jtUYixxPiaRUiEN1+0CXgQ==",
591766 "cpu": [
592767 "arm64"
593768 ],
@@ -596,12 +771,15 @@
596771 "optional": true,
597772 "os": [
598773 "linux"
599- ]
774+ ],
775+ "engines": {
776+ "node": "^20.19.0 || >=22.12.0"
777+ }
600778 },
601- "node_modules/@rollup/rollup-linux-arm64-musl": {
602- "version": "4.63.4",
603- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.63.4.tgz",
604- "integrity": "sha512-6+ZQX6P5s0cMDN2Ypb8Lbm2+/sZYmZjdaYny992ujUU9UKi/4CWoJWsl1pNvjWJHNHGK51m+jKGLlh1ylb2ifQ==",
779+ "node_modules/@oxc-parser/binding-linux-arm64-musl": {
780+ "version": "0.127.0",
781+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.127.0.tgz",
782+ "integrity": "sha512-EoTCZneNFU/P2qrpEM+RHmQwt+CvDkyGESG6qhr7KaegXLZwePfbrkCDfAk8/rhxbDUVGsZILX+2tqPzFtoFWA==",
605783 "cpu": [
606784 "arm64"
607785 ],
@@ -610,110 +788,256 @@
610788 "optional": true,
611789 "os": [
612790 "linux"
613- ]
791+ ],
792+ "engines": {
793+ "node": "^20.19.0 || >=22.12.0"
794+ }
614795 },
615- "node_modules/@rollup/rollup-linux-loong64-gnu": {
616- "version": "4.63.4",
617- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.63.4.tgz",
618- "integrity": "sha512-D72ZnvkFkBXOfzMMQLcwfPLyGkKb7HZ9/mf97B7v6/P5Lbv4oFOtSY/uHbS8lH6uKUOxoKiuokdb50XZSzzbJw==",
796+ "node_modules/@oxc-parser/binding-linux-ppc64-gnu": {
797+ "version": "0.127.0",
798+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.127.0.tgz",
799+ "integrity": "sha512-zALjmZYgxFLHjXeudcDF0xFGNydTAtkAeXAr2EuC17ywCyFxcmQra4w0BMde0Yi/re4Bi4iwEoEXtYN7l6eBLQ==",
619800 "cpu": [
620- "loong64"
801+ "ppc64"
621802 ],
622803 "dev": true,
623804 "license": "MIT",
624805 "optional": true,
625806 "os": [
626807 "linux"
627- ]
808+ ],
809+ "engines": {
810+ "node": "^20.19.0 || >=22.12.0"
811+ }
628812 },
629- "node_modules/@rollup/rollup-linux-loong64-musl": {
630- "version": "4.63.4",
631- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.63.4.tgz",
632- "integrity": "sha512-piU6BxeqA3O9KSu3kRCIQQtNqFFaTu21SEV4FwaRZowpnj3bLaWPZHw+xFqCs0XlJ+aOH3PTRWGoglH+mKA/OA==",
813+ "node_modules/@oxc-parser/binding-linux-riscv64-gnu": {
814+ "version": "0.127.0",
815+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.127.0.tgz",
816+ "integrity": "sha512-fPP8M6zQLS7Jz7o9d5ArUSuAuSK3e+WCYVrCpdzeCOejidtZExJ9tjhDrAd3HEPqARBCPmdpqxESPFqy44vkBQ==",
633817 "cpu": [
634- "loong64"
818+ "riscv64"
635819 ],
636820 "dev": true,
637821 "license": "MIT",
638822 "optional": true,
639823 "os": [
640824 "linux"
641- ]
825+ ],
826+ "engines": {
827+ "node": "^20.19.0 || >=22.12.0"
828+ }
642829 },
643- "node_modules/@rollup/rollup-linux-ppc64-gnu": {
644- "version": "4.63.4",
645- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.63.4.tgz",
646- "integrity": "sha512-/5PGpHwqt2EEEOUs1XwzubE/ucr0dWDQ+to3zqi4Ds7EWpwtQ79wXc4JBoxqj/OwpawTsKWzJxHfSuBOq3DrWA==",
830+ "node_modules/@oxc-parser/binding-linux-riscv64-musl": {
831+ "version": "0.127.0",
832+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.127.0.tgz",
833+ "integrity": "sha512-7IcC4Ao02oGpfnjt+X/oF4U2mllo2qoSkw5xxiXNKL9MCTsTiAC6616beOuehdxGcnz1bRoPC1RQ2f1GQDdN+g==",
647834 "cpu": [
648- "ppc64"
835+ "riscv64"
649836 ],
650837 "dev": true,
651838 "license": "MIT",
652839 "optional": true,
653840 "os": [
654841 "linux"
655- ]
842+ ],
843+ "engines": {
844+ "node": "^20.19.0 || >=22.12.0"
845+ }
656846 },
657- "node_modules/@rollup/rollup-linux-ppc64-musl": {
658- "version": "4.63.4",
659- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.63.4.tgz",
660- "integrity": "sha512-cX3beZDLWt7G2oJF+nhChiT+qtaihs+S2xi7ziGmVB+2pwPng6D0Ed0HmElQOgv2UsUmSJJLGwpBao/3TDx3VA==",
847+ "node_modules/@oxc-parser/binding-linux-s390x-gnu": {
848+ "version": "0.127.0",
849+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.127.0.tgz",
850+ "integrity": "sha512-pbXIhiNFHoqWeqDNLiJ9JkpHz1IM9k4DXa66x+1GTWMG7iLxtkXgE53iiuKSXwmk3zIYmaPVfBvgcAhS583K4Q==",
661851 "cpu": [
662- "ppc64"
852+ "s390x"
663853 ],
664854 "dev": true,
665855 "license": "MIT",
666856 "optional": true,
667857 "os": [
668858 "linux"
669- ]
859+ ],
860+ "engines": {
861+ "node": "^20.19.0 || >=22.12.0"
862+ }
670863 },
671- "node_modules/@rollup/rollup-linux-riscv64-gnu": {
672- "version": "4.63.4",
673- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.63.4.tgz",
674- "integrity": "sha512-1uz2mGWHyptR7DgHHrlbdRAjXK7v7elGZ9lMja910/RP+ZYbX6xAmCiU9UZSX4hqmgtHMv6lr5l3kq1HIOpcag==",
864+ "node_modules/@oxc-parser/binding-linux-x64-gnu": {
865+ "version": "0.127.0",
866+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.127.0.tgz",
867+ "integrity": "sha512-MYCguB9RvBvlSd6gbuNI7QwiLoCCAlGnlRJFPrzLI6U1/9wkC/WK6LtBAUln55H1Ctqw45PWmqrobKoMhsYQzQ==",
675868 "cpu": [
676- "riscv64"
869+ "x64"
677870 ],
678871 "dev": true,
679872 "license": "MIT",
680873 "optional": true,
681874 "os": [
682875 "linux"
683- ]
876+ ],
877+ "engines": {
878+ "node": "^20.19.0 || >=22.12.0"
879+ }
684880 },
685- "node_modules/@rollup/rollup-linux-riscv64-musl": {
686- "version": "4.63.4",
687- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.63.4.tgz",
688- "integrity": "sha512-nLS8topojxyz7SRpKR2IODRpQ0XPZ+xaOXvT3+hqK/Uy8Lo5HFgkkIBiIrCu5tL5YqzTvgovGw55PwpahTAGig==",
881+ "node_modules/@oxc-parser/binding-linux-x64-musl": {
882+ "version": "0.127.0",
883+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.127.0.tgz",
884+ "integrity": "sha512-5eY0B/bxf1xIUxb4NOTvOI3KWtBQfPWYyKAzgcrCt0mDibSZygVpO1Pz8bkeiSZ5Jj9+M09dkggG3H8I5d0Uyg==",
689885 "cpu": [
690- "riscv64"
886+ "x64"
691887 ],
692888 "dev": true,
693889 "license": "MIT",
694890 "optional": true,
695891 "os": [
696892 "linux"
893+ ],
894+ "engines": {
895+ "node": "^20.19.0 || >=22.12.0"
896+ }
897+ },
898+ "node_modules/@oxc-parser/binding-openharmony-arm64": {
899+ "version": "0.127.0",
900+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.127.0.tgz",
901+ "integrity": "sha512-Gld0ajrFTUXNtdw20fVBuTQx66FA75nIVg+//pPfR3sXkuABB4mTBhl3r9JNzrJpgW//qiwxf0nWXUWGJSL3UQ==",
902+ "cpu": [
903+ "arm64"
904+ ],
905+ "dev": true,
906+ "license": "MIT",
907+ "optional": true,
908+ "os": [
909+ "openharmony"
910+ ],
911+ "engines": {
912+ "node": "^20.19.0 || >=22.12.0"
913+ }
914+ },
915+ "node_modules/@oxc-parser/binding-wasm32-wasi": {
916+ "version": "0.127.0",
917+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.127.0.tgz",
918+ "integrity": "sha512-T6KVD7rhLzFlwGRXMnxUFfkCZD8FHnb968wVXW1mXzgRFc5RNXOBY2mPPDZ77x5Ln76ltLMgtPg0cOkU1NSrEQ==",
919+ "cpu": [
920+ "wasm32"
921+ ],
922+ "dev": true,
923+ "license": "MIT",
924+ "optional": true,
925+ "dependencies": {
926+ "@emnapi/core": "1.9.2",
927+ "@emnapi/runtime": "1.9.2",
928+ "@napi-rs/wasm-runtime": "^1.1.4"
929+ },
930+ "engines": {
931+ "node": "^20.19.0 || >=22.12.0"
932+ }
933+ },
934+ "node_modules/@oxc-parser/binding-win32-arm64-msvc": {
935+ "version": "0.127.0",
936+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.127.0.tgz",
937+ "integrity": "sha512-Ujvw4X+LD1CCGULcsQcvb4YNVoBGqt+JHgNNzGGaCImELiZLk477ifUH53gIbE7EKd933NdTi25JWEr9K2HwXw==",
938+ "cpu": [
939+ "arm64"
940+ ],
941+ "dev": true,
942+ "license": "MIT",
943+ "optional": true,
944+ "os": [
945+ "win32"
946+ ],
947+ "engines": {
948+ "node": "^20.19.0 || >=22.12.0"
949+ }
950+ },
951+ "node_modules/@oxc-parser/binding-win32-ia32-msvc": {
952+ "version": "0.127.0",
953+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.127.0.tgz",
954+ "integrity": "sha512-0cwxKO7KHQQQfo4Uf4B2SQrhgm+cJaP9OvFFhx52Tkg4bezsacu83GB2/In5bC415Ueeym+kXdnge/57rbSfTw==",
955+ "cpu": [
956+ "ia32"
957+ ],
958+ "dev": true,
959+ "license": "MIT",
960+ "optional": true,
961+ "os": [
962+ "win32"
963+ ],
964+ "engines": {
965+ "node": "^20.19.0 || >=22.12.0"
966+ }
967+ },
968+ "node_modules/@oxc-parser/binding-win32-x64-msvc": {
969+ "version": "0.127.0",
970+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.127.0.tgz",
971+ "integrity": "sha512-rOrnSQSCbhI2kowr9XxE7m9a8oQXnBHjnS6j95LxxAnEZ0+Fz20WlRXG4ondQb+ejjt2KOsa65sE6++L6kUd+w==",
972+ "cpu": [
973+ "x64"
974+ ],
975+ "dev": true,
976+ "license": "MIT",
977+ "optional": true,
978+ "os": [
979+ "win32"
980+ ],
981+ "engines": {
982+ "node": "^20.19.0 || >=22.12.0"
983+ }
984+ },
985+ "node_modules/@oxc-project/types": {
986+ "version": "0.127.0",
987+ "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.127.0.tgz",
988+ "integrity": "sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==",
989+ "dev": true,
990+ "license": "MIT",
991+ "funding": {
992+ "url": "https://github.com/sponsors/Boshen"
993+ }
994+ },
995+ "node_modules/@oxc-resolver/binding-android-arm-eabi": {
996+ "version": "11.21.2",
997+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.21.2.tgz",
998+ "integrity": "sha512-xQoCRv+gKax9KTdwdaQNnAFOai8neay7g3jExDIORzhbrejwGSJaZNTdOJHR5ziLg2joMxOCMOFMo4zuxza2uQ==",
999+ "cpu": [
1000+ "arm"
1001+ ],
1002+ "dev": true,
1003+ "license": "MIT",
1004+ "optional": true,
1005+ "os": [
1006+ "android"
6971007 ]
6981008 },
699- "node_modules/@rollup/rollup-linux-s390x-gnu": {
700- "version": "4.63.4",
701- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.63.4.tgz",
702- "integrity": "sha512-gs7DRKotr3l3q+jGPQBjH0ng1FjlEDm5ueQrkw5JtQvtLyEIcLASqAEaor56BhkKRzk+IcQzrcanBdb/bBQn8g==",
1009+ "node_modules/@oxc-resolver/binding-android-arm64": {
1010+ "version": "11.21.2",
1011+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.21.2.tgz",
1012+ "integrity": "sha512-HF5oiE2L05yInPYCFD/4uxSrEZW4SuIfn99Y6L1xnJnzl066JR+MJs2rIdstw8A2MPlAKH+13dpFPNycjqzvGg==",
7031013 "cpu": [
704- "s390x"
1014+ "arm64"
7051015 ],
7061016 "dev": true,
7071017 "license": "MIT",
7081018 "optional": true,
7091019 "os": [
710- "linux"
1020+ "android"
7111021 ]
7121022 },
713- "node_modules/@rollup/rollup-linux-x64-gnu": {
714- "version": "4.63.4",
715- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.4.tgz",
716- "integrity": "sha512-791ET7W17NnScOZM7h4dX5hYspxE28htPFsb1awY/NRR8+PRNkS53e475rDdxXXDrP+kwnCcNWg9CX5ztn/Aqw==",
1023+ "node_modules/@oxc-resolver/binding-darwin-arm64": {
1024+ "version": "11.21.2",
1025+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.21.2.tgz",
1026+ "integrity": "sha512-UX4u49CVCAD8QZNELaW8eMGgMAGwFWYEPbvNsh+3r/gs4NX3KfpiACMVwRQT0EuH3uat9hM5Zl+Ppm9pJD8tgg==",
1027+ "cpu": [
1028+ "arm64"
1029+ ],
1030+ "dev": true,
1031+ "license": "MIT",
1032+ "optional": true,
1033+ "os": [
1034+ "darwin"
1035+ ]
1036+ },
1037+ "node_modules/@oxc-resolver/binding-darwin-x64": {
1038+ "version": "11.21.2",
1039+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.21.2.tgz",
1040+ "integrity": "sha512-J9xPx7YBkrRmJ+xl561ztnMWEc1aOyjEIxBiGX1dVb3u7bGSnfObfcZk+Pd+uM0HZAPNsQ1xvD8j52A/uOSqNQ==",
7171041 "cpu": [
7181042 "x64"
7191043 ],
@@ -721,13 +1045,13 @@
7211045 "license": "MIT",
7221046 "optional": true,
7231047 "os": [
724- "linux"
1048+ "darwin"
7251049 ]
7261050 },
727- "node_modules/@rollup/rollup-linux-x64-musl": {
728- "version": "4.63.4",
729- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.63.4.tgz",
730- "integrity": "sha512-iwZQRcmj7g88g3tzefIrQY7qvmuA/cfYwhrDtTBhsmukO4U2huVO5W+86XacUMRvdSFVAc6kZUZy21JaRwiB9w==",
1051+ "node_modules/@oxc-resolver/binding-freebsd-x64": {
1052+ "version": "11.21.2",
1053+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.21.2.tgz",
1054+ "integrity": "sha512-fRlt7OvSaQkWj6+EDTVxawVxOlqJB2QSnBfkeCyK4RTvsGctbw3BiH2Tb7DzMs7bikc4BRBpvWP5zF9K8b54Zg==",
7311055 "cpu": [
7321056 "x64"
7331057 ],
@@ -735,27 +1059,41 @@
7351059 "license": "MIT",
7361060 "optional": true,
7371061 "os": [
1062+ "freebsd"
1063+ ]
1064+ },
1065+ "node_modules/@oxc-resolver/binding-linux-arm-gnueabihf": {
1066+ "version": "11.21.2",
1067+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.21.2.tgz",
1068+ "integrity": "sha512-gK+vPUcPQITkGwBKpZGrcDHSlU6eDGl7AQacxS2CEKAZIBHWkOVFeJwLZ4tYnA1acJqRM5lt7yYwPCVqGHIJ7A==",
1069+ "cpu": [
1070+ "arm"
1071+ ],
1072+ "dev": true,
1073+ "license": "MIT",
1074+ "optional": true,
1075+ "os": [
7381076 "linux"
7391077 ]
7401078 },
741- "node_modules/@rollup/rollup-openbsd-x64": {
742- "version": "4.63.4",
743- "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.63.4.tgz",
744- "integrity": "sha512-dVHFp9gRWrdTpnqQuGfCwd7hOQDatK1VCP2iWhLY/cGrOQs/ucFzJ6A5SRqbXX12ZDI8EUuejSM5kwg+ja7Png==",
1079+ "node_modules/@oxc-resolver/binding-linux-arm-musleabihf": {
1080+ "version": "11.21.2",
1081+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.21.2.tgz",
1082+ "integrity": "sha512-L/Rgas7SrOKy/z7IH+HxSFRqVO4PuLDKLEGKvnhoCBBq3UJ0YzGBou3qMzaAGgkJwsIvX2pBF+7ojzfUhLiZxQ==",
7451083 "cpu": [
746- "x64"
1084+ "arm"
7471085 ],
7481086 "dev": true,
7491087 "license": "MIT",
7501088 "optional": true,
7511089 "os": [
752- "openbsd"
1090+ "linux"
7531091 ]
7541092 },
755- "node_modules/@rollup/rollup-openharmony-arm64": {
756- "version": "4.63.4",
757- "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.63.4.tgz",
758- "integrity": "sha512-t3NlauOW6gxZVVFcBEnO62Cb4wbyDFL416gTg1uFI/2tgqYQlf69FbSE115Ajre9I+c26Lk4mcmdFUsS/DGifQ==",
1093+ "node_modules/@oxc-resolver/binding-linux-arm64-gnu": {
1094+ "version": "11.21.2",
1095+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.21.2.tgz",
1096+ "integrity": "sha512-CUEYvlX1Fk7E9kUMzuswru1J9HLxMwnpDeQGjQuI4ZH+iNCoa2X9T+pvyzrbsgl7WnIeTFTlNlHsRfVdf5g9/g==",
7591097 "cpu": [
7601098 "arm64"
7611099 ],
@@ -763,13 +1101,13 @@
7631101 "license": "MIT",
7641102 "optional": true,
7651103 "os": [
766- "openharmony"
1104+ "linux"
7671105 ]
7681106 },
769- "node_modules/@rollup/rollup-win32-arm64-msvc": {
770- "version": "4.63.4",
771- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.63.4.tgz",
772- "integrity": "sha512-xWuIaSye5FWZF8+UYtVEcHtRJDN5kN9Kfgxx3Kq8XIov9KSKbc1fiqQCm90SKrgQbUXZelbnUhnlUJmfSE7P9A==",
1107+ "node_modules/@oxc-resolver/binding-linux-arm64-musl": {
1108+ "version": "11.21.2",
1109+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.21.2.tgz",
1110+ "integrity": "sha512-ViN1ZibQyxwC67GpoP33oo+S9UyUnkog13vzQb9+v9bCNvrVzJuk0MRdacjtv/9xfRMVF/eqHFyl8YOeykI10Q==",
7731111 "cpu": [
7741112 "arm64"
7751113 ],
@@ -777,27 +1115,69 @@
7771115 "license": "MIT",
7781116 "optional": true,
7791117 "os": [
780- "win32"
1118+ "linux"
7811119 ]
7821120 },
783- "node_modules/@rollup/rollup-win32-ia32-msvc": {
784- "version": "4.63.4",
785- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.63.4.tgz",
786- "integrity": "sha512-9ALJJUOg/ZflMJepVo2PlgsGxSaxN7SQ4Z8GoZfVlarWr6r3rkHUNsd/zAio7p4YMtChSMXPionxej4Hkf6CXQ==",
1121+ "node_modules/@oxc-resolver/binding-linux-ppc64-gnu": {
1122+ "version": "11.21.2",
1123+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.21.2.tgz",
1124+ "integrity": "sha512-CU1sCqWnhGqYD5I1HedHk5pujrn7ssDkNB/AEQd/pd3D/EojVSgJUlpbafwIbyhia3PgIfkvdFpRPWSALzVumA==",
7871125 "cpu": [
788- "ia32"
1126+ "ppc64"
7891127 ],
7901128 "dev": true,
7911129 "license": "MIT",
7921130 "optional": true,
7931131 "os": [
794- "win32"
1132+ "linux"
7951133 ]
7961134 },
797- "node_modules/@rollup/rollup-win32-x64-gnu": {
798- "version": "4.63.4",
799- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.63.4.tgz",
800- "integrity": "sha512-blj9z5qx/Pv4WU0W1NMFDB97e0JH5ed+aZGywW8WCvp/NhWX/4PFAq5uu6Q0AebNn+Vo6KzUYDT++JzTT5ojlQ==",
1135+ "node_modules/@oxc-resolver/binding-linux-riscv64-gnu": {
1136+ "version": "11.21.2",
1137+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.21.2.tgz",
1138+ "integrity": "sha512-jWVyZtIHca4Gb96x7dag+y69vlei7ffjrsveLkmf2ZhqEAz6ZSBnY1GWvgZUaZlwZP62A3xD092BW97Q5VGc+g==",
1139+ "cpu": [
1140+ "riscv64"
1141+ ],
1142+ "dev": true,
1143+ "license": "MIT",
1144+ "optional": true,
1145+ "os": [
1146+ "linux"
1147+ ]
1148+ },
1149+ "node_modules/@oxc-resolver/binding-linux-riscv64-musl": {
1150+ "version": "11.21.2",
1151+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.21.2.tgz",
1152+ "integrity": "sha512-LF29obFqNgBUgDX7rmUK7M4D0JQG5LxhYzn3xXmECcHU9aQAdWG7NiY052qybtesEdwHQXKNTWYQ7mTsybNvWg==",
1153+ "cpu": [
1154+ "riscv64"
1155+ ],
1156+ "dev": true,
1157+ "license": "MIT",
1158+ "optional": true,
1159+ "os": [
1160+ "linux"
1161+ ]
1162+ },
1163+ "node_modules/@oxc-resolver/binding-linux-s390x-gnu": {
1164+ "version": "11.21.2",
1165+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.21.2.tgz",
1166+ "integrity": "sha512-+NYcm+cCHBbtdQQ3A4phQTSuVRYnNHz7wrl9XRAPEovcdoqi0mb1K5ZOl+jN54ZD+q1zz3V0vltbFJmzecJKmw==",
1167+ "cpu": [
1168+ "s390x"
1169+ ],
1170+ "dev": true,
1171+ "license": "MIT",
1172+ "optional": true,
1173+ "os": [
1174+ "linux"
1175+ ]
1176+ },
1177+ "node_modules/@oxc-resolver/binding-linux-x64-gnu": {
1178+ "version": "11.21.2",
1179+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.21.2.tgz",
1180+ "integrity": "sha512-UQqZDdG2r2HhAOsZEgufkIWHPQ886IUyuJQkoZByvzhW8j51R4UNzGBJFkTiTnLhQnggwJRdJgFWK4uY6ZVIMw==",
8011181 "cpu": [
8021182 "x64"
8031183 ],
@@ -805,13 +1185,13 @@
8051185 "license": "MIT",
8061186 "optional": true,
8071187 "os": [
808- "win32"
1188+ "linux"
8091189 ]
8101190 },
811- "node_modules/@rollup/rollup-win32-x64-msvc": {
812- "version": "4.63.4",
813- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.63.4.tgz",
814- "integrity": "sha512-Erx822VRBwLa124shbj+wNXe//BOgMEctDV0m1aqTQdNO1S69DgNUCFKC1RCeZfixs1J31l6igk1ziyXErbigQ==",
1191+ "node_modules/@oxc-resolver/binding-linux-x64-musl": {
1192+ "version": "11.21.2",
1193+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.21.2.tgz",
1194+ "integrity": "sha512-3Q9PMRjalWkT6NZ4jfujuqTCFwoWErg3y3BnOgb544B8IMw4PktiwWOigMfOHNRLMghZeJ7hpfpZf4CP7rV7Og==",
8151195 "cpu": [
8161196 "x64"
8171197 ],
@@ -819,141 +1199,2512 @@
8191199 "license": "MIT",
8201200 "optional": true,
8211201 "os": [
822- "win32"
1202+ "linux"
8231203 ]
8241204 },
825- "node_modules/@sveltejs/vite-plugin-svelte": {
826- "version": "3.1.2",
827- "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-3.1.2.tgz",
828- "integrity": "sha512-Txsm1tJvtiYeLUVRNqxZGKR/mI+CzuIQuc2gn+YCs9rMTowpNZ2Nqt53JdL8KF9bLhAf2ruR/dr9eZCwdTriRA==",
1205+ "node_modules/@oxc-resolver/binding-openharmony-arm64": {
1206+ "version": "11.21.2",
1207+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.21.2.tgz",
1208+ "integrity": "sha512-Eljeq3ndtyKhM+Es8LITi4Zl2htzuRZcrPMF3kMCsrILztvU6AjZ3FuEhHHKobPUB9rMpzLpJP5bDqXI7r+iog==",
1209+ "cpu": [
1210+ "arm64"
1211+ ],
8291212 "dev": true,
8301213 "license": "MIT",
831- "dependencies": {
832- "@sveltejs/vite-plugin-svelte-inspector": "^2.1.0",
833- "debug": "^4.3.4",
834- "deepmerge": "^4.3.1",
835- "kleur": "^4.1.5",
836- "magic-string": "^0.30.10",
837- "svelte-hmr": "^0.16.0",
838- "vitefu": "^0.2.5"
839- },
840- "engines": {
841- "node": "^18.0.0 || >=20"
842- },
843- "peerDependencies": {
844- "svelte": "^4.0.0 || ^5.0.0-next.0",
845- "vite": "^5.0.0"
846- }
1214+ "optional": true,
1215+ "os": [
1216+ "openharmony"
1217+ ]
8471218 },
848- "node_modules/@sveltejs/vite-plugin-svelte-inspector": {
849- "version": "2.1.0",
850- "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-2.1.0.tgz",
851- "integrity": "sha512-9QX28IymvBlSCqsCll5t0kQVxipsfhFFL+L2t3nTWfXnddYwxBuAEtTtlaVQpRz9c37BhJjltSeY4AJSC03SSg==",
1219+ "node_modules/@oxc-resolver/binding-wasm32-wasi": {
1220+ "version": "11.21.2",
1221+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.21.2.tgz",
1222+ "integrity": "sha512-HGDbNsIywqc4LxU38+CTJNnB/6BF7rheWOJ259b4eE0aEnelYblC8x+1tEd63bp31fYne63RQz9Jb4yVnC7Yig==",
1223+ "cpu": [
1224+ "wasm32"
1225+ ],
8521226 "dev": true,
8531227 "license": "MIT",
1228+ "optional": true,
8541229 "dependencies": {
855- "debug": "^4.3.4"
1230+ "@emnapi/core": "1.11.0",
1231+ "@emnapi/runtime": "1.11.0",
1232+ "@napi-rs/wasm-runtime": "^1.1.5"
8561233 },
8571234 "engines": {
858- "node": "^18.0.0 || >=20"
859- },
860- "peerDependencies": {
861- "@sveltejs/vite-plugin-svelte": "^3.0.0",
862- "svelte": "^4.0.0 || ^5.0.0-next.0",
863- "vite": "^5.0.0"
1235+ "node": ">=14.0.0"
8641236 }
8651237 },
866- "node_modules/@types/estree": {
867- "version": "1.0.9",
868- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
869- "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
870- "license": "MIT"
871- },
872- "node_modules/acorn": {
873- "version": "8.18.0",
874- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz",
875- "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==",
1238+ "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/core": {
1239+ "version": "1.11.0",
1240+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.0.tgz",
1241+ "integrity": "sha512-l9Oo58x0HOP5znGzVhYW9U3e5wVuA4LAZU2AGezTmkhO1CgQRFDhDg4nneHsu/t3WniXg9QrG2nIXL/ZS8ln8Q==",
1242+ "dev": true,
8761243 "license": "MIT",
877- "bin": {
878- "acorn": "bin/acorn"
879- },
880- "engines": {
881- "node": ">=0.4.0"
882- }
883- },
884- "node_modules/aria-query": {
885- "version": "5.3.2",
886- "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
887- "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==",
888- "license": "Apache-2.0",
889- "engines": {
890- "node": ">= 0.4"
891- }
892- },
893- "node_modules/axobject-query": {
894- "version": "4.1.0",
895- "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
896- "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==",
897- "license": "Apache-2.0",
898- "engines": {
899- "node": ">= 0.4"
1244+ "optional": true,
1245+ "dependencies": {
1246+ "@emnapi/wasi-threads": "1.2.2",
1247+ "tslib": "^2.4.0"
9001248 }
9011249 },
902- "node_modules/code-red": {
903- "version": "1.0.4",
904- "resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz",
905- "integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==",
1250+ "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/runtime": {
1251+ "version": "1.11.0",
1252+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.0.tgz",
1253+ "integrity": "sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg==",
1254+ "dev": true,
9061255 "license": "MIT",
1256+ "optional": true,
9071257 "dependencies": {
908- "@jridgewell/sourcemap-codec": "^1.4.15",
909- "@types/estree": "^1.0.1",
910- "acorn": "^8.10.0",
911- "estree-walker": "^3.0.3",
912- "periscopic": "^3.1.0"
1258+ "tslib": "^2.4.0"
9131259 }
9141260 },
915- "node_modules/css-tree": {
916- "version": "2.3.1",
917- "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
918- "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
1261+ "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/wasi-threads": {
1262+ "version": "1.2.2",
1263+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz",
1264+ "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==",
1265+ "dev": true,
9191266 "license": "MIT",
1267+ "optional": true,
9201268 "dependencies": {
921- "mdn-data": "2.0.30",
922- "source-map-js": "^1.0.1"
923- },
924- "engines": {
925- "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
1269+ "tslib": "^2.4.0"
9261270 }
9271271 },
928- "node_modules/debug": {
929- "version": "4.4.3",
930- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
931- "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
1272+ "node_modules/@oxc-resolver/binding-win32-arm64-msvc": {
1273+ "version": "11.21.2",
1274+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.21.2.tgz",
1275+ "integrity": "sha512-iWx25CBEgH49iE9q5coEGI/jb1jl5kkCY9z6U5Og67xCkQ/WFMDc2J5U78+AE91SUxM2NqSLhJC8/PLfWnImww==",
1276+ "cpu": [
1277+ "arm64"
1278+ ],
9321279 "dev": true,
9331280 "license": "MIT",
934- "dependencies": {
1281+ "optional": true,
1282+ "os": [
1283+ "win32"
1284+ ]
1285+ },
1286+ "node_modules/@oxc-resolver/binding-win32-x64-msvc": {
1287+ "version": "11.21.2",
1288+ "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.21.2.tgz",
1289+ "integrity": "sha512-VPoCAhKvCQTlG7vxqaBXcmuvbh77BfnGXj8g0pbvVXpm1F/R8rDVwqIWcEbMzrI1JvJlm8v7T9uMVQb6UctMRg==",
1290+ "cpu": [
1291+ "x64"
1292+ ],
1293+ "dev": true,
1294+ "license": "MIT",
1295+ "optional": true,
1296+ "os": [
1297+ "win32"
1298+ ]
1299+ },
1300+ "node_modules/@rollup/rollup-android-arm-eabi": {
1301+ "version": "4.64.2",
1302+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.64.2.tgz",
1303+ "integrity": "sha512-gjNDUwMswogkw3fNqV00u54uZlgmInuw5EU/1TeqQ6K0bEagoGvqjPdKzy112lKyhe7PkGudZ/kSQHsc9qroIg==",
1304+ "cpu": [
1305+ "arm"
1306+ ],
1307+ "dev": true,
1308+ "license": "MIT",
1309+ "optional": true,
1310+ "os": [
1311+ "android"
1312+ ]
1313+ },
1314+ "node_modules/@rollup/rollup-android-arm64": {
1315+ "version": "4.64.2",
1316+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.64.2.tgz",
1317+ "integrity": "sha512-YkAW8q7p5SCLSaGWaUkmsRnBEb3MGA1pB5nI/8k0lAK5fZsQKRBqkdR1h4UU9p8u3c7ZGdUF2Zcx7mK+E/pUKg==",
1318+ "cpu": [
1319+ "arm64"
1320+ ],
1321+ "dev": true,
1322+ "license": "MIT",
1323+ "optional": true,
1324+ "os": [
1325+ "android"
1326+ ]
1327+ },
1328+ "node_modules/@rollup/rollup-darwin-arm64": {
1329+ "version": "4.64.2",
1330+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.64.2.tgz",
1331+ "integrity": "sha512-hNmuIHv+LnrP/+q/44vsmIMWfoh7X2YXOp5FLtjGuvqN52VdtFpOpnshTc1k28tWv1wE/0D5gLjsozBUowEGbQ==",
1332+ "cpu": [
1333+ "arm64"
1334+ ],
1335+ "dev": true,
1336+ "license": "MIT",
1337+ "optional": true,
1338+ "os": [
1339+ "darwin"
1340+ ]
1341+ },
1342+ "node_modules/@rollup/rollup-darwin-x64": {
1343+ "version": "4.64.2",
1344+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.64.2.tgz",
1345+ "integrity": "sha512-B0t5CoxgF2KL7R5FHAsyN8yVEQCSIkh4XVs8MCDjuBLNjEa8l3zd5tVKaFMv53LfUtHs9p2rnqeavPTOunV+Ww==",
1346+ "cpu": [
1347+ "x64"
1348+ ],
1349+ "dev": true,
1350+ "license": "MIT",
1351+ "optional": true,
1352+ "os": [
1353+ "darwin"
1354+ ]
1355+ },
1356+ "node_modules/@rollup/rollup-freebsd-arm64": {
1357+ "version": "4.64.2",
1358+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.64.2.tgz",
1359+ "integrity": "sha512-QQdYnfwrPmc1DoPlddjFvQPzUHFbStKXsHrG6EBYsWHhBVUCjsCRdcfGGXGAvhDpDtTzKK4vy1UrGFJ45rQaiw==",
1360+ "cpu": [
1361+ "arm64"
1362+ ],
1363+ "dev": true,
1364+ "license": "MIT",
1365+ "optional": true,
1366+ "os": [
1367+ "freebsd"
1368+ ]
1369+ },
1370+ "node_modules/@rollup/rollup-freebsd-x64": {
1371+ "version": "4.64.2",
1372+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.64.2.tgz",
1373+ "integrity": "sha512-1xRxtGWif6BB58VSfMavi0Yfs2M4x6CtRp4o7ScY4W7AGlgvf6GSPKdDNdw7TX3tMiQwZY5W/mvyrOJTq/7AKA==",
1374+ "cpu": [
1375+ "x64"
1376+ ],
1377+ "dev": true,
1378+ "license": "MIT",
1379+ "optional": true,
1380+ "os": [
1381+ "freebsd"
1382+ ]
1383+ },
1384+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
1385+ "version": "4.64.2",
1386+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.64.2.tgz",
1387+ "integrity": "sha512-CXFLEt384BUwwOLR9DURWNHl1bQOVLd1ibOee+yKBJm/fZtvrSAOZ0JbxjAxszVMIovYMwFMS+oP+Q1aPcx8Eg==",
1388+ "cpu": [
1389+ "arm"
1390+ ],
1391+ "dev": true,
1392+ "license": "MIT",
1393+ "optional": true,
1394+ "os": [
1395+ "linux"
1396+ ]
1397+ },
1398+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
1399+ "version": "4.64.2",
1400+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.64.2.tgz",
1401+ "integrity": "sha512-T1dorY0WHm5mMNpMHFl5rYRZ02gLSZcr6J45r66tYb3S3R3DyIRGiGWVdLtF+zOQjratNJaObl9jYumueRiL1A==",
1402+ "cpu": [
1403+ "arm"
1404+ ],
1405+ "dev": true,
1406+ "license": "MIT",
1407+ "optional": true,
1408+ "os": [
1409+ "linux"
1410+ ]
1411+ },
1412+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
1413+ "version": "4.64.2",
1414+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.64.2.tgz",
1415+ "integrity": "sha512-ia6bfaCIc0ldp6bvs9oEkFrJVJKNUMoiDF0+LBZsQAZg+vE0znXnzGqH+ew/FYswE30cruAWPTO3dPw7keaVkQ==",
1416+ "cpu": [
1417+ "arm64"
1418+ ],
1419+ "dev": true,
1420+ "license": "MIT",
1421+ "optional": true,
1422+ "os": [
1423+ "linux"
1424+ ]
1425+ },
1426+ "node_modules/@rollup/rollup-linux-arm64-musl": {
1427+ "version": "4.64.2",
1428+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.64.2.tgz",
1429+ "integrity": "sha512-mB/gwfBfXtT0EBpbSssqKCqMFuX8b21Zy2TGoIhehSJEssZ5ksybxymwp1BkEM5Nczh/AAxpmh5q+MkhtundGg==",
1430+ "cpu": [
1431+ "arm64"
1432+ ],
1433+ "dev": true,
1434+ "license": "MIT",
1435+ "optional": true,
1436+ "os": [
1437+ "linux"
1438+ ]
1439+ },
1440+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
1441+ "version": "4.64.2",
1442+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.64.2.tgz",
1443+ "integrity": "sha512-VPd0a+XlHoNRI9jIPRHww2UKhDkD6XjbtcKYXZijAnhJS1jLLbiNtVl6a5LyEfNN+eyiyWcJOZXPzBo69oTsew==",
1444+ "cpu": [
1445+ "loong64"
1446+ ],
1447+ "dev": true,
1448+ "license": "MIT",
1449+ "optional": true,
1450+ "os": [
1451+ "linux"
1452+ ]
1453+ },
1454+ "node_modules/@rollup/rollup-linux-loong64-musl": {
1455+ "version": "4.64.2",
1456+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.64.2.tgz",
1457+ "integrity": "sha512-10N1I6oBpERqzGtHKcCLrEpHlbXSBbMK9TURHvqgWbIFZuU3oJROxJ0H2nHfq96TVI0XJHQ9qzolJMFd2tz0LA==",
1458+ "cpu": [
1459+ "loong64"
1460+ ],
1461+ "dev": true,
1462+ "license": "MIT",
1463+ "optional": true,
1464+ "os": [
1465+ "linux"
1466+ ]
1467+ },
1468+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
1469+ "version": "4.64.2",
1470+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.64.2.tgz",
1471+ "integrity": "sha512-dhJrLVZ3wNNhwCmy9/VTg6Pq6J8a8eZ0n15dVRlbSWcDb+8V/1mDfbnvis+vt8W6Pbxzv6gUbTEl4piOT3bbUQ==",
1472+ "cpu": [
1473+ "ppc64"
1474+ ],
1475+ "dev": true,
1476+ "license": "MIT",
1477+ "optional": true,
1478+ "os": [
1479+ "linux"
1480+ ]
1481+ },
1482+ "node_modules/@rollup/rollup-linux-ppc64-musl": {
1483+ "version": "4.64.2",
1484+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.64.2.tgz",
1485+ "integrity": "sha512-tPd2QuwxkTxgju5eARyb3RdgBWWi/O9dn/NFH0VkvGbLzoQeXrH6qMABtW8IODb5lUeBzB3sy2U7qmI4MtQ72g==",
1486+ "cpu": [
1487+ "ppc64"
1488+ ],
1489+ "dev": true,
1490+ "license": "MIT",
1491+ "optional": true,
1492+ "os": [
1493+ "linux"
1494+ ]
1495+ },
1496+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
1497+ "version": "4.64.2",
1498+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.64.2.tgz",
1499+ "integrity": "sha512-njtDdPvSQWI9CDhIPPxsiEMeHayCfu7wwcbSQ7c2kYPhGVviL+WGVhq6Jfj/b+VuhSTkFuQ0z4ZFbFjkWwBFmg==",
1500+ "cpu": [
1501+ "riscv64"
1502+ ],
1503+ "dev": true,
1504+ "license": "MIT",
1505+ "optional": true,
1506+ "os": [
1507+ "linux"
1508+ ]
1509+ },
1510+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
1511+ "version": "4.64.2",
1512+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.64.2.tgz",
1513+ "integrity": "sha512-EbOBo3dqCj9ecEV9Z7zvulB6AieMP2ztnKQYGVOSN0pzIyOu+3JH2YJZnLmbPeVvfn6yP6Qi7NbBM27EzGbj2w==",
1514+ "cpu": [
1515+ "riscv64"
1516+ ],
1517+ "dev": true,
1518+ "license": "MIT",
1519+ "optional": true,
1520+ "os": [
1521+ "linux"
1522+ ]
1523+ },
1524+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
1525+ "version": "4.64.2",
1526+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.64.2.tgz",
1527+ "integrity": "sha512-8wLt2Q5n4qmfiE2Gk8V6KNLSf+oJuBX7Kdw5joFDtnZIZvirS0hLzBc79OiJjV71oYDmBUZLNy6pDgvylo4qiQ==",
1528+ "cpu": [
1529+ "s390x"
1530+ ],
1531+ "dev": true,
1532+ "license": "MIT",
1533+ "optional": true,
1534+ "os": [
1535+ "linux"
1536+ ]
1537+ },
1538+ "node_modules/@rollup/rollup-linux-x64-gnu": {
1539+ "version": "4.64.2",
1540+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.64.2.tgz",
1541+ "integrity": "sha512-aIChbkhC4d33bxkVP0LSi2/2lUSGRXE6k4Qa0G7tgpgTOK7kxHTixmx5qC3SKUJ6yUMznHBtQoWaG4QMqLUnhw==",
1542+ "cpu": [
1543+ "x64"
1544+ ],
1545+ "dev": true,
1546+ "license": "MIT",
1547+ "optional": true,
1548+ "os": [
1549+ "linux"
1550+ ]
1551+ },
1552+ "node_modules/@rollup/rollup-linux-x64-musl": {
1553+ "version": "4.64.2",
1554+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.64.2.tgz",
1555+ "integrity": "sha512-gQ+TkVZRfjZVcl/UOAxxfWw+sHjzIXoieNwxEyJlD3tRBmBhR8ZDq11w+QEgQDcpslEB+fhN/OEpaTsDoIIHhQ==",
1556+ "cpu": [
1557+ "x64"
1558+ ],
1559+ "dev": true,
1560+ "license": "MIT",
1561+ "optional": true,
1562+ "os": [
1563+ "linux"
1564+ ]
1565+ },
1566+ "node_modules/@rollup/rollup-openbsd-x64": {
1567+ "version": "4.64.2",
1568+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.64.2.tgz",
1569+ "integrity": "sha512-sUaJpMKS2JBCvUk8fYimG7aMq2jHVT4kw/uVHxPolKLrMmkKy0KXirmZyZehdNpL7PffqAlPAWUELQcTiD8ChA==",
1570+ "cpu": [
1571+ "x64"
1572+ ],
1573+ "dev": true,
1574+ "license": "MIT",
1575+ "optional": true,
1576+ "os": [
1577+ "openbsd"
1578+ ]
1579+ },
1580+ "node_modules/@rollup/rollup-openharmony-arm64": {
1581+ "version": "4.64.2",
1582+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.64.2.tgz",
1583+ "integrity": "sha512-j1RdJWb0CrlSuwt12OPjLucPlwfLtA/KMOKVvh5rXSJEtl/WDoIGR3CSykK/jLGTo9RqNfW5Ub6kY5Z5ccYl9w==",
1584+ "cpu": [
1585+ "arm64"
1586+ ],
1587+ "dev": true,
1588+ "license": "MIT",
1589+ "optional": true,
1590+ "os": [
1591+ "openharmony"
1592+ ]
1593+ },
1594+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
1595+ "version": "4.64.2",
1596+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.64.2.tgz",
1597+ "integrity": "sha512-i3r04aHCIY3eggTBLUp/4IxiBjDpAgQaK89Ue2nMJ2MKuIXX5JfoeZjHw+kPjGGNVDlhi8FT8xzuGMJZe3aehg==",
1598+ "cpu": [
1599+ "arm64"
1600+ ],
1601+ "dev": true,
1602+ "license": "MIT",
1603+ "optional": true,
1604+ "os": [
1605+ "win32"
1606+ ]
1607+ },
1608+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
1609+ "version": "4.64.2",
1610+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.64.2.tgz",
1611+ "integrity": "sha512-kCQ15h/uwNMGdN8gjXBZGf+aGE+ODzHfbi90/cAm4+Q89WoMiLODNoj8HoGZmYx87HPxfu5CBNPsZ+ZG4uIAoA==",
1612+ "cpu": [
1613+ "ia32"
1614+ ],
1615+ "dev": true,
1616+ "license": "MIT",
1617+ "optional": true,
1618+ "os": [
1619+ "win32"
1620+ ]
1621+ },
1622+ "node_modules/@rollup/rollup-win32-x64-gnu": {
1623+ "version": "4.64.2",
1624+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.64.2.tgz",
1625+ "integrity": "sha512-yYAuySSs+0X2gSMJPw/Q4jHsHZHyMq1dpAE+XnN790Jg1WuReUAD/+j+e4vuDvS4PXJ4PA4NCzTR4OYtDf18/g==",
1626+ "cpu": [
1627+ "x64"
1628+ ],
1629+ "dev": true,
1630+ "license": "MIT",
1631+ "optional": true,
1632+ "os": [
1633+ "win32"
1634+ ]
1635+ },
1636+ "node_modules/@rollup/rollup-win32-x64-msvc": {
1637+ "version": "4.64.2",
1638+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.64.2.tgz",
1639+ "integrity": "sha512-JY1rQdWOJrKutORqKO1uzrHXV86jFiQzfLFKx2s8S1CjLXL9fC5LOIvk0q7Z7PXJvsByt+sdoE1Q2a9gBr4gLQ==",
1640+ "cpu": [
1641+ "x64"
1642+ ],
1643+ "dev": true,
1644+ "license": "MIT",
1645+ "optional": true,
1646+ "os": [
1647+ "win32"
1648+ ]
1649+ },
1650+ "node_modules/@standard-schema/spec": {
1651+ "version": "1.1.0",
1652+ "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
1653+ "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
1654+ "dev": true,
1655+ "license": "MIT"
1656+ },
1657+ "node_modules/@storybook/addon-docs": {
1658+ "version": "10.6.1",
1659+ "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-10.6.1.tgz",
1660+ "integrity": "sha512-31PXR+MCWfI5YHKKah6tWnbV1FJWA9qbTHxtvpszPLd8FBekXtPOnmyYOpFF9CfciBF9bkeJFdFanJvUCjWbjA==",
1661+ "dev": true,
1662+ "license": "MIT",
1663+ "dependencies": {
1664+ "@mdx-js/react": "^3.0.0",
1665+ "@storybook/icons": "^2.0.2",
1666+ "@storybook/react-dom-shim": "10.6.1",
1667+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
1668+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
1669+ "ts-dedent": "^2.0.0",
1670+ "unplugin": "^2.3.5"
1671+ },
1672+ "funding": {
1673+ "type": "opencollective",
1674+ "url": "https://opencollective.com/storybook"
1675+ },
1676+ "peerDependencies": {
1677+ "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
1678+ "storybook": "^10.6.1"
1679+ },
1680+ "peerDependenciesMeta": {
1681+ "@types/react": {
1682+ "optional": true
1683+ }
1684+ }
1685+ },
1686+ "node_modules/@storybook/addon-mcp": {
1687+ "version": "10.6.1",
1688+ "resolved": "https://registry.npmjs.org/@storybook/addon-mcp/-/addon-mcp-10.6.1.tgz",
1689+ "integrity": "sha512-ILh1wAI/3SgkkjRi4lZRXwT1jq6gEGEjDed5Z2Gd6ykO3yfLX2QnBM3UG2FSDv+uMKyxv8P80uXRDgKUhtmcOQ==",
1690+ "dev": true,
1691+ "license": "MIT",
1692+ "dependencies": {
1693+ "@tmcp/adapter-valibot": "^0.1.5",
1694+ "@tmcp/transport-http": "^0.8.5",
1695+ "tmcp": "^1.19.4",
1696+ "valibot": "^1.4.0"
1697+ },
1698+ "funding": {
1699+ "type": "opencollective",
1700+ "url": "https://opencollective.com/storybook"
1701+ },
1702+ "peerDependencies": {
1703+ "@storybook/addon-vitest": "^10.6.1",
1704+ "storybook": "^10.6.1"
1705+ },
1706+ "peerDependenciesMeta": {
1707+ "@storybook/addon-vitest": {
1708+ "optional": true
1709+ }
1710+ }
1711+ },
1712+ "node_modules/@storybook/builder-vite": {
1713+ "version": "10.6.1",
1714+ "resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-10.6.1.tgz",
1715+ "integrity": "sha512-kUMRRRS4MpJdxOLhrlQX3cpMYi2vVB3tIIRXyqWmAjVM6xT7OZ1SCQEMFPP+S3ToRqaYWZQb8aBqrJucbbYufA==",
1716+ "dev": true,
1717+ "license": "MIT",
1718+ "dependencies": {
1719+ "ts-dedent": "^2.0.0"
1720+ },
1721+ "funding": {
1722+ "type": "opencollective",
1723+ "url": "https://opencollective.com/storybook"
1724+ },
1725+ "peerDependencies": {
1726+ "storybook": "^10.6.1",
1727+ "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
1728+ }
1729+ },
1730+ "node_modules/@storybook/global": {
1731+ "version": "5.0.0",
1732+ "resolved": "https://registry.npmjs.org/@storybook/global/-/global-5.0.0.tgz",
1733+ "integrity": "sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==",
1734+ "dev": true,
1735+ "license": "MIT"
1736+ },
1737+ "node_modules/@storybook/icons": {
1738+ "version": "2.1.0",
1739+ "resolved": "https://registry.npmjs.org/@storybook/icons/-/icons-2.1.0.tgz",
1740+ "integrity": "sha512-Fxh9vYpX9bQqFeHRiY8h2ApeRGDzRSMLwJwNZ/AIRqnyOKHxRKL+yFe+ctEkVJmuptRE9u1Hrn8ZZNHyfDKKNg==",
1741+ "dev": true,
1742+ "license": "MIT",
1743+ "peerDependencies": {
1744+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
1745+ }
1746+ },
1747+ "node_modules/@storybook/react-dom-shim": {
1748+ "version": "10.6.1",
1749+ "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-10.6.1.tgz",
1750+ "integrity": "sha512-OkPFycR6kVE+9ZsgQWRqnUfZm/sLY6dxsNAn3lsvV7DSAQWH9QnHLkqP9E/N/Hg6cKPDtGzDZ8vPNDz2FuaDuQ==",
1751+ "dev": true,
1752+ "license": "MIT",
1753+ "funding": {
1754+ "type": "opencollective",
1755+ "url": "https://opencollective.com/storybook"
1756+ },
1757+ "peerDependencies": {
1758+ "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
1759+ "@types/react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
1760+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
1761+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
1762+ "storybook": "^10.6.1"
1763+ },
1764+ "peerDependenciesMeta": {
1765+ "@types/react": {
1766+ "optional": true
1767+ },
1768+ "@types/react-dom": {
1769+ "optional": true
1770+ }
1771+ }
1772+ },
1773+ "node_modules/@storybook/svelte": {
1774+ "version": "10.6.1",
1775+ "resolved": "https://registry.npmjs.org/@storybook/svelte/-/svelte-10.6.1.tgz",
1776+ "integrity": "sha512-r4mH9gX2unus6IpBVAt5b9Q50SgoT2fMX+NmLoZ3WwTFwIfgWHV7aOYEZgJsk61KEFwUFtJp2il2nI67O3G8Xw==",
1777+ "dev": true,
1778+ "license": "MIT",
1779+ "dependencies": {
1780+ "ts-dedent": "^2.0.0",
1781+ "type-fest": "^5.6.0"
1782+ },
1783+ "funding": {
1784+ "type": "opencollective",
1785+ "url": "https://opencollective.com/storybook"
1786+ },
1787+ "peerDependencies": {
1788+ "storybook": "^10.6.1",
1789+ "svelte": "^5.0.0"
1790+ }
1791+ },
1792+ "node_modules/@storybook/svelte-vite": {
1793+ "version": "10.6.1",
1794+ "resolved": "https://registry.npmjs.org/@storybook/svelte-vite/-/svelte-vite-10.6.1.tgz",
1795+ "integrity": "sha512-xyA3EjKdW0gK7mM97pJSzdI3lkbjLzE6MZ3hBLWZtGBxl8UjpYeKwyi9AKvmiMaDoF646mFw0HJo0lSGrFmFDw==",
1796+ "dev": true,
1797+ "license": "MIT",
1798+ "dependencies": {
1799+ "@storybook/builder-vite": "10.6.1",
1800+ "@storybook/svelte": "10.6.1",
1801+ "magic-string": "^1.1.0",
1802+ "svelte2tsx": "^0.7.55",
1803+ "typescript": "^4.9.4 || ^5.0.0"
1804+ },
1805+ "funding": {
1806+ "type": "opencollective",
1807+ "url": "https://opencollective.com/storybook"
1808+ },
1809+ "peerDependencies": {
1810+ "@sveltejs/vite-plugin-svelte": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0",
1811+ "storybook": "^10.6.1",
1812+ "svelte": "^5.0.0",
1813+ "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
1814+ }
1815+ },
1816+ "node_modules/@storybook/svelte-vite/node_modules/typescript": {
1817+ "version": "5.9.3",
1818+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
1819+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
1820+ "dev": true,
1821+ "license": "Apache-2.0",
1822+ "bin": {
1823+ "tsc": "bin/tsc",
1824+ "tsserver": "bin/tsserver"
1825+ },
1826+ "engines": {
1827+ "node": ">=14.17"
1828+ }
1829+ },
1830+ "node_modules/@sveltejs/acorn-typescript": {
1831+ "version": "1.0.13",
1832+ "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.13.tgz",
1833+ "integrity": "sha512-wgKggnhZVL9Bfx1OaKKTrYY9BFRk6C8UAkQNUcIv1+llzYrIqy+RZm5HPKzn0NpEBvTVhTqB4kQyllZywsRBRQ==",
1834+ "license": "MIT",
1835+ "peerDependencies": {
1836+ "acorn": "^8.9.0"
1837+ }
1838+ },
1839+ "node_modules/@sveltejs/vite-plugin-svelte": {
1840+ "version": "4.0.4",
1841+ "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-4.0.4.tgz",
1842+ "integrity": "sha512-0ba1RQ/PHen5FGpdSrW7Y3fAMQjrXantECALeOiOdBdzR5+5vPP6HVZRLmZaQL+W8m++o+haIAKq5qT+MiZ7VA==",
1843+ "dev": true,
1844+ "license": "MIT",
1845+ "dependencies": {
1846+ "@sveltejs/vite-plugin-svelte-inspector": "^3.0.0-next.0||^3.0.0",
1847+ "debug": "^4.3.7",
1848+ "deepmerge": "^4.3.1",
1849+ "kleur": "^4.1.5",
1850+ "magic-string": "^0.30.12",
1851+ "vitefu": "^1.0.3"
1852+ },
1853+ "engines": {
1854+ "node": "^18.0.0 || ^20.0.0 || >=22"
1855+ },
1856+ "peerDependencies": {
1857+ "svelte": "^5.0.0-next.96 || ^5.0.0",
1858+ "vite": "^5.0.0"
1859+ }
1860+ },
1861+ "node_modules/@sveltejs/vite-plugin-svelte-inspector": {
1862+ "version": "3.0.1",
1863+ "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-3.0.1.tgz",
1864+ "integrity": "sha512-2CKypmj1sM4GE7HjllT7UKmo4Q6L5xFRd7VMGEWhYnZ+wc6AUVU01IBd7yUi6WnFndEwWoMNOd6e8UjoN0nbvQ==",
1865+ "dev": true,
1866+ "license": "MIT",
1867+ "dependencies": {
1868+ "debug": "^4.3.7"
1869+ },
1870+ "engines": {
1871+ "node": "^18.0.0 || ^20.0.0 || >=22"
1872+ },
1873+ "peerDependencies": {
1874+ "@sveltejs/vite-plugin-svelte": "^4.0.0-next.0||^4.0.0",
1875+ "svelte": "^5.0.0-next.96 || ^5.0.0",
1876+ "vite": "^5.0.0"
1877+ }
1878+ },
1879+ "node_modules/@sveltejs/vite-plugin-svelte/node_modules/magic-string": {
1880+ "version": "0.30.21",
1881+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
1882+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
1883+ "dev": true,
1884+ "license": "MIT",
1885+ "dependencies": {
1886+ "@jridgewell/sourcemap-codec": "^1.5.5"
1887+ }
1888+ },
1889+ "node_modules/@testing-library/dom": {
1890+ "version": "10.4.2",
1891+ "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.2.tgz",
1892+ "integrity": "sha512-yzr2S9HyAIdhz2/6qHgbs665Q7PKVcDF05vsOlHPxG1mo36gKVesdYVeDLnXgfjJ03CrKRk08knc6+E/9m8v2Q==",
1893+ "dev": true,
1894+ "license": "MIT",
1895+ "dependencies": {
1896+ "@babel/code-frame": "^7.10.4",
1897+ "@babel/runtime": "^7.12.5",
1898+ "@types/aria-query": "^5.0.1",
1899+ "aria-query": "5.3.0",
1900+ "dom-accessibility-api": "^0.5.9",
1901+ "lz-string": "^1.5.0",
1902+ "picocolors": "1.1.1",
1903+ "pretty-format": "^27.0.2"
1904+ },
1905+ "engines": {
1906+ "node": ">=18"
1907+ }
1908+ },
1909+ "node_modules/@testing-library/jest-dom": {
1910+ "version": "6.9.1",
1911+ "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz",
1912+ "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==",
1913+ "dev": true,
1914+ "license": "MIT",
1915+ "dependencies": {
1916+ "@adobe/css-tools": "^4.4.0",
1917+ "aria-query": "^5.0.0",
1918+ "css.escape": "^1.5.1",
1919+ "dom-accessibility-api": "^0.6.3",
1920+ "picocolors": "^1.1.1",
1921+ "redent": "^3.0.0"
1922+ },
1923+ "engines": {
1924+ "node": ">=14",
1925+ "npm": ">=6",
1926+ "yarn": ">=1"
1927+ }
1928+ },
1929+ "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": {
1930+ "version": "0.6.3",
1931+ "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz",
1932+ "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==",
1933+ "dev": true,
1934+ "license": "MIT"
1935+ },
1936+ "node_modules/@testing-library/user-event": {
1937+ "version": "14.6.7",
1938+ "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.7.tgz",
1939+ "integrity": "sha512-MPCpX8bxe8zS+JmmTwLp8jd0dy1rAm60Te/SL8JrQM3qvQJcBOs1d7IefJMyZzqM3EWBrDn/LWDt1BCGu4ASfg==",
1940+ "dev": true,
1941+ "license": "MIT",
1942+ "engines": {
1943+ "node": ">=12",
1944+ "npm": ">=6"
1945+ },
1946+ "peerDependencies": {
1947+ "@testing-library/dom": ">=7.21.4"
1948+ }
1949+ },
1950+ "node_modules/@tmcp/adapter-valibot": {
1951+ "version": "0.1.6",
1952+ "resolved": "https://registry.npmjs.org/@tmcp/adapter-valibot/-/adapter-valibot-0.1.6.tgz",
1953+ "integrity": "sha512-drirZeNinhYLiRSMksN+m//u0ImFxtGRk1Vp425Xp/7CbBXFQdjAG+f7grssyHAukbVTGzmWsMMP6ejrGVErUA==",
1954+ "dev": true,
1955+ "license": "MIT",
1956+ "dependencies": {
1957+ "@standard-schema/spec": "^1.0.0",
1958+ "@valibot/to-json-schema": "^1.3.0",
1959+ "valibot": "^1.1.0"
1960+ },
1961+ "peerDependencies": {
1962+ "tmcp": "^1.17.0",
1963+ "valibot": "^1.1.0"
1964+ }
1965+ },
1966+ "node_modules/@tmcp/session-manager": {
1967+ "version": "0.2.2",
1968+ "resolved": "https://registry.npmjs.org/@tmcp/session-manager/-/session-manager-0.2.2.tgz",
1969+ "integrity": "sha512-UrCRpTsxh5XnMbplspvftEYboiZWgAiXqqAUbyFTHoHMJ0LoNDy8bQd0+7qtxtT4S5Qsnv650gvs/Nbec5NTCQ==",
1970+ "dev": true,
1971+ "license": "MIT",
1972+ "peerDependencies": {
1973+ "tmcp": "^1.16.3"
1974+ }
1975+ },
1976+ "node_modules/@tmcp/transport-http": {
1977+ "version": "0.8.6",
1978+ "resolved": "https://registry.npmjs.org/@tmcp/transport-http/-/transport-http-0.8.6.tgz",
1979+ "integrity": "sha512-iLcxu+tEMbkVHbhFfyXQhxfPDDTfm+F0kEw8Xg/a1rm29s4cBg1vwcpbtk02XTxsdDh8RJ1AZkQwF9WDGeb/IA==",
1980+ "dev": true,
1981+ "license": "MIT",
1982+ "dependencies": {
1983+ "@tmcp/session-manager": "^0.2.2",
1984+ "esm-env": "^1.2.2"
1985+ },
1986+ "peerDependencies": {
1987+ "@tmcp/auth": "^0.3.3 || ^0.4.0",
1988+ "tmcp": "^1.18.0"
1989+ },
1990+ "peerDependenciesMeta": {
1991+ "@tmcp/auth": {
1992+ "optional": true
1993+ }
1994+ }
1995+ },
1996+ "node_modules/@tybys/wasm-util": {
1997+ "version": "0.10.4",
1998+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.4.tgz",
1999+ "integrity": "sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A==",
2000+ "dev": true,
2001+ "license": "MIT",
2002+ "optional": true,
2003+ "dependencies": {
2004+ "tslib": "^2.4.0"
2005+ }
2006+ },
2007+ "node_modules/@types/aria-query": {
2008+ "version": "5.0.4",
2009+ "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
2010+ "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
2011+ "dev": true,
2012+ "license": "MIT"
2013+ },
2014+ "node_modules/@types/chai": {
2015+ "version": "5.2.3",
2016+ "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz",
2017+ "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==",
2018+ "dev": true,
2019+ "license": "MIT",
2020+ "dependencies": {
2021+ "@types/deep-eql": "*",
2022+ "assertion-error": "^2.0.1"
2023+ }
2024+ },
2025+ "node_modules/@types/deep-eql": {
2026+ "version": "4.0.2",
2027+ "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
2028+ "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
2029+ "dev": true,
2030+ "license": "MIT"
2031+ },
2032+ "node_modules/@types/estree": {
2033+ "version": "1.0.9",
2034+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
2035+ "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
2036+ "license": "MIT"
2037+ },
2038+ "node_modules/@types/mdx": {
2039+ "version": "2.0.14",
2040+ "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.14.tgz",
2041+ "integrity": "sha512-T48PeuJtvLosNTPVhfnIp3i/n3a4g4Bad7YCq5k64D4u7NwDrAotikQ+5+sjtUvBmxCMlbo3dVL+C2dP0rWHzg==",
2042+ "dev": true,
2043+ "license": "MIT"
2044+ },
2045+ "node_modules/@types/react": {
2046+ "version": "19.3.0",
2047+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.3.0.tgz",
2048+ "integrity": "sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==",
2049+ "dev": true,
2050+ "license": "MIT",
2051+ "peer": true,
2052+ "dependencies": {
2053+ "csstype": "^3.2.2"
2054+ }
2055+ },
2056+ "node_modules/@valibot/to-json-schema": {
2057+ "version": "1.8.0",
2058+ "resolved": "https://registry.npmjs.org/@valibot/to-json-schema/-/to-json-schema-1.8.0.tgz",
2059+ "integrity": "sha512-a0M+uwCuQZEPAo65NYkFSJ14O5c213KoSZmPdoCfuFvUhfULi4T2Z6Tpjv6lTVMW9RoLm74RpRAjNEa43KcN+g==",
2060+ "dev": true,
2061+ "license": "MIT",
2062+ "peerDependencies": {
2063+ "valibot": "^1.5.0"
2064+ }
2065+ },
2066+ "node_modules/@vitest/expect": {
2067+ "version": "3.2.4",
2068+ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz",
2069+ "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==",
2070+ "dev": true,
2071+ "license": "MIT",
2072+ "dependencies": {
2073+ "@types/chai": "^5.2.2",
2074+ "@vitest/spy": "3.2.4",
2075+ "@vitest/utils": "3.2.4",
2076+ "chai": "^5.2.0",
2077+ "tinyrainbow": "^2.0.0"
2078+ },
2079+ "funding": {
2080+ "url": "https://opencollective.com/vitest"
2081+ }
2082+ },
2083+ "node_modules/@vitest/pretty-format": {
2084+ "version": "3.2.4",
2085+ "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz",
2086+ "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==",
2087+ "dev": true,
2088+ "license": "MIT",
2089+ "dependencies": {
2090+ "tinyrainbow": "^2.0.0"
2091+ },
2092+ "funding": {
2093+ "url": "https://opencollective.com/vitest"
2094+ }
2095+ },
2096+ "node_modules/@vitest/spy": {
2097+ "version": "3.2.4",
2098+ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz",
2099+ "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==",
2100+ "dev": true,
2101+ "license": "MIT",
2102+ "dependencies": {
2103+ "tinyspy": "^4.0.3"
2104+ },
2105+ "funding": {
2106+ "url": "https://opencollective.com/vitest"
2107+ }
2108+ },
2109+ "node_modules/@vitest/utils": {
2110+ "version": "3.2.4",
2111+ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz",
2112+ "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==",
2113+ "dev": true,
2114+ "license": "MIT",
2115+ "dependencies": {
2116+ "@vitest/pretty-format": "3.2.4",
2117+ "loupe": "^3.1.4",
2118+ "tinyrainbow": "^2.0.0"
2119+ },
2120+ "funding": {
2121+ "url": "https://opencollective.com/vitest"
2122+ }
2123+ },
2124+ "node_modules/@webcontainer/env": {
2125+ "version": "1.1.1",
2126+ "resolved": "https://registry.npmjs.org/@webcontainer/env/-/env-1.1.1.tgz",
2127+ "integrity": "sha512-6aN99yL695Hi9SuIk1oC88l9o0gmxL1nGWWQ/kNy81HigJ0FoaoTXpytCj6ItzgyCEwA9kF1wixsTuv5cjsgng==",
2128+ "dev": true,
2129+ "license": "MIT"
2130+ },
2131+ "node_modules/acorn": {
2132+ "version": "8.19.0",
2133+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.19.0.tgz",
2134+ "integrity": "sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==",
2135+ "license": "MIT",
2136+ "bin": {
2137+ "acorn": "bin/acorn"
2138+ },
2139+ "engines": {
2140+ "node": ">=0.4.0"
2141+ }
2142+ },
2143+ "node_modules/ansi-regex": {
2144+ "version": "5.0.1",
2145+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
2146+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
2147+ "dev": true,
2148+ "license": "MIT",
2149+ "engines": {
2150+ "node": ">=8"
2151+ }
2152+ },
2153+ "node_modules/ansi-styles": {
2154+ "version": "5.2.0",
2155+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
2156+ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
2157+ "dev": true,
2158+ "license": "MIT",
2159+ "engines": {
2160+ "node": ">=10"
2161+ },
2162+ "funding": {
2163+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
2164+ }
2165+ },
2166+ "node_modules/aria-query": {
2167+ "version": "5.3.0",
2168+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
2169+ "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
2170+ "dev": true,
2171+ "license": "Apache-2.0",
2172+ "dependencies": {
2173+ "dequal": "^2.0.3"
2174+ }
2175+ },
2176+ "node_modules/assertion-error": {
2177+ "version": "2.0.1",
2178+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
2179+ "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
2180+ "dev": true,
2181+ "license": "MIT",
2182+ "engines": {
2183+ "node": ">=12"
2184+ }
2185+ },
2186+ "node_modules/ast-types": {
2187+ "version": "0.16.3",
2188+ "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.3.tgz",
2189+ "integrity": "sha512-FvWoWYfSCM6kRxCSH+MGLHIKKGRL6A6AW7Zek2O32REPQRdg131428uRTKMBYAeRd3XXAaHDS60Wpri7CdKDrA==",
2190+ "dev": true,
2191+ "license": "MIT",
2192+ "dependencies": {
2193+ "tslib": "^2.0.1"
2194+ },
2195+ "engines": {
2196+ "node": ">=4"
2197+ }
2198+ },
2199+ "node_modules/axobject-query": {
2200+ "version": "4.1.0",
2201+ "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
2202+ "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==",
2203+ "license": "Apache-2.0",
2204+ "engines": {
2205+ "node": ">= 0.4"
2206+ }
2207+ },
2208+ "node_modules/bundle-name": {
2209+ "version": "4.1.1",
2210+ "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.1.tgz",
2211+ "integrity": "sha512-DdH81/zPLVS11EUgWq3tEu/xn+EzljlMYooDNdzWEnFha3R3NBMpMV1UqYIpjYHV/SgpFKMIX1Oh7o06SjM/oA==",
2212+ "dev": true,
2213+ "license": "MIT",
2214+ "dependencies": {
2215+ "run-applescript": "^7.0.0"
2216+ },
2217+ "engines": {
2218+ "node": ">=18"
2219+ },
2220+ "funding": {
2221+ "url": "https://github.com/sponsors/sindresorhus"
2222+ }
2223+ },
2224+ "node_modules/chai": {
2225+ "version": "5.3.3",
2226+ "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz",
2227+ "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==",
2228+ "dev": true,
2229+ "license": "MIT",
2230+ "dependencies": {
2231+ "assertion-error": "^2.0.1",
2232+ "check-error": "^2.1.1",
2233+ "deep-eql": "^5.0.1",
2234+ "loupe": "^3.1.0",
2235+ "pathval": "^2.0.0"
2236+ },
2237+ "engines": {
2238+ "node": ">=18"
2239+ }
2240+ },
2241+ "node_modules/check-error": {
2242+ "version": "2.1.3",
2243+ "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz",
2244+ "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==",
2245+ "dev": true,
2246+ "license": "MIT",
2247+ "engines": {
2248+ "node": ">= 16"
2249+ }
2250+ },
2251+ "node_modules/clsx": {
2252+ "version": "2.1.1",
2253+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
2254+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
2255+ "license": "MIT",
2256+ "engines": {
2257+ "node": ">=6"
2258+ }
2259+ },
2260+ "node_modules/css.escape": {
2261+ "version": "1.5.1",
2262+ "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz",
2263+ "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==",
2264+ "dev": true,
2265+ "license": "MIT"
2266+ },
2267+ "node_modules/csstype": {
2268+ "version": "3.2.3",
2269+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
2270+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
2271+ "dev": true,
2272+ "license": "MIT",
2273+ "peer": true
2274+ },
2275+ "node_modules/debug": {
2276+ "version": "4.4.3",
2277+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
2278+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
2279+ "dev": true,
2280+ "license": "MIT",
2281+ "dependencies": {
9352282 "ms": "^2.1.3"
9362283 },
9372284 "engines": {
938- "node": ">=6.0"
939- },
940- "peerDependenciesMeta": {
941- "supports-color": {
942- "optional": true
943- }
2285+ "node": ">=6.0"
2286+ },
2287+ "peerDependenciesMeta": {
2288+ "supports-color": {
2289+ "optional": true
2290+ }
2291+ }
2292+ },
2293+ "node_modules/dedent-js": {
2294+ "version": "1.0.1",
2295+ "resolved": "https://registry.npmjs.org/dedent-js/-/dedent-js-1.0.1.tgz",
2296+ "integrity": "sha512-OUepMozQULMLUmhxS95Vudo0jb0UchLimi3+pQ2plj61Fcy8axbP9hbiD4Sz6DPqn6XG3kfmziVfQ1rSys5AJQ==",
2297+ "dev": true,
2298+ "license": "MIT"
2299+ },
2300+ "node_modules/deep-eql": {
2301+ "version": "5.0.2",
2302+ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
2303+ "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==",
2304+ "dev": true,
2305+ "license": "MIT",
2306+ "engines": {
2307+ "node": ">=6"
2308+ }
2309+ },
2310+ "node_modules/deepmerge": {
2311+ "version": "4.3.1",
2312+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
2313+ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
2314+ "dev": true,
2315+ "license": "MIT",
2316+ "engines": {
2317+ "node": ">=0.10.0"
2318+ }
2319+ },
2320+ "node_modules/default-browser": {
2321+ "version": "5.5.1",
2322+ "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.1.tgz",
2323+ "integrity": "sha512-m1pAzaJgZ/gssEqlOhJkPJp8Xly7QyW6xcrkUa2KKcDeDSEMP7X8xipU3snUcfisTQx0w1AGae+9UtJSfVnXGw==",
2324+ "dev": true,
2325+ "license": "MIT",
2326+ "dependencies": {
2327+ "bundle-name": "^4.1.0",
2328+ "default-browser-id": "^5.0.0"
2329+ },
2330+ "engines": {
2331+ "node": ">=18"
2332+ },
2333+ "funding": {
2334+ "url": "https://github.com/sponsors/sindresorhus"
2335+ }
2336+ },
2337+ "node_modules/default-browser-id": {
2338+ "version": "5.0.1",
2339+ "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz",
2340+ "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==",
2341+ "dev": true,
2342+ "license": "MIT",
2343+ "engines": {
2344+ "node": ">=18"
2345+ },
2346+ "funding": {
2347+ "url": "https://github.com/sponsors/sindresorhus"
2348+ }
2349+ },
2350+ "node_modules/define-lazy-prop": {
2351+ "version": "3.0.0",
2352+ "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz",
2353+ "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==",
2354+ "dev": true,
2355+ "license": "MIT",
2356+ "engines": {
2357+ "node": ">=12"
2358+ },
2359+ "funding": {
2360+ "url": "https://github.com/sponsors/sindresorhus"
2361+ }
2362+ },
2363+ "node_modules/dequal": {
2364+ "version": "2.0.3",
2365+ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
2366+ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
2367+ "dev": true,
2368+ "license": "MIT",
2369+ "engines": {
2370+ "node": ">=6"
2371+ }
2372+ },
2373+ "node_modules/devalue": {
2374+ "version": "5.9.4",
2375+ "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.9.4.tgz",
2376+ "integrity": "sha512-sPAT4pztbu6586/hrhOnMKS17IJrvg12mXiSPSS3W5qDeN2RGgvZ0diZCm31dBbnevfVmujNO3IM2wrS4Y2Rhg==",
2377+ "license": "MIT"
2378+ },
2379+ "node_modules/dom-accessibility-api": {
2380+ "version": "0.5.16",
2381+ "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
2382+ "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
2383+ "dev": true,
2384+ "license": "MIT"
2385+ },
2386+ "node_modules/esbuild": {
2387+ "version": "0.28.2",
2388+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz",
2389+ "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==",
2390+ "dev": true,
2391+ "hasInstallScript": true,
2392+ "license": "MIT",
2393+ "bin": {
2394+ "esbuild": "bin/esbuild"
2395+ },
2396+ "engines": {
2397+ "node": ">=18"
2398+ },
2399+ "optionalDependencies": {
2400+ "@esbuild/aix-ppc64": "0.28.2",
2401+ "@esbuild/android-arm": "0.28.2",
2402+ "@esbuild/android-arm64": "0.28.2",
2403+ "@esbuild/android-x64": "0.28.2",
2404+ "@esbuild/darwin-arm64": "0.28.2",
2405+ "@esbuild/darwin-x64": "0.28.2",
2406+ "@esbuild/freebsd-arm64": "0.28.2",
2407+ "@esbuild/freebsd-x64": "0.28.2",
2408+ "@esbuild/linux-arm": "0.28.2",
2409+ "@esbuild/linux-arm64": "0.28.2",
2410+ "@esbuild/linux-ia32": "0.28.2",
2411+ "@esbuild/linux-loong64": "0.28.2",
2412+ "@esbuild/linux-mips64el": "0.28.2",
2413+ "@esbuild/linux-ppc64": "0.28.2",
2414+ "@esbuild/linux-riscv64": "0.28.2",
2415+ "@esbuild/linux-s390x": "0.28.2",
2416+ "@esbuild/linux-x64": "0.28.2",
2417+ "@esbuild/netbsd-arm64": "0.28.2",
2418+ "@esbuild/netbsd-x64": "0.28.2",
2419+ "@esbuild/openbsd-arm64": "0.28.2",
2420+ "@esbuild/openbsd-x64": "0.28.2",
2421+ "@esbuild/openharmony-arm64": "0.28.2",
2422+ "@esbuild/sunos-x64": "0.28.2",
2423+ "@esbuild/win32-arm64": "0.28.2",
2424+ "@esbuild/win32-ia32": "0.28.2",
2425+ "@esbuild/win32-x64": "0.28.2"
2426+ }
2427+ },
2428+ "node_modules/esm-env": {
2429+ "version": "1.2.2",
2430+ "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz",
2431+ "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==",
2432+ "license": "MIT"
2433+ },
2434+ "node_modules/esprima": {
2435+ "version": "4.0.1",
2436+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
2437+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
2438+ "dev": true,
2439+ "license": "BSD-2-Clause",
2440+ "bin": {
2441+ "esparse": "bin/esparse.js",
2442+ "esvalidate": "bin/esvalidate.js"
2443+ },
2444+ "engines": {
2445+ "node": ">=4"
2446+ }
2447+ },
2448+ "node_modules/esrap": {
2449+ "version": "2.4.0",
2450+ "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.4.0.tgz",
2451+ "integrity": "sha512-CQPcneEHw/czj32uwQC52nYnax8H4SXt25FCM+LIwGZ49cMIxpOj3bP8wmzcI+Z0HIy5KkClaPhRLfCySzwHPA==",
2452+ "license": "MIT",
2453+ "dependencies": {
2454+ "@jridgewell/sourcemap-codec": "^1.4.15"
2455+ },
2456+ "peerDependencies": {
2457+ "@typescript-eslint/types": "^8.2.0"
2458+ },
2459+ "peerDependenciesMeta": {
2460+ "@typescript-eslint/types": {
2461+ "optional": true
2462+ }
2463+ }
2464+ },
2465+ "node_modules/fsevents": {
2466+ "version": "2.3.3",
2467+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
2468+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
2469+ "dev": true,
2470+ "hasInstallScript": true,
2471+ "license": "MIT",
2472+ "optional": true,
2473+ "os": [
2474+ "darwin"
2475+ ],
2476+ "engines": {
2477+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
2478+ }
2479+ },
2480+ "node_modules/indent-string": {
2481+ "version": "4.0.0",
2482+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
2483+ "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
2484+ "dev": true,
2485+ "license": "MIT",
2486+ "engines": {
2487+ "node": ">=8"
2488+ }
2489+ },
2490+ "node_modules/is-docker": {
2491+ "version": "3.0.0",
2492+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz",
2493+ "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==",
2494+ "dev": true,
2495+ "license": "MIT",
2496+ "bin": {
2497+ "is-docker": "cli.js"
2498+ },
2499+ "engines": {
2500+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
2501+ },
2502+ "funding": {
2503+ "url": "https://github.com/sponsors/sindresorhus"
2504+ }
2505+ },
2506+ "node_modules/is-inside-container": {
2507+ "version": "1.0.0",
2508+ "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz",
2509+ "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==",
2510+ "dev": true,
2511+ "license": "MIT",
2512+ "dependencies": {
2513+ "is-docker": "^3.0.0"
2514+ },
2515+ "bin": {
2516+ "is-inside-container": "cli.js"
2517+ },
2518+ "engines": {
2519+ "node": ">=14.16"
2520+ },
2521+ "funding": {
2522+ "url": "https://github.com/sponsors/sindresorhus"
2523+ }
2524+ },
2525+ "node_modules/is-reference": {
2526+ "version": "3.0.3",
2527+ "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz",
2528+ "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==",
2529+ "license": "MIT",
2530+ "dependencies": {
2531+ "@types/estree": "^1.0.6"
2532+ }
2533+ },
2534+ "node_modules/is-wsl": {
2535+ "version": "3.1.1",
2536+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz",
2537+ "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==",
2538+ "dev": true,
2539+ "license": "MIT",
2540+ "dependencies": {
2541+ "is-inside-container": "^1.0.0"
2542+ },
2543+ "engines": {
2544+ "node": ">=16"
2545+ },
2546+ "funding": {
2547+ "url": "https://github.com/sponsors/sindresorhus"
2548+ }
2549+ },
2550+ "node_modules/js-tokens": {
2551+ "version": "4.0.0",
2552+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
2553+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
2554+ "dev": true,
2555+ "license": "MIT"
2556+ },
2557+ "node_modules/json-rpc-2.0": {
2558+ "version": "1.8.1",
2559+ "resolved": "https://registry.npmjs.org/json-rpc-2.0/-/json-rpc-2.0-1.8.1.tgz",
2560+ "integrity": "sha512-BmN42JKn6FI36pcMMzHCXQhpKNEfbeqgSIcUYHKnifq0/4Ey6PDinovwUOgajT7NsgUtpWdDXaCWlpI4LHXN3g==",
2561+ "dev": true,
2562+ "license": "MIT"
2563+ },
2564+ "node_modules/jsonc-parser": {
2565+ "version": "3.3.1",
2566+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz",
2567+ "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==",
2568+ "dev": true,
2569+ "license": "MIT"
2570+ },
2571+ "node_modules/kleur": {
2572+ "version": "4.1.5",
2573+ "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
2574+ "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==",
2575+ "dev": true,
2576+ "license": "MIT",
2577+ "engines": {
2578+ "node": ">=6"
2579+ }
2580+ },
2581+ "node_modules/locate-character": {
2582+ "version": "3.0.0",
2583+ "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz",
2584+ "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==",
2585+ "license": "MIT"
2586+ },
2587+ "node_modules/loupe": {
2588+ "version": "3.2.1",
2589+ "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz",
2590+ "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==",
2591+ "dev": true,
2592+ "license": "MIT"
2593+ },
2594+ "node_modules/lz-string": {
2595+ "version": "1.5.0",
2596+ "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
2597+ "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
2598+ "dev": true,
2599+ "license": "MIT",
2600+ "bin": {
2601+ "lz-string": "bin/bin.js"
2602+ }
2603+ },
2604+ "node_modules/magic-string": {
2605+ "version": "1.4.3",
2606+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.3.tgz",
2607+ "integrity": "sha512-z12OxmaPGE0F4xlpGdjuAUckipLpQlTAuAmyGhNoD7ODpYUzvDfvtxUbjM/jwznyP178oju/KDdyi220wNJ0RQ==",
2608+ "dev": true,
2609+ "license": "MIT",
2610+ "dependencies": {
2611+ "@jridgewell/sourcemap-codec": "^1.6.0"
2612+ }
2613+ },
2614+ "node_modules/min-indent": {
2615+ "version": "1.0.1",
2616+ "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
2617+ "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",
2618+ "dev": true,
2619+ "license": "MIT",
2620+ "engines": {
2621+ "node": ">=4"
2622+ }
2623+ },
2624+ "node_modules/ms": {
2625+ "version": "2.1.3",
2626+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
2627+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
2628+ "dev": true,
2629+ "license": "MIT"
2630+ },
2631+ "node_modules/nanoid": {
2632+ "version": "3.3.20",
2633+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz",
2634+ "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==",
2635+ "dev": true,
2636+ "funding": [
2637+ {
2638+ "type": "github",
2639+ "url": "https://github.com/sponsors/ai"
2640+ }
2641+ ],
2642+ "license": "MIT",
2643+ "bin": {
2644+ "nanoid": "bin/nanoid.cjs"
2645+ },
2646+ "engines": {
2647+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
2648+ }
2649+ },
2650+ "node_modules/open": {
2651+ "version": "10.2.0",
2652+ "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz",
2653+ "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==",
2654+ "dev": true,
2655+ "license": "MIT",
2656+ "dependencies": {
2657+ "default-browser": "^5.2.1",
2658+ "define-lazy-prop": "^3.0.0",
2659+ "is-inside-container": "^1.0.0",
2660+ "wsl-utils": "^0.1.0"
2661+ },
2662+ "engines": {
2663+ "node": ">=18"
2664+ },
2665+ "funding": {
2666+ "url": "https://github.com/sponsors/sindresorhus"
2667+ }
2668+ },
2669+ "node_modules/oxc-parser": {
2670+ "version": "0.127.0",
2671+ "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.127.0.tgz",
2672+ "integrity": "sha512-bkgD4qHlN7WxLdX8bLXdaU54TtQtAIg/ZBAfm0aje/mo3MRDo3P0hZSgr4U7O3xfX+fQmR5AP04JS/TGcZLcFA==",
2673+ "dev": true,
2674+ "license": "MIT",
2675+ "dependencies": {
2676+ "@oxc-project/types": "^0.127.0"
2677+ },
2678+ "engines": {
2679+ "node": "^20.19.0 || >=22.12.0"
2680+ },
2681+ "funding": {
2682+ "url": "https://github.com/sponsors/Boshen"
2683+ },
2684+ "optionalDependencies": {
2685+ "@oxc-parser/binding-android-arm-eabi": "0.127.0",
2686+ "@oxc-parser/binding-android-arm64": "0.127.0",
2687+ "@oxc-parser/binding-darwin-arm64": "0.127.0",
2688+ "@oxc-parser/binding-darwin-x64": "0.127.0",
2689+ "@oxc-parser/binding-freebsd-x64": "0.127.0",
2690+ "@oxc-parser/binding-linux-arm-gnueabihf": "0.127.0",
2691+ "@oxc-parser/binding-linux-arm-musleabihf": "0.127.0",
2692+ "@oxc-parser/binding-linux-arm64-gnu": "0.127.0",
2693+ "@oxc-parser/binding-linux-arm64-musl": "0.127.0",
2694+ "@oxc-parser/binding-linux-ppc64-gnu": "0.127.0",
2695+ "@oxc-parser/binding-linux-riscv64-gnu": "0.127.0",
2696+ "@oxc-parser/binding-linux-riscv64-musl": "0.127.0",
2697+ "@oxc-parser/binding-linux-s390x-gnu": "0.127.0",
2698+ "@oxc-parser/binding-linux-x64-gnu": "0.127.0",
2699+ "@oxc-parser/binding-linux-x64-musl": "0.127.0",
2700+ "@oxc-parser/binding-openharmony-arm64": "0.127.0",
2701+ "@oxc-parser/binding-wasm32-wasi": "0.127.0",
2702+ "@oxc-parser/binding-win32-arm64-msvc": "0.127.0",
2703+ "@oxc-parser/binding-win32-ia32-msvc": "0.127.0",
2704+ "@oxc-parser/binding-win32-x64-msvc": "0.127.0"
2705+ }
2706+ },
2707+ "node_modules/oxc-resolver": {
2708+ "version": "11.21.2",
2709+ "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.21.2.tgz",
2710+ "integrity": "sha512-w5tLwYN3Zo24w5EeWJjJWZOwhYqTtC8PS2B1tIt7BZUuqTIcU07sQValbDw+rq7+AuAGzOHklgK+ifsy4lpXfw==",
2711+ "dev": true,
2712+ "license": "MIT",
2713+ "funding": {
2714+ "url": "https://github.com/sponsors/Boshen"
2715+ },
2716+ "optionalDependencies": {
2717+ "@oxc-resolver/binding-android-arm-eabi": "11.21.2",
2718+ "@oxc-resolver/binding-android-arm64": "11.21.2",
2719+ "@oxc-resolver/binding-darwin-arm64": "11.21.2",
2720+ "@oxc-resolver/binding-darwin-x64": "11.21.2",
2721+ "@oxc-resolver/binding-freebsd-x64": "11.21.2",
2722+ "@oxc-resolver/binding-linux-arm-gnueabihf": "11.21.2",
2723+ "@oxc-resolver/binding-linux-arm-musleabihf": "11.21.2",
2724+ "@oxc-resolver/binding-linux-arm64-gnu": "11.21.2",
2725+ "@oxc-resolver/binding-linux-arm64-musl": "11.21.2",
2726+ "@oxc-resolver/binding-linux-ppc64-gnu": "11.21.2",
2727+ "@oxc-resolver/binding-linux-riscv64-gnu": "11.21.2",
2728+ "@oxc-resolver/binding-linux-riscv64-musl": "11.21.2",
2729+ "@oxc-resolver/binding-linux-s390x-gnu": "11.21.2",
2730+ "@oxc-resolver/binding-linux-x64-gnu": "11.21.2",
2731+ "@oxc-resolver/binding-linux-x64-musl": "11.21.2",
2732+ "@oxc-resolver/binding-openharmony-arm64": "11.21.2",
2733+ "@oxc-resolver/binding-wasm32-wasi": "11.21.2",
2734+ "@oxc-resolver/binding-win32-arm64-msvc": "11.21.2",
2735+ "@oxc-resolver/binding-win32-x64-msvc": "11.21.2"
2736+ }
2737+ },
2738+ "node_modules/pathval": {
2739+ "version": "2.0.1",
2740+ "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz",
2741+ "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==",
2742+ "dev": true,
2743+ "license": "MIT",
2744+ "engines": {
2745+ "node": ">= 14.16"
2746+ }
2747+ },
2748+ "node_modules/picocolors": {
2749+ "version": "1.1.1",
2750+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
2751+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
2752+ "dev": true,
2753+ "license": "ISC"
2754+ },
2755+ "node_modules/picomatch": {
2756+ "version": "4.0.7",
2757+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz",
2758+ "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==",
2759+ "dev": true,
2760+ "license": "MIT",
2761+ "engines": {
2762+ "node": ">=12"
2763+ },
2764+ "funding": {
2765+ "url": "https://github.com/sponsors/jonschlinkert"
2766+ }
2767+ },
2768+ "node_modules/postcss": {
2769+ "version": "8.5.29",
2770+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.29.tgz",
2771+ "integrity": "sha512-49cGhUbXj8Qenv0iTMxA1cFBzxXoctpC9Ujd77t1WcbJIr6nF/eI7g/8MgxrYldFRuAXvja7xQRwavoW7kgrxQ==",
2772+ "dev": true,
2773+ "funding": [
2774+ {
2775+ "type": "opencollective",
2776+ "url": "https://opencollective.com/postcss/"
2777+ },
2778+ {
2779+ "type": "tidelift",
2780+ "url": "https://tidelift.com/funding/github/npm/postcss"
2781+ },
2782+ {
2783+ "type": "github",
2784+ "url": "https://github.com/sponsors/ai"
2785+ }
2786+ ],
2787+ "license": "MIT",
2788+ "dependencies": {
2789+ "nanoid": "^3.3.19",
2790+ "picocolors": "^1.1.1",
2791+ "source-map-js": "^1.2.2"
2792+ },
2793+ "engines": {
2794+ "node": "^10 || ^12 || >=14"
2795+ }
2796+ },
2797+ "node_modules/pretty-format": {
2798+ "version": "27.5.1",
2799+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
2800+ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
2801+ "dev": true,
2802+ "license": "MIT",
2803+ "dependencies": {
2804+ "ansi-regex": "^5.0.1",
2805+ "ansi-styles": "^5.0.0",
2806+ "react-is": "^17.0.1"
2807+ },
2808+ "engines": {
2809+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
2810+ }
2811+ },
2812+ "node_modules/react": {
2813+ "version": "19.3.0",
2814+ "resolved": "https://registry.npmjs.org/react/-/react-19.3.0.tgz",
2815+ "integrity": "sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==",
2816+ "dev": true,
2817+ "license": "MIT",
2818+ "engines": {
2819+ "node": ">=0.10.0"
2820+ }
2821+ },
2822+ "node_modules/react-dom": {
2823+ "version": "19.3.0",
2824+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.3.0.tgz",
2825+ "integrity": "sha512-JDk8dgif51OjFoDE70+OT9ICyYr+69HlmihNwp1+Nsfbna3t5sIiCa9ZJktDmQ4/1b/rn26hIAR2uYXDMr5r0Q==",
2826+ "dev": true,
2827+ "license": "MIT",
2828+ "dependencies": {
2829+ "scheduler": "^0.28.0"
2830+ },
2831+ "peerDependencies": {
2832+ "react": "^19.3.0"
2833+ }
2834+ },
2835+ "node_modules/react-is": {
2836+ "version": "17.0.2",
2837+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
2838+ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
2839+ "dev": true,
2840+ "license": "MIT"
2841+ },
2842+ "node_modules/recast": {
2843+ "version": "0.23.21",
2844+ "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.21.tgz",
2845+ "integrity": "sha512-mFAyJq9vUbSTARLZUvAEf1z3YxlvAwswbmxMx2mPA/MSm4KmpwvwvhsH/NIrZhyOuwD60Lzyw2qh83uCbgTPYw==",
2846+ "dev": true,
2847+ "license": "MIT",
2848+ "dependencies": {
2849+ "ast-types": "^0.16.1",
2850+ "esprima": "~4.0.0",
2851+ "source-map": "~0.6.1",
2852+ "tiny-invariant": "^1.3.3",
2853+ "tslib": "^2.0.1"
2854+ },
2855+ "engines": {
2856+ "node": ">= 4"
2857+ }
2858+ },
2859+ "node_modules/redent": {
2860+ "version": "3.0.0",
2861+ "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz",
2862+ "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==",
2863+ "dev": true,
2864+ "license": "MIT",
2865+ "dependencies": {
2866+ "indent-string": "^4.0.0",
2867+ "strip-indent": "^3.0.0"
2868+ },
2869+ "engines": {
2870+ "node": ">=8"
2871+ }
2872+ },
2873+ "node_modules/rollup": {
2874+ "version": "4.64.2",
2875+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.64.2.tgz",
2876+ "integrity": "sha512-RxXm0XVVV1hFI3dYe12ruaOgsYQ725lUcUWeMSQqkF9tTxVErY+fy2Ets6vWhItL9MZMSNZGbvZFc7rH/VHp2Q==",
2877+ "dev": true,
2878+ "license": "MIT",
2879+ "dependencies": {
2880+ "@types/estree": "1.0.9"
2881+ },
2882+ "bin": {
2883+ "rollup": "dist/bin/rollup"
2884+ },
2885+ "engines": {
2886+ "node": ">=18.0.0",
2887+ "npm": ">=8.0.0"
2888+ },
2889+ "optionalDependencies": {
2890+ "@napi-rs/lzma-linux-x64-gnu": "1.5.1",
2891+ "@rollup/rollup-android-arm-eabi": "4.64.2",
2892+ "@rollup/rollup-android-arm64": "4.64.2",
2893+ "@rollup/rollup-darwin-arm64": "4.64.2",
2894+ "@rollup/rollup-darwin-x64": "4.64.2",
2895+ "@rollup/rollup-freebsd-arm64": "4.64.2",
2896+ "@rollup/rollup-freebsd-x64": "4.64.2",
2897+ "@rollup/rollup-linux-arm-gnueabihf": "4.64.2",
2898+ "@rollup/rollup-linux-arm-musleabihf": "4.64.2",
2899+ "@rollup/rollup-linux-arm64-gnu": "4.64.2",
2900+ "@rollup/rollup-linux-arm64-musl": "4.64.2",
2901+ "@rollup/rollup-linux-loong64-gnu": "4.64.2",
2902+ "@rollup/rollup-linux-loong64-musl": "4.64.2",
2903+ "@rollup/rollup-linux-ppc64-gnu": "4.64.2",
2904+ "@rollup/rollup-linux-ppc64-musl": "4.64.2",
2905+ "@rollup/rollup-linux-riscv64-gnu": "4.64.2",
2906+ "@rollup/rollup-linux-riscv64-musl": "4.64.2",
2907+ "@rollup/rollup-linux-s390x-gnu": "4.64.2",
2908+ "@rollup/rollup-linux-x64-gnu": "4.64.2",
2909+ "@rollup/rollup-linux-x64-musl": "4.64.2",
2910+ "@rollup/rollup-openbsd-x64": "4.64.2",
2911+ "@rollup/rollup-openharmony-arm64": "4.64.2",
2912+ "@rollup/rollup-win32-arm64-msvc": "4.64.2",
2913+ "@rollup/rollup-win32-ia32-msvc": "4.64.2",
2914+ "@rollup/rollup-win32-x64-gnu": "4.64.2",
2915+ "@rollup/rollup-win32-x64-msvc": "4.64.2",
2916+ "fsevents": "~2.3.2"
2917+ }
2918+ },
2919+ "node_modules/run-applescript": {
2920+ "version": "7.1.0",
2921+ "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz",
2922+ "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==",
2923+ "dev": true,
2924+ "license": "MIT",
2925+ "engines": {
2926+ "node": ">=18"
2927+ },
2928+ "funding": {
2929+ "url": "https://github.com/sponsors/sindresorhus"
2930+ }
2931+ },
2932+ "node_modules/scheduler": {
2933+ "version": "0.28.0",
2934+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.28.0.tgz",
2935+ "integrity": "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==",
2936+ "dev": true,
2937+ "license": "MIT"
2938+ },
2939+ "node_modules/scule": {
2940+ "version": "1.3.0",
2941+ "resolved": "https://registry.npmjs.org/scule/-/scule-1.3.0.tgz",
2942+ "integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==",
2943+ "dev": true,
2944+ "license": "MIT"
2945+ },
2946+ "node_modules/semver": {
2947+ "version": "7.8.5",
2948+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
2949+ "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
2950+ "dev": true,
2951+ "license": "ISC",
2952+ "bin": {
2953+ "semver": "bin/semver.js"
2954+ },
2955+ "engines": {
2956+ "node": ">=10"
2957+ }
2958+ },
2959+ "node_modules/source-map": {
2960+ "version": "0.6.1",
2961+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
2962+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
2963+ "dev": true,
2964+ "license": "BSD-3-Clause",
2965+ "engines": {
2966+ "node": ">=0.10.0"
2967+ }
2968+ },
2969+ "node_modules/source-map-js": {
2970+ "version": "1.2.2",
2971+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz",
2972+ "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==",
2973+ "dev": true,
2974+ "license": "BSD-3-Clause",
2975+ "engines": {
2976+ "node": ">=0.10.0"
2977+ }
2978+ },
2979+ "node_modules/sqids": {
2980+ "version": "0.3.0",
2981+ "resolved": "https://registry.npmjs.org/sqids/-/sqids-0.3.0.tgz",
2982+ "integrity": "sha512-lOQK1ucVg+W6n3FhRwwSeUijxe93b51Bfz5PMRMihVf1iVkl82ePQG7V5vwrhzB11v0NtsR25PSZRGiSomJaJw==",
2983+ "dev": true,
2984+ "license": "MIT"
2985+ },
2986+ "node_modules/storybook": {
2987+ "version": "10.6.1",
2988+ "resolved": "https://registry.npmjs.org/storybook/-/storybook-10.6.1.tgz",
2989+ "integrity": "sha512-NeEmukMWPA5OqHizUkQtxCIhGt7MUZLPY4FFgpC0LNiipjrac/oKpH0TxI+9Zc+vI5+IoN+NiskOmBP7gtYfmA==",
2990+ "dev": true,
2991+ "license": "MIT",
2992+ "dependencies": {
2993+ "@storybook/global": "^5.0.0",
2994+ "@storybook/icons": "^2.0.2",
2995+ "@testing-library/dom": "^10.4.1",
2996+ "@testing-library/jest-dom": "6.9.1",
2997+ "@testing-library/user-event": "^14.6.3",
2998+ "@vitest/expect": "3.2.4",
2999+ "@vitest/spy": "3.2.4",
3000+ "@webcontainer/env": "^1.1.1",
3001+ "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0 || ^0.26.0 || ^0.27.0 || ^0.28.0",
3002+ "jsonc-parser": "^3.3.1",
3003+ "open": "^10.2.0",
3004+ "oxc-parser": "^0.127.0",
3005+ "oxc-resolver": "11.21.2",
3006+ "recast": "^0.23.5",
3007+ "semver": "^7.7.3",
3008+ "use-sync-external-store": "^1.5.0",
3009+ "ws": "^8.21.1"
3010+ },
3011+ "bin": {
3012+ "storybook": "dist/bin/dispatcher.js"
3013+ },
3014+ "funding": {
3015+ "type": "opencollective",
3016+ "url": "https://opencollective.com/storybook"
3017+ },
3018+ "peerDependencies": {
3019+ "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
3020+ "prettier": "^2 || ^3",
3021+ "vite-plus": "^0.1.15 || ^0.2.0"
3022+ },
3023+ "peerDependenciesMeta": {
3024+ "@types/react": {
3025+ "optional": true
3026+ },
3027+ "prettier": {
3028+ "optional": true
3029+ },
3030+ "vite-plus": {
3031+ "optional": true
3032+ }
3033+ }
3034+ },
3035+ "node_modules/strip-indent": {
3036+ "version": "3.0.0",
3037+ "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
3038+ "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==",
3039+ "dev": true,
3040+ "license": "MIT",
3041+ "dependencies": {
3042+ "min-indent": "^1.0.0"
3043+ },
3044+ "engines": {
3045+ "node": ">=8"
3046+ }
3047+ },
3048+ "node_modules/svelte": {
3049+ "version": "5.57.2",
3050+ "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.57.2.tgz",
3051+ "integrity": "sha512-1YiPCAXT0Ct6g1vOMmSLa0Nsc3QRMvtDF32ihgglGVrugIuIK38RyDiFNXVDhtQ81TJjSO57NdMw6DyYLqSGDQ==",
3052+ "license": "MIT",
3053+ "dependencies": {
3054+ "@jridgewell/remapping": "^2.3.5",
3055+ "@jridgewell/sourcemap-codec": "^1.6.0",
3056+ "@sveltejs/acorn-typescript": "^1.0.13",
3057+ "@types/estree": "^1.0.9",
3058+ "acorn": "^8.18.0",
3059+ "aria-query": "5.3.1",
3060+ "axobject-query": "^4.1.0",
3061+ "clsx": "^2.1.1",
3062+ "devalue": "^5.9.2",
3063+ "esm-env": "^1.2.1",
3064+ "esrap": "^2.3.6",
3065+ "is-reference": "^3.0.3",
3066+ "locate-character": "^3.0.0",
3067+ "magic-string": "^0.30.11",
3068+ "zimmerframe": "^1.1.2"
3069+ },
3070+ "engines": {
3071+ "node": ">=18"
3072+ }
3073+ },
3074+ "node_modules/svelte/node_modules/aria-query": {
3075+ "version": "5.3.1",
3076+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.1.tgz",
3077+ "integrity": "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==",
3078+ "license": "Apache-2.0",
3079+ "engines": {
3080+ "node": ">= 0.4"
3081+ }
3082+ },
3083+ "node_modules/svelte/node_modules/magic-string": {
3084+ "version": "0.30.21",
3085+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
3086+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
3087+ "license": "MIT",
3088+ "dependencies": {
3089+ "@jridgewell/sourcemap-codec": "^1.5.5"
3090+ }
3091+ },
3092+ "node_modules/svelte2tsx": {
3093+ "version": "0.7.61",
3094+ "resolved": "https://registry.npmjs.org/svelte2tsx/-/svelte2tsx-0.7.61.tgz",
3095+ "integrity": "sha512-EpQ/+UHITBULeUojx/LLD3uTYasZXcX1BrqlrzfLUnT9vdUhaOWK59a3ryWcFU8L0sY1SP+gRhJ2Beiis98+qw==",
3096+ "dev": true,
3097+ "license": "MIT",
3098+ "dependencies": {
3099+ "dedent-js": "^1.0.1",
3100+ "scule": "^1.3.0"
3101+ },
3102+ "peerDependencies": {
3103+ "svelte": "^3.55 || ^4.0.0-next.0 || ^4.0 || ^5.0.0-next.0",
3104+ "typescript": "^4.9.4 || ^5.0.0 || ^6.0.0"
3105+ }
3106+ },
3107+ "node_modules/tagged-tag": {
3108+ "version": "1.0.0",
3109+ "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz",
3110+ "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==",
3111+ "dev": true,
3112+ "license": "MIT",
3113+ "engines": {
3114+ "node": ">=20"
3115+ },
3116+ "funding": {
3117+ "url": "https://github.com/sponsors/sindresorhus"
3118+ }
3119+ },
3120+ "node_modules/tiny-invariant": {
3121+ "version": "1.3.3",
3122+ "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
3123+ "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
3124+ "dev": true,
3125+ "license": "MIT"
3126+ },
3127+ "node_modules/tinyrainbow": {
3128+ "version": "2.0.0",
3129+ "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz",
3130+ "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==",
3131+ "dev": true,
3132+ "license": "MIT",
3133+ "engines": {
3134+ "node": ">=14.0.0"
3135+ }
3136+ },
3137+ "node_modules/tinyspy": {
3138+ "version": "4.0.6",
3139+ "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.6.tgz",
3140+ "integrity": "sha512-u8KszXvGfU68hVcZpRHKG28T0krMuv2G5nDhiHaMLen/gIuFEgIJhaJuO69qjnXg5paSrbPMFfx3brNuN8eVSg==",
3141+ "dev": true,
3142+ "license": "MIT",
3143+ "engines": {
3144+ "node": ">=14.0.0"
3145+ }
3146+ },
3147+ "node_modules/tmcp": {
3148+ "version": "1.20.0",
3149+ "resolved": "https://registry.npmjs.org/tmcp/-/tmcp-1.20.0.tgz",
3150+ "integrity": "sha512-dcDximKQBGqLP/aEAVA26HcWRHhKipstg1w0fbDDJ0HcFZ6lolLU1YYmStYEn/73f534i7WrDNcjRfRYdV9CoA==",
3151+ "dev": true,
3152+ "license": "MIT",
3153+ "dependencies": {
3154+ "@standard-schema/spec": "^1.0.0",
3155+ "json-rpc-2.0": "^1.7.1",
3156+ "sqids": "^0.3.0",
3157+ "uri-template-matcher": "^1.1.1",
3158+ "valibot": "^1.1.0"
3159+ }
3160+ },
3161+ "node_modules/ts-dedent": {
3162+ "version": "2.3.0",
3163+ "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.3.0.tgz",
3164+ "integrity": "sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg==",
3165+ "dev": true,
3166+ "license": "MIT",
3167+ "engines": {
3168+ "node": ">=6.10"
3169+ }
3170+ },
3171+ "node_modules/tslib": {
3172+ "version": "2.8.1",
3173+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
3174+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
3175+ "dev": true,
3176+ "license": "0BSD"
3177+ },
3178+ "node_modules/type-fest": {
3179+ "version": "5.10.0",
3180+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.10.0.tgz",
3181+ "integrity": "sha512-NoSdpq/WEiAg5sjmBkmV/hfxv6HJH4NqPNrqjtSO5CwRmpsDfaf4begxW34KdJykH/l1yHtwBWQkCRdoXO8mPA==",
3182+ "dev": true,
3183+ "license": "(MIT OR CC0-1.0)",
3184+ "dependencies": {
3185+ "tagged-tag": "^1.0.0"
3186+ },
3187+ "engines": {
3188+ "node": ">=20"
3189+ },
3190+ "funding": {
3191+ "url": "https://github.com/sponsors/sindresorhus"
3192+ }
3193+ },
3194+ "node_modules/typescript": {
3195+ "version": "6.0.3",
3196+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
3197+ "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
3198+ "dev": true,
3199+ "license": "Apache-2.0",
3200+ "bin": {
3201+ "tsc": "bin/tsc",
3202+ "tsserver": "bin/tsserver"
3203+ },
3204+ "engines": {
3205+ "node": ">=14.17"
3206+ }
3207+ },
3208+ "node_modules/unplugin": {
3209+ "version": "2.3.11",
3210+ "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.11.tgz",
3211+ "integrity": "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==",
3212+ "dev": true,
3213+ "license": "MIT",
3214+ "dependencies": {
3215+ "@jridgewell/remapping": "^2.3.5",
3216+ "acorn": "^8.15.0",
3217+ "picomatch": "^4.0.3",
3218+ "webpack-virtual-modules": "^0.6.2"
3219+ },
3220+ "engines": {
3221+ "node": ">=18.12.0"
3222+ }
3223+ },
3224+ "node_modules/uri-template-matcher": {
3225+ "version": "1.1.2",
3226+ "resolved": "https://registry.npmjs.org/uri-template-matcher/-/uri-template-matcher-1.1.2.tgz",
3227+ "integrity": "sha512-uZc1h12jdO3m/R77SfTEOuo6VbMhgWznaawKpBjRGSJb7i91x5PgI37NQJtG+Cerxkk0yr1pylBY2qG1kQ+aEQ==",
3228+ "dev": true,
3229+ "license": "ISC"
3230+ },
3231+ "node_modules/use-sync-external-store": {
3232+ "version": "1.7.0",
3233+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.7.0.tgz",
3234+ "integrity": "sha512-6L+EeigHMQhdaIPNIFUKwfWJSwWFQ8gJbJ2DLOs5sDIegTwR9fRxvnM3uciHKjIZhFz+KAv2emhWMRvDmMcY8A==",
3235+ "dev": true,
3236+ "license": "MIT",
3237+ "peerDependencies": {
3238+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
3239+ }
3240+ },
3241+ "node_modules/valibot": {
3242+ "version": "1.5.0",
3243+ "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.5.0.tgz",
3244+ "integrity": "sha512-nil6AkP2TChWL43Z5uJ6GTxX01CUA+g8LWUM+N/rB9NBbkUMaUsi9PUNzlUPgoKASgmx9f7eGOYpJ04/fSa6FQ==",
3245+ "dev": true,
3246+ "license": "MIT",
3247+ "peerDependencies": {
3248+ "typescript": ">=5"
3249+ },
3250+ "peerDependenciesMeta": {
3251+ "typescript": {
3252+ "optional": true
3253+ }
3254+ }
3255+ },
3256+ "node_modules/vite": {
3257+ "version": "5.4.21",
3258+ "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
3259+ "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
3260+ "dev": true,
3261+ "license": "MIT",
3262+ "dependencies": {
3263+ "esbuild": "^0.21.3",
3264+ "postcss": "^8.4.43",
3265+ "rollup": "^4.20.0"
3266+ },
3267+ "bin": {
3268+ "vite": "bin/vite.js"
3269+ },
3270+ "engines": {
3271+ "node": "^18.0.0 || >=20.0.0"
3272+ },
3273+ "funding": {
3274+ "url": "https://github.com/vitejs/vite?sponsor=1"
3275+ },
3276+ "optionalDependencies": {
3277+ "fsevents": "~2.3.3"
3278+ },
3279+ "peerDependencies": {
3280+ "@types/node": "^18.0.0 || >=20.0.0",
3281+ "less": "*",
3282+ "lightningcss": "^1.21.0",
3283+ "sass": "*",
3284+ "sass-embedded": "*",
3285+ "stylus": "*",
3286+ "sugarss": "*",
3287+ "terser": "^5.4.0"
3288+ },
3289+ "peerDependenciesMeta": {
3290+ "@types/node": {
3291+ "optional": true
3292+ },
3293+ "less": {
3294+ "optional": true
3295+ },
3296+ "lightningcss": {
3297+ "optional": true
3298+ },
3299+ "sass": {
3300+ "optional": true
3301+ },
3302+ "sass-embedded": {
3303+ "optional": true
3304+ },
3305+ "stylus": {
3306+ "optional": true
3307+ },
3308+ "sugarss": {
3309+ "optional": true
3310+ },
3311+ "terser": {
3312+ "optional": true
3313+ }
3314+ }
3315+ },
3316+ "node_modules/vite/node_modules/@esbuild/aix-ppc64": {
3317+ "version": "0.21.5",
3318+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
3319+ "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
3320+ "cpu": [
3321+ "ppc64"
3322+ ],
3323+ "dev": true,
3324+ "license": "MIT",
3325+ "optional": true,
3326+ "os": [
3327+ "aix"
3328+ ],
3329+ "engines": {
3330+ "node": ">=12"
3331+ }
3332+ },
3333+ "node_modules/vite/node_modules/@esbuild/android-arm": {
3334+ "version": "0.21.5",
3335+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
3336+ "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
3337+ "cpu": [
3338+ "arm"
3339+ ],
3340+ "dev": true,
3341+ "license": "MIT",
3342+ "optional": true,
3343+ "os": [
3344+ "android"
3345+ ],
3346+ "engines": {
3347+ "node": ">=12"
3348+ }
3349+ },
3350+ "node_modules/vite/node_modules/@esbuild/android-arm64": {
3351+ "version": "0.21.5",
3352+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
3353+ "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
3354+ "cpu": [
3355+ "arm64"
3356+ ],
3357+ "dev": true,
3358+ "license": "MIT",
3359+ "optional": true,
3360+ "os": [
3361+ "android"
3362+ ],
3363+ "engines": {
3364+ "node": ">=12"
3365+ }
3366+ },
3367+ "node_modules/vite/node_modules/@esbuild/android-x64": {
3368+ "version": "0.21.5",
3369+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
3370+ "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
3371+ "cpu": [
3372+ "x64"
3373+ ],
3374+ "dev": true,
3375+ "license": "MIT",
3376+ "optional": true,
3377+ "os": [
3378+ "android"
3379+ ],
3380+ "engines": {
3381+ "node": ">=12"
3382+ }
3383+ },
3384+ "node_modules/vite/node_modules/@esbuild/darwin-arm64": {
3385+ "version": "0.21.5",
3386+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
3387+ "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
3388+ "cpu": [
3389+ "arm64"
3390+ ],
3391+ "dev": true,
3392+ "license": "MIT",
3393+ "optional": true,
3394+ "os": [
3395+ "darwin"
3396+ ],
3397+ "engines": {
3398+ "node": ">=12"
3399+ }
3400+ },
3401+ "node_modules/vite/node_modules/@esbuild/darwin-x64": {
3402+ "version": "0.21.5",
3403+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
3404+ "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
3405+ "cpu": [
3406+ "x64"
3407+ ],
3408+ "dev": true,
3409+ "license": "MIT",
3410+ "optional": true,
3411+ "os": [
3412+ "darwin"
3413+ ],
3414+ "engines": {
3415+ "node": ">=12"
3416+ }
3417+ },
3418+ "node_modules/vite/node_modules/@esbuild/freebsd-arm64": {
3419+ "version": "0.21.5",
3420+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
3421+ "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
3422+ "cpu": [
3423+ "arm64"
3424+ ],
3425+ "dev": true,
3426+ "license": "MIT",
3427+ "optional": true,
3428+ "os": [
3429+ "freebsd"
3430+ ],
3431+ "engines": {
3432+ "node": ">=12"
3433+ }
3434+ },
3435+ "node_modules/vite/node_modules/@esbuild/freebsd-x64": {
3436+ "version": "0.21.5",
3437+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
3438+ "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
3439+ "cpu": [
3440+ "x64"
3441+ ],
3442+ "dev": true,
3443+ "license": "MIT",
3444+ "optional": true,
3445+ "os": [
3446+ "freebsd"
3447+ ],
3448+ "engines": {
3449+ "node": ">=12"
3450+ }
3451+ },
3452+ "node_modules/vite/node_modules/@esbuild/linux-arm": {
3453+ "version": "0.21.5",
3454+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
3455+ "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
3456+ "cpu": [
3457+ "arm"
3458+ ],
3459+ "dev": true,
3460+ "license": "MIT",
3461+ "optional": true,
3462+ "os": [
3463+ "linux"
3464+ ],
3465+ "engines": {
3466+ "node": ">=12"
3467+ }
3468+ },
3469+ "node_modules/vite/node_modules/@esbuild/linux-arm64": {
3470+ "version": "0.21.5",
3471+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
3472+ "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
3473+ "cpu": [
3474+ "arm64"
3475+ ],
3476+ "dev": true,
3477+ "license": "MIT",
3478+ "optional": true,
3479+ "os": [
3480+ "linux"
3481+ ],
3482+ "engines": {
3483+ "node": ">=12"
3484+ }
3485+ },
3486+ "node_modules/vite/node_modules/@esbuild/linux-ia32": {
3487+ "version": "0.21.5",
3488+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
3489+ "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
3490+ "cpu": [
3491+ "ia32"
3492+ ],
3493+ "dev": true,
3494+ "license": "MIT",
3495+ "optional": true,
3496+ "os": [
3497+ "linux"
3498+ ],
3499+ "engines": {
3500+ "node": ">=12"
3501+ }
3502+ },
3503+ "node_modules/vite/node_modules/@esbuild/linux-loong64": {
3504+ "version": "0.21.5",
3505+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
3506+ "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
3507+ "cpu": [
3508+ "loong64"
3509+ ],
3510+ "dev": true,
3511+ "license": "MIT",
3512+ "optional": true,
3513+ "os": [
3514+ "linux"
3515+ ],
3516+ "engines": {
3517+ "node": ">=12"
3518+ }
3519+ },
3520+ "node_modules/vite/node_modules/@esbuild/linux-mips64el": {
3521+ "version": "0.21.5",
3522+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
3523+ "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
3524+ "cpu": [
3525+ "mips64el"
3526+ ],
3527+ "dev": true,
3528+ "license": "MIT",
3529+ "optional": true,
3530+ "os": [
3531+ "linux"
3532+ ],
3533+ "engines": {
3534+ "node": ">=12"
3535+ }
3536+ },
3537+ "node_modules/vite/node_modules/@esbuild/linux-ppc64": {
3538+ "version": "0.21.5",
3539+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
3540+ "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
3541+ "cpu": [
3542+ "ppc64"
3543+ ],
3544+ "dev": true,
3545+ "license": "MIT",
3546+ "optional": true,
3547+ "os": [
3548+ "linux"
3549+ ],
3550+ "engines": {
3551+ "node": ">=12"
3552+ }
3553+ },
3554+ "node_modules/vite/node_modules/@esbuild/linux-riscv64": {
3555+ "version": "0.21.5",
3556+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
3557+ "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
3558+ "cpu": [
3559+ "riscv64"
3560+ ],
3561+ "dev": true,
3562+ "license": "MIT",
3563+ "optional": true,
3564+ "os": [
3565+ "linux"
3566+ ],
3567+ "engines": {
3568+ "node": ">=12"
3569+ }
3570+ },
3571+ "node_modules/vite/node_modules/@esbuild/linux-s390x": {
3572+ "version": "0.21.5",
3573+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
3574+ "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
3575+ "cpu": [
3576+ "s390x"
3577+ ],
3578+ "dev": true,
3579+ "license": "MIT",
3580+ "optional": true,
3581+ "os": [
3582+ "linux"
3583+ ],
3584+ "engines": {
3585+ "node": ">=12"
3586+ }
3587+ },
3588+ "node_modules/vite/node_modules/@esbuild/linux-x64": {
3589+ "version": "0.21.5",
3590+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
3591+ "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
3592+ "cpu": [
3593+ "x64"
3594+ ],
3595+ "dev": true,
3596+ "license": "MIT",
3597+ "optional": true,
3598+ "os": [
3599+ "linux"
3600+ ],
3601+ "engines": {
3602+ "node": ">=12"
3603+ }
3604+ },
3605+ "node_modules/vite/node_modules/@esbuild/netbsd-x64": {
3606+ "version": "0.21.5",
3607+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
3608+ "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
3609+ "cpu": [
3610+ "x64"
3611+ ],
3612+ "dev": true,
3613+ "license": "MIT",
3614+ "optional": true,
3615+ "os": [
3616+ "netbsd"
3617+ ],
3618+ "engines": {
3619+ "node": ">=12"
3620+ }
3621+ },
3622+ "node_modules/vite/node_modules/@esbuild/openbsd-x64": {
3623+ "version": "0.21.5",
3624+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
3625+ "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
3626+ "cpu": [
3627+ "x64"
3628+ ],
3629+ "dev": true,
3630+ "license": "MIT",
3631+ "optional": true,
3632+ "os": [
3633+ "openbsd"
3634+ ],
3635+ "engines": {
3636+ "node": ">=12"
3637+ }
3638+ },
3639+ "node_modules/vite/node_modules/@esbuild/sunos-x64": {
3640+ "version": "0.21.5",
3641+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
3642+ "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
3643+ "cpu": [
3644+ "x64"
3645+ ],
3646+ "dev": true,
3647+ "license": "MIT",
3648+ "optional": true,
3649+ "os": [
3650+ "sunos"
3651+ ],
3652+ "engines": {
3653+ "node": ">=12"
3654+ }
3655+ },
3656+ "node_modules/vite/node_modules/@esbuild/win32-arm64": {
3657+ "version": "0.21.5",
3658+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
3659+ "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
3660+ "cpu": [
3661+ "arm64"
3662+ ],
3663+ "dev": true,
3664+ "license": "MIT",
3665+ "optional": true,
3666+ "os": [
3667+ "win32"
3668+ ],
3669+ "engines": {
3670+ "node": ">=12"
3671+ }
3672+ },
3673+ "node_modules/vite/node_modules/@esbuild/win32-ia32": {
3674+ "version": "0.21.5",
3675+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
3676+ "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
3677+ "cpu": [
3678+ "ia32"
3679+ ],
3680+ "dev": true,
3681+ "license": "MIT",
3682+ "optional": true,
3683+ "os": [
3684+ "win32"
3685+ ],
3686+ "engines": {
3687+ "node": ">=12"
9443688 }
9453689 },
946- "node_modules/deepmerge": {
947- "version": "4.3.1",
948- "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
949- "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
3690+ "node_modules/vite/node_modules/@esbuild/win32-x64": {
3691+ "version": "0.21.5",
3692+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
3693+ "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
3694+ "cpu": [
3695+ "x64"
3696+ ],
9503697 "dev": true,
9513698 "license": "MIT",
3699+ "optional": true,
3700+ "os": [
3701+ "win32"
3702+ ],
9523703 "engines": {
953- "node": ">=0.10.0"
3704+ "node": ">=12"
9543705 }
9553706 },
956- "node_modules/esbuild": {
3707+ "node_modules/vite/node_modules/esbuild": {
9573708 "version": "0.21.5",
9583709 "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
9593710 "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
@@ -992,324 +3743,76 @@
9923743 "@esbuild/win32-x64": "0.21.5"
9933744 }
9943745 },
995- "node_modules/estree-walker": {
996- "version": "3.0.3",
997- "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
998- "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
999- "license": "MIT",
1000- "dependencies": {
1001- "@types/estree": "^1.0.0"
1002- }
1003- },
1004- "node_modules/fsevents": {
1005- "version": "2.3.3",
1006- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
1007- "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
1008- "dev": true,
1009- "hasInstallScript": true,
1010- "license": "MIT",
1011- "optional": true,
1012- "os": [
1013- "darwin"
1014- ],
1015- "engines": {
1016- "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
1017- }
1018- },
1019- "node_modules/is-reference": {
1020- "version": "3.0.3",
1021- "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz",
1022- "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==",
1023- "license": "MIT",
1024- "dependencies": {
1025- "@types/estree": "^1.0.6"
1026- }
1027- },
1028- "node_modules/kleur": {
1029- "version": "4.1.5",
1030- "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
1031- "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==",
3746+ "node_modules/vitefu": {
3747+ "version": "1.1.3",
3748+ "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz",
3749+ "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==",
10323750 "dev": true,
10333751 "license": "MIT",
1034- "engines": {
1035- "node": ">=6"
1036- }
1037- },
1038- "node_modules/locate-character": {
1039- "version": "3.0.0",
1040- "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz",
1041- "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==",
1042- "license": "MIT"
1043- },
1044- "node_modules/magic-string": {
1045- "version": "0.30.21",
1046- "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
1047- "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
1048- "license": "MIT",
1049- "dependencies": {
1050- "@jridgewell/sourcemap-codec": "^1.5.5"
1051- }
1052- },
1053- "node_modules/mdn-data": {
1054- "version": "2.0.30",
1055- "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
1056- "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
1057- "license": "CC0-1.0"
1058- },
1059- "node_modules/ms": {
1060- "version": "2.1.3",
1061- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1062- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
1063- "dev": true,
1064- "license": "MIT"
1065- },
1066- "node_modules/nanoid": {
1067- "version": "3.3.19",
1068- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz",
1069- "integrity": "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==",
1070- "dev": true,
1071- "funding": [
1072- {
1073- "type": "github",
1074- "url": "https://github.com/sponsors/ai"
1075- }
3752+ "workspaces": [
3753+ "tests/deps/*",
3754+ "tests/projects/*",
3755+ "tests/projects/workspace/packages/*"
10763756 ],
1077- "license": "MIT",
1078- "bin": {
1079- "nanoid": "bin/nanoid.cjs"
3757+ "peerDependencies": {
3758+ "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
10803759 },
1081- "engines": {
1082- "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
1083- }
1084- },
1085- "node_modules/periscopic": {
1086- "version": "3.1.0",
1087- "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz",
1088- "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==",
1089- "license": "MIT",
1090- "dependencies": {
1091- "@types/estree": "^1.0.0",
1092- "estree-walker": "^3.0.0",
1093- "is-reference": "^3.0.0"
1094- }
1095- },
1096- "node_modules/picocolors": {
1097- "version": "1.1.1",
1098- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
1099- "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
1100- "dev": true,
1101- "license": "ISC"
1102- },
1103- "node_modules/postcss": {
1104- "version": "8.5.28",
1105- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz",
1106- "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==",
1107- "dev": true,
1108- "funding": [
1109- {
1110- "type": "opencollective",
1111- "url": "https://opencollective.com/postcss/"
1112- },
1113- {
1114- "type": "tidelift",
1115- "url": "https://tidelift.com/funding/github/npm/postcss"
1116- },
1117- {
1118- "type": "github",
1119- "url": "https://github.com/sponsors/ai"
3760+ "peerDependenciesMeta": {
3761+ "vite": {
3762+ "optional": true
11203763 }
1121- ],
1122- "license": "MIT",
1123- "dependencies": {
1124- "nanoid": "^3.3.18",
1125- "picocolors": "^1.1.1",
1126- "source-map-js": "^1.2.1"
1127- },
1128- "engines": {
1129- "node": "^10 || ^12 || >=14"
1130- }
1131- },
1132- "node_modules/rollup": {
1133- "version": "4.63.4",
1134- "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.63.4.tgz",
1135- "integrity": "sha512-4U0liVayNIoLp3GFl1FcI8561WepLnZ1rqfraGh7S9B3Ur5F9S283y8Futii7RUU2C/97tOBmBy7nYvhoiOpbQ==",
1136- "dev": true,
1137- "license": "MIT",
1138- "dependencies": {
1139- "@types/estree": "1.0.9"
1140- },
1141- "bin": {
1142- "rollup": "dist/bin/rollup"
1143- },
1144- "engines": {
1145- "node": ">=18.0.0",
1146- "npm": ">=8.0.0"
1147- },
1148- "optionalDependencies": {
1149- "@napi-rs/lzma-linux-x64-gnu": "1.5.1",
1150- "@rollup/rollup-android-arm-eabi": "4.63.4",
1151- "@rollup/rollup-android-arm64": "4.63.4",
1152- "@rollup/rollup-darwin-arm64": "4.63.4",
1153- "@rollup/rollup-darwin-x64": "4.63.4",
1154- "@rollup/rollup-freebsd-arm64": "4.63.4",
1155- "@rollup/rollup-freebsd-x64": "4.63.4",
1156- "@rollup/rollup-linux-arm-gnueabihf": "4.63.4",
1157- "@rollup/rollup-linux-arm-musleabihf": "4.63.4",
1158- "@rollup/rollup-linux-arm64-gnu": "4.63.4",
1159- "@rollup/rollup-linux-arm64-musl": "4.63.4",
1160- "@rollup/rollup-linux-loong64-gnu": "4.63.4",
1161- "@rollup/rollup-linux-loong64-musl": "4.63.4",
1162- "@rollup/rollup-linux-ppc64-gnu": "4.63.4",
1163- "@rollup/rollup-linux-ppc64-musl": "4.63.4",
1164- "@rollup/rollup-linux-riscv64-gnu": "4.63.4",
1165- "@rollup/rollup-linux-riscv64-musl": "4.63.4",
1166- "@rollup/rollup-linux-s390x-gnu": "4.63.4",
1167- "@rollup/rollup-linux-x64-gnu": "4.63.4",
1168- "@rollup/rollup-linux-x64-musl": "4.63.4",
1169- "@rollup/rollup-openbsd-x64": "4.63.4",
1170- "@rollup/rollup-openharmony-arm64": "4.63.4",
1171- "@rollup/rollup-win32-arm64-msvc": "4.63.4",
1172- "@rollup/rollup-win32-ia32-msvc": "4.63.4",
1173- "@rollup/rollup-win32-x64-gnu": "4.63.4",
1174- "@rollup/rollup-win32-x64-msvc": "4.63.4",
1175- "fsevents": "~2.3.2"
1176- }
1177- },
1178- "node_modules/source-map-js": {
1179- "version": "1.2.1",
1180- "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
1181- "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
1182- "license": "BSD-3-Clause",
1183- "engines": {
1184- "node": ">=0.10.0"
1185- }
1186- },
1187- "node_modules/svelte": {
1188- "version": "4.2.20",
1189- "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.20.tgz",
1190- "integrity": "sha512-eeEgGc2DtiUil5ANdtd8vPwt9AgaMdnuUFnPft9F5oMvU/FHu5IHFic+p1dR/UOB7XU2mX2yHW+NcTch4DCh5Q==",
1191- "license": "MIT",
1192- "dependencies": {
1193- "@ampproject/remapping": "^2.2.1",
1194- "@jridgewell/sourcemap-codec": "^1.4.15",
1195- "@jridgewell/trace-mapping": "^0.3.18",
1196- "@types/estree": "^1.0.1",
1197- "acorn": "^8.9.0",
1198- "aria-query": "^5.3.0",
1199- "axobject-query": "^4.0.0",
1200- "code-red": "^1.0.3",
1201- "css-tree": "^2.3.1",
1202- "estree-walker": "^3.0.3",
1203- "is-reference": "^3.0.1",
1204- "locate-character": "^3.0.0",
1205- "magic-string": "^0.30.4",
1206- "periscopic": "^3.1.0"
1207- },
1208- "engines": {
1209- "node": ">=16"
1210- }
1211- },
1212- "node_modules/svelte-hmr": {
1213- "version": "0.16.0",
1214- "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.16.0.tgz",
1215- "integrity": "sha512-Gyc7cOS3VJzLlfj7wKS0ZnzDVdv3Pn2IuVeJPk9m2skfhcu5bq3wtIZyQGggr7/Iim5rH5cncyQft/kRLupcnA==",
1216- "dev": true,
1217- "license": "ISC",
1218- "engines": {
1219- "node": "^12.20 || ^14.13.1 || >= 16"
1220- },
1221- "peerDependencies": {
1222- "svelte": "^3.19.0 || ^4.0.0"
12233764 }
12243765 },
1225- "node_modules/typescript": {
1226- "version": "5.6.3",
1227- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz",
1228- "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==",
3766+ "node_modules/webpack-virtual-modules": {
3767+ "version": "0.6.2",
3768+ "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",
3769+ "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==",
12293770 "dev": true,
1230- "license": "Apache-2.0",
1231- "bin": {
1232- "tsc": "bin/tsc",
1233- "tsserver": "bin/tsserver"
1234- },
1235- "engines": {
1236- "node": ">=14.17"
1237- }
3771+ "license": "MIT"
12383772 },
1239- "node_modules/vite": {
1240- "version": "5.4.21",
1241- "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
1242- "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
3773+ "node_modules/ws": {
3774+ "version": "8.22.0",
3775+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.22.0.tgz",
3776+ "integrity": "sha512-Ydggc987+RO0AnWtZ/7Wq9FtNvcrL1b/RO0ud9mWjUPgDrsAAwQSF51sm2hm1XofbU/4jkpGEsLFsZZxU+1DOg==",
12433777 "dev": true,
12443778 "license": "MIT",
1245- "dependencies": {
1246- "esbuild": "^0.21.3",
1247- "postcss": "^8.4.43",
1248- "rollup": "^4.20.0"
1249- },
1250- "bin": {
1251- "vite": "bin/vite.js"
1252- },
12533779 "engines": {
1254- "node": "^18.0.0 || >=20.0.0"
1255- },
1256- "funding": {
1257- "url": "https://github.com/vitejs/vite?sponsor=1"
1258- },
1259- "optionalDependencies": {
1260- "fsevents": "~2.3.3"
3780+ "node": ">=10.0.0"
12613781 },
12623782 "peerDependencies": {
1263- "@types/node": "^18.0.0 || >=20.0.0",
1264- "less": "*",
1265- "lightningcss": "^1.21.0",
1266- "sass": "*",
1267- "sass-embedded": "*",
1268- "stylus": "*",
1269- "sugarss": "*",
1270- "terser": "^5.4.0"
3783+ "bufferutil": "^4.0.1",
3784+ "utf-8-validate": ">=5.0.2"
12713785 },
12723786 "peerDependenciesMeta": {
1273- "@types/node": {
3787+ "bufferutil": {
12743788 "optional": true
12753789 },
1276- "less": {
1277- "optional": true
1278- },
1279- "lightningcss": {
1280- "optional": true
1281- },
1282- "sass": {
1283- "optional": true
1284- },
1285- "sass-embedded": {
1286- "optional": true
1287- },
1288- "stylus": {
1289- "optional": true
1290- },
1291- "sugarss": {
1292- "optional": true
1293- },
1294- "terser": {
3790+ "utf-8-validate": {
12953791 "optional": true
12963792 }
12973793 }
12983794 },
1299- "node_modules/vitefu": {
1300- "version": "0.2.5",
1301- "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz",
1302- "integrity": "sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==",
3795+ "node_modules/wsl-utils": {
3796+ "version": "0.1.0",
3797+ "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz",
3798+ "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==",
13033799 "dev": true,
13043800 "license": "MIT",
1305- "peerDependencies": {
1306- "vite": "^3.0.0 || ^4.0.0 || ^5.0.0"
3801+ "dependencies": {
3802+ "is-wsl": "^3.1.0"
13073803 },
1308- "peerDependenciesMeta": {
1309- "vite": {
1310- "optional": true
1311- }
3804+ "engines": {
3805+ "node": ">=18"
3806+ },
3807+ "funding": {
3808+ "url": "https://github.com/sponsors/sindresorhus"
13123809 }
3810+ },
3811+ "node_modules/zimmerframe": {
3812+ "version": "1.1.5",
3813+ "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.5.tgz",
3814+ "integrity": "sha512-msJxIvYDYcoNL+PJsu+7qmpDWsYmAxTY+2TNYXXF0hzBzBk0BMecOqDOG/EckUoKCuKwObfbugIl8QpqHDXeFA==",
3815+ "license": "MIT"
13133816 }
13143817 }
13153818 }
Modified file: web/package.json+11 additions−4 deletions
@@ -8,14 +8,21 @@
88 "build": "vite build",
99 "build:device": "vite build --mode device",
1010 "preview": "vite preview --host 127.0.0.1 --port 4173",
11- "test": "rm -rf .test-dist && tsc -p tsconfig.test.json && node --test .test-dist/test/media.test.js"
11+ "check": "tsc --noEmit -p tsconfig.json",
12+ "test": "node --experimental-transform-types --test 'test/**/*.test.ts'",
13+ "storybook": "storybook dev -p 6006 --no-open",
14+ "build-storybook": "storybook build && chmod -R u+w storybook-static"
1215 },
1316 "dependencies": {
14- "svelte": "^4.2.19"
17+ "svelte": "^5.39.0"
1518 },
1619 "devDependencies": {
17- "@sveltejs/vite-plugin-svelte": "^3.1.2",
18- "typescript": "~5.6.3",
20+ "@sveltejs/vite-plugin-svelte": "^4.0.4",
21+ "@storybook/addon-docs": "^10.6.1",
22+ "@storybook/addon-mcp": "^10.6.1",
23+ "@storybook/svelte-vite": "^10.6.1",
24+ "storybook": "^10.6.1",
25+ "typescript": "~6.0.3",
1926 "vite": "^5.4.11"
2027 }
2128 }
Added file: web/src/lib/LoginPanel.stories.ts+16 additions
@@ -0,0 +1,16 @@
1+import type { Meta, StoryObj } from '@storybook/svelte-vite';
2+import LoginPanel from './LoginPanel.svelte';
3+
4+const meta = {
5+ title: 'Panels/LoginPanel',
6+ component: LoginPanel,
7+ args: { softLogout: null }
8+} satisfies Meta<typeof LoginPanel>;
9+
10+export default meta;
11+type Story = StoryObj<typeof meta>;
12+
13+export const Fresh: Story = {};
14+export const SoftLogout: Story = {
15+ args: { softLogout: { user_id: '@alice:example.org', homeserver: 'https://matrix.example.org' } }
16+};
Added file: web/src/lib/MessageRow.stories.ts+77 additions
@@ -0,0 +1,77 @@
1+import type { Meta, StoryObj } from '@storybook/svelte-vite';
2+import { fn } from 'storybook/test';
3+import MessageRow from './MessageRow.svelte';
4+import type { TimelineContent, TimelineEvent } from './types';
5+
6+const alice = { user_id: '@alice:example.org', display_name: 'Alice', avatar: null, name_ambiguous: false };
7+
8+function event(content: TimelineContent, overrides: Partial<TimelineEvent> = {}): TimelineEvent {
9+ return {
10+ item_id: 'item-1',
11+ event_id: '$event1',
12+ transaction_id: null,
13+ sender: alice,
14+ timestamp_ms: Date.UTC(2026, 9, 8, 9, 41),
15+ own: false,
16+ send_state: { kind: 'Sent' },
17+ content,
18+ reply_to: null,
19+ thread_root: null,
20+ thread_summary: null,
21+ reactions: [],
22+ read_by: [],
23+ editable: false,
24+ ...overrides
25+ };
26+}
27+
28+function text(body: string): TimelineContent {
29+ return {
30+ kind: 'Message',
31+ message: { kind: 'Text', body, geo_uri: null, formatted_body: null, edited: false, media: null }
32+ };
33+}
34+
35+const meta = {
36+ title: 'Timeline/MessageRow',
37+ component: MessageRow,
38+ args: {
39+ itemId: 'item-1',
40+ event: event(text('Curiouser and curiouser!')),
41+ showSender: true,
42+ reacting: false,
43+ onReact: fn()
44+ }
45+} satisfies Meta<typeof MessageRow>;
46+
47+export default meta;
48+type Story = StoryObj<typeof meta>;
49+
50+export const Text: Story = {};
51+export const Continuation: Story = { args: { showSender: false } };
52+export const WithLink: Story = { args: { event: event(text('Notes are at https://example.org/rabbit-hole')) } };
53+export const Reactions: Story = {
54+ args: {
55+ event: event(text('Tea party at six'), {
56+ reactions: [
57+ { key: '🎉', count: 3, own: true, senders: [] },
58+ { key: '😮', count: 1, own: false, senders: [] }
59+ ]
60+ })
61+ }
62+};
63+export const OwnSending: Story = {
64+ args: { event: event(text('On my way'), { own: true, send_state: { kind: 'Sending' } }) }
65+};
66+export const OwnFailed: Story = {
67+ args: { event: event(text('On my way'), { own: true, send_state: { kind: 'Failed', will_retry: false } }) }
68+};
69+export const UnableToDecrypt: Story = {
70+ args: { event: event({ kind: 'UnableToDecrypt', cause: 'Unknown' }) }
71+};
72+export const Redacted: Story = { args: { event: event({ kind: 'Redacted' }) } };
73+export const Membership: Story = {
74+ args: {
75+ event: event({ kind: 'Membership', user_id: '@hatter:example.org', display_name: 'Hatter', change: 'Joined' })
76+ }
77+};
Added file: web/src/lib/MxcAvatar.stories.ts+16 additions
@@ -0,0 +1,16 @@
1+import type { Meta, StoryObj } from '@storybook/svelte-vite';
2+import MxcAvatar from './MxcAvatar.svelte';
3+
4+const meta = {
5+ title: 'Avatar/MxcAvatar',
6+ component: MxcAvatar,
7+ args: { mxc: null, label: 'Alice Liddell', size: 34, shape: 'circle' },
8+ argTypes: { shape: { control: 'inline-radio', options: ['circle', 'rounded'] } }
9+} satisfies Meta<typeof MxcAvatar>;
10+
11+export default meta;
12+type Story = StoryObj<typeof meta>;
13+
14+export const Initials: Story = {};
15+export const Rounded: Story = { args: { shape: 'rounded', size: 48 } };
16+export const BrokenMxc: Story = { args: { mxc: 'mxc://example.org/missing' } };
Added file: web/src/lib/SecurityPanel.stories.ts+39 additions
@@ -0,0 +1,39 @@
1+import type { Meta, StoryObj } from '@storybook/svelte-vite';
2+import { fn } from 'storybook/test';
3+import SecurityPanel from './SecurityPanel.svelte';
4+
5+const unverified = { device_id: 'ABCDEFGHIJ', device_verified: false, recovery: 'Disabled', key_backup: 'Disabled' } as const;
6+
7+const meta = {
8+ title: 'Panels/SecurityPanel',
9+ component: SecurityPanel,
10+ args: { encryptionState: unverified, verificationState: null, onAction: fn() }
11+} satisfies Meta<typeof SecurityPanel>;
12+
13+export default meta;
14+type Story = StoryObj<typeof meta>;
15+
16+export const Unverified: Story = {};
17+export const Verified: Story = {
18+ args: { encryptionState: { ...unverified, device_verified: true, recovery: 'Enabled', key_backup: 'Enabled' } }
19+};
20+export const Loading: Story = { args: { encryptionState: null } };
21+export const Waiting: Story = { args: { verificationState: { kind: 'Waiting' } } };
22+export const Comparison: Story = {
23+ args: {
24+ verificationState: {
25+ kind: 'Comparison',
26+ emojis: [
27+ { emoji: '🐶', name: 'Dog' },
28+ { emoji: '🐱', name: 'Cat' },
29+ { emoji: '🦁', name: 'Lion' },
30+ { emoji: '🐎', name: 'Horse' },
31+ { emoji: '🦄', name: 'Unicorn' },
32+ { emoji: '🐷', name: 'Pig' },
33+ { emoji: '🐘', name: 'Elephant' }
34+ ],
35+ decimals: [1234, 5678, 9012]
36+ }
37+ }
38+};
39+export const Cancelled: Story = { args: { verificationState: { kind: 'Cancelled', reason: 'User cancelled' } } };
Modified file: web/src/lib/media-decrypt.ts+7 additions−7 deletions
@@ -6,7 +6,7 @@
66
77 const BLOCK_SIZE = 16;
88
9-function decodeBase64(value: string): Uint8Array {
9+function decodeBase64(value: string): Uint8Array<ArrayBuffer> {
1010 const normalised = value.replace(/-/g, '+').replace(/_/g, '/');
1111 const padded = normalised + '='.repeat((4 - (normalised.length % 4)) % 4);
1212 const binary = atob(padded);
@@ -19,7 +19,7 @@ export async function importAttachmentKey(k: string): Promise<CryptoKey> {
1919 return crypto.subtle.importKey('raw', decodeBase64(k), { name: 'AES-CTR' }, false, ['decrypt']);
2020 }
2121
22-export function decodeAttachmentIv(iv: string): Uint8Array {
22+export function decodeAttachmentIv(iv: string): Uint8Array<ArrayBuffer> {
2323 const bytes = decodeBase64(iv);
2424 if (bytes.length >= BLOCK_SIZE) return bytes.subarray(0, BLOCK_SIZE);
2525 const padded = new Uint8Array(BLOCK_SIZE);
@@ -28,7 +28,7 @@ export function decodeAttachmentIv(iv: string): Uint8Array {
2828 }
2929
3030 /** iv + blocks, added as a big-endian 64-bit value into the low 8 bytes only, per the Matrix attachment convention. */
31-export function addBlockOffset(iv: Uint8Array, blocks: number): Uint8Array {
31+export function addBlockOffset(iv: Uint8Array, blocks: number): Uint8Array<ArrayBuffer> {
3232 const counter = iv.slice();
3333 const view = new DataView(counter.buffer, counter.byteOffset, counter.byteLength);
3434 const low = view.getBigUint64(8);
@@ -36,7 +36,7 @@ export function addBlockOffset(iv: Uint8Array, blocks: number): Uint8Array {
3636 return counter;
3737 }
3838
39-function concat(a: Uint8Array, b: Uint8Array): Uint8Array {
39+function concat(a: Uint8Array<ArrayBuffer>, b: Uint8Array<ArrayBuffer>): Uint8Array<ArrayBuffer> {
4040 if (a.length === 0) return b;
4141 if (b.length === 0) return a;
4242 const out = new Uint8Array(a.length + b.length);
@@ -51,7 +51,7 @@ function concat(a: Uint8Array, b: Uint8Array): Uint8Array {
5151 * the source is exhausted to flush the trailing partial block.
5252 */
5353 export class RangeDecryptor {
54- private buffered = new Uint8Array(0);
54+ private buffered: Uint8Array<ArrayBuffer> = new Uint8Array(0);
5555 private blocksConsumed = 0;
5656
5757 constructor(
@@ -60,7 +60,7 @@ export class RangeDecryptor {
6060 private readonly startBlock: number
6161 ) {}
6262
63- async push(chunk: Uint8Array): Promise<Uint8Array> {
63+ async push(chunk: Uint8Array<ArrayBuffer>): Promise<Uint8Array> {
6464 const data = concat(this.buffered, chunk);
6565 const wholeLen = data.length - (data.length % BLOCK_SIZE);
6666 if (wholeLen === 0) {
@@ -78,7 +78,7 @@ export class RangeDecryptor {
7878 return plain;
7979 }
8080
81- private async decrypt(ciphertext: Uint8Array): Promise<Uint8Array> {
81+ private async decrypt(ciphertext: Uint8Array<ArrayBuffer>): Promise<Uint8Array> {
8282 const counter = addBlockOffset(this.iv, this.startBlock + this.blocksConsumed);
8383 this.blocksConsumed += Math.ceil(ciphertext.length / BLOCK_SIZE);
8484 const plain = await crypto.subtle.decrypt({ name: 'AES-CTR', counter, length: 64 }, this.key, ciphertext);
Modified file: web/src/main.ts+2 additions−1 deletions
@@ -1,8 +1,9 @@
11 import './app.css';
2+import { mount } from 'svelte';
23 import App from './App.svelte';
34
45 const target = document.getElementById('app');
56 if (!target) throw new Error('#app element missing');
6-const app = new App({ target });
7+const app = mount(App, { target });
78
89 export default app;
Modified file: web/src/sw.ts+9 additions−6 deletions
@@ -29,7 +29,7 @@ sw.addEventListener('activate', (event) => {
2929 });
3030
3131 interface MediaReply {
32- bytes?: Uint8Array;
32+ bytes?: Uint8Array<ArrayBuffer>;
3333 content_type?: string | null;
3434 error?: { kind: string; message: string };
3535 }
@@ -122,13 +122,13 @@ async function locate(mediaId: string, size: string, force: boolean): Promise<Lo
122122 }
123123
124124 interface RangeSlice {
125- body: Uint8Array;
125+ body: Uint8Array<ArrayBuffer>;
126126 status: number;
127127 contentRange?: string;
128128 }
129129
130130 /** The engine hands back the whole attachment at once, so seeking in <video>/<audio> needs this SW to slice it. */
131-function sliceForRange(bytes: Uint8Array, rangeHeader: string | null): RangeSlice {
131+function sliceForRange(bytes: Uint8Array<ArrayBuffer>, rangeHeader: string | null): RangeSlice {
132132 const match = rangeHeader ? /^bytes=(\d*)-(\d*)$/.exec(rangeHeader) : null;
133133 if (!match) return { body: bytes, status: 200 };
134134 const total = bytes.byteLength;
@@ -214,7 +214,7 @@ function shouldAuthorize(fetchUrl: string, locatedUrl: string): boolean {
214214 }
215215 }
216216
217-function concatChunks(parts: Uint8Array[]): Uint8Array {
217+function concatChunks(parts: Uint8Array[]): Uint8Array<ArrayBuffer> {
218218 const total = parts.reduce((sum, part) => sum + part.byteLength, 0);
219219 const out = new Uint8Array(total);
220220 let offset = 0;
@@ -225,7 +225,10 @@ function concatChunks(parts: Uint8Array[]): Uint8Array {
225225 return out;
226226 }
227227
228-async function readUpTo(reader: ReadableStreamDefaultReader<Uint8Array>, limit: number): Promise<Uint8Array> {
228+async function readUpTo(
229+ reader: ReadableStreamDefaultReader<Uint8Array<ArrayBuffer>>,
230+ limit: number
231+): Promise<Uint8Array<ArrayBuffer>> {
229232 const chunks: Uint8Array[] = [];
230233 let total = 0;
231234 while (total < limit) {
@@ -244,7 +247,7 @@ async function readUpTo(reader: ReadableStreamDefaultReader<Uint8Array>, limit:
244247 * alignment prefix from the very first output, and stops once `limit` bytes have been produced.
245248 */
246249 async function* decryptedChunks(
247- reader: ReadableStreamDefaultReader<Uint8Array>,
250+ reader: ReadableStreamDefaultReader<Uint8Array<ArrayBuffer>>,
248251 decryptor: RangeDecryptor | null,
249252 skipBytes: number,
250253 dropPrefix: number,
Modified file: web/test/media.test.ts+11 additions−7 deletions
@@ -1,19 +1,19 @@
11 import assert from 'node:assert/strict';
22 import { test } from 'node:test';
3-import { addBlockOffset, importAttachmentKey, RangeDecryptor } from '../src/lib/media-decrypt.js';
4-import { attachmentContentType, imageContentType } from '../src/lib/media-sniff.js';
3+import { addBlockOffset, importAttachmentKey, RangeDecryptor } from '../src/lib/media-decrypt.ts';
4+import { attachmentContentType, imageContentType } from '../src/lib/media-sniff.ts';
55
6-function randomBytes(length: number): Uint8Array {
6+function randomBytes(length: number): Uint8Array<ArrayBuffer> {
77 const bytes = new Uint8Array(length);
88 crypto.getRandomValues(bytes);
99 return bytes;
1010 }
1111
12-function textBytes(text: string): Uint8Array {
12+function textBytes(text: string): Uint8Array<ArrayBuffer> {
1313 return new TextEncoder().encode(text);
1414 }
1515
16-function concatBytes(parts: Uint8Array[]): Uint8Array {
16+function concatBytes(parts: Uint8Array[]): Uint8Array<ArrayBuffer> {
1717 const total = parts.reduce((sum, part) => sum + part.length, 0);
1818 const out = new Uint8Array(total);
1919 let offset = 0;
@@ -30,7 +30,11 @@ function base64url(bytes: Uint8Array): string {
3030 return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
3131 }
3232
33-async function encryptWhole(rawKey: Uint8Array, iv: Uint8Array, plaintext: Uint8Array): Promise<Uint8Array> {
33+async function encryptWhole(
34+ rawKey: Uint8Array<ArrayBuffer>,
35+ iv: Uint8Array,
36+ plaintext: Uint8Array<ArrayBuffer>
37+): Promise<Uint8Array<ArrayBuffer>> {
3438 const encryptKey = await crypto.subtle.importKey('raw', rawKey, { name: 'AES-CTR' }, false, ['encrypt']);
3539 const counter = addBlockOffset(iv, 0);
3640 const cipher = await crypto.subtle.encrypt({ name: 'AES-CTR', counter, length: 64 }, encryptKey, plaintext);
@@ -40,7 +44,7 @@ async function encryptWhole(rawKey: Uint8Array, iv: Uint8Array, plaintext: Uint8
4044 async function decryptRange(
4145 key: CryptoKey,
4246 iv: Uint8Array,
43- ciphertext: Uint8Array,
47+ ciphertext: Uint8Array<ArrayBuffer>,
4448 start: number,
4549 end: number,
4650 chunkSize: number
Deleted file: web/test/node-shims.d.ts−15 deletions
@@ -1,15 +0,0 @@
1-// No @types/node in this project (kept dependency-free), so these are the
2-// minimal ambient declarations node --test and node:assert/strict need to
3-// type-check under tsconfig.test.json's DOM-flavoured lib.
4-declare module 'node:test' {
5- export function test(name: string, fn: () => void | Promise<void>): void;
6-}
7-
8-declare module 'node:assert/strict' {
9- interface Assert {
10- equal(actual: unknown, expected: unknown, message?: string): void;
11- deepEqual(actual: unknown, expected: unknown, message?: string): void;
12- }
13- const assert: Assert;
14- export default assert;
15-}
Deleted file: web/tsconfig.test.json−11 deletions
@@ -1,11 +0,0 @@
1-{
2- "compilerOptions": {
3- "target": "ES2022",
4- "module": "NodeNext",
5- "moduleResolution": "NodeNext",
6- "strict": true,
7- "skipLibCheck": true,
8- "outDir": ".test-dist"
9- },
10- "include": ["src/lib/media-decrypt.ts", "src/lib/media-sniff.ts", "test/media.test.ts", "test/node-shims.d.ts"]
11-}

show truncated diff