From 8b22b7c85bef0355f29b8a54c7ec4441dd180e55 Mon Sep 17 00:00:00 2001 From: Husamettin ARABACI Date: Thu, 2 Oct 2025 23:45:42 +0300 Subject: [PATCH 01/16] feat(setup): added basic at command (#19) Added basic at command implementation. It support usb-midi sysex protocol --- firmware/Cargo.lock | 30 ++++++++++++ firmware/Cargo.toml | 3 ++ firmware/src/at/commands.rs | 22 +++++++++ firmware/src/at/handler.rs | 57 +++++++++++++++++++++ firmware/src/at/mod.rs | 74 ++++++++++++++++++++++++++++ firmware/src/at/parser.rs | 80 ++++++++++++++++++++++++++++++ firmware/src/b64/mod.rs | 29 +++++++++++ firmware/src/error/mod.rs | 29 +++++++++++ firmware/src/main.rs | 11 ++++- firmware/src/sysex/mod.rs | 98 +++++++++++++++++++++++++++++++++++++ firmware/src/usb/mod.rs | 71 +++++++++++++++++++++++++-- 11 files changed, 499 insertions(+), 5 deletions(-) create mode 100644 firmware/src/at/commands.rs create mode 100644 firmware/src/at/handler.rs create mode 100644 firmware/src/at/mod.rs create mode 100644 firmware/src/at/parser.rs create mode 100644 firmware/src/b64/mod.rs create mode 100644 firmware/src/error/mod.rs create mode 100644 firmware/src/sysex/mod.rs diff --git a/firmware/Cargo.lock b/firmware/Cargo.lock index dead682..a7b0df4 100644 --- a/firmware/Cargo.lock +++ b/firmware/Cargo.lock @@ -90,6 +90,17 @@ name = "assign-resources" version = "0.4.0" source = "git+https://github.com/adamgreig/assign-resources?rev=bd22cb7a92031fb16f74a5da42469d466c33383e#bd22cb7a92031fb16f74a5da42469d466c33383e" +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "atomic-polyfill" version = "1.0.3" @@ -132,6 +143,12 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64ct" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" + [[package]] name = "bit-set" version = "0.8.0" @@ -1283,6 +1300,8 @@ name = "hexagenmini" version = "0.1.0" dependencies = [ "assign-resources", + "async-trait", + "base64ct", "byte-slice-cast", "cortex-m", "cortex-m-rt", @@ -1324,6 +1343,7 @@ dependencies = [ "smart-leds", "static_cell", "usbd-hid", + "usbd-midi", ] [[package]] @@ -2302,6 +2322,16 @@ dependencies = [ "usbd-hid-descriptors", ] +[[package]] +name = "usbd-midi" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a88782a02de4ea460cd84dc5cc8007a27618a5a8bb14514699dba5e7dcf9a590" +dependencies = [ + "num_enum 0.7.4", + "usb-device", +] + [[package]] name = "vcell" version = "0.1.3" diff --git a/firmware/Cargo.toml b/firmware/Cargo.toml index e516b41..e7cdd0b 100644 --- a/firmware/Cargo.toml +++ b/firmware/Cargo.toml @@ -87,6 +87,7 @@ byte-slice-cast = { version = "1.2.0", default-features = false } smart-leds = "0.4.0" heapless = "0.9.1" usbd-hid = "0.8.1" +usbd-midi = "0.5.0" embedded-hal-1 = { package = "embedded-hal", version = "1.0" } embedded-hal-async = "1.0" @@ -98,6 +99,8 @@ portable-atomic = { version = "1.5", features = ["critical-section"] } log = "0.4" rand = { version = "0.9.0", default-features = false } embedded-sdmmc = "0.9.0" +base64ct = "1.8.0" +async-trait = "0.1.89" [profile.release] # Enable generation of debug symbols even on release builds diff --git a/firmware/src/at/commands.rs b/firmware/src/at/commands.rs new file mode 100644 index 0000000..553ec88 --- /dev/null +++ b/firmware/src/at/commands.rs @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2025 hexaTune LLC +// SPDX-License-Identifier: MIT + +use crate::at::handler::{SumHandler, VersionHandler}; +use heapless::{LinearMap, String}; + +pub enum Handler { + Version(VersionHandler), + Sum(SumHandler), +} + +pub fn register_all() -> LinearMap, Handler, 8> { + let mut map: LinearMap, Handler, 8> = LinearMap::new(); + let mut version_key = String::<16>::new(); + version_key.push_str("VERSION").unwrap(); + map.insert(version_key, Handler::Version(VersionHandler)) + .ok(); + let mut sum_key = String::<16>::new(); + sum_key.push_str("SUM").unwrap(); + map.insert(sum_key, Handler::Sum(SumHandler)).ok(); + map +} diff --git a/firmware/src/at/handler.rs b/firmware/src/at/handler.rs new file mode 100644 index 0000000..76c9aa9 --- /dev/null +++ b/firmware/src/at/handler.rs @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: 2025 hexaTune LLC +// SPDX-License-Identifier: MIT + +use core::fmt::Write; + +/// Trait for AT command handlers +pub trait AtHandler { + fn handle( + &self, + params: &[heapless::String<16>], + is_query: bool, + ) -> Result, crate::error::Error>; +} + +/// Example: Version query handler +pub struct VersionHandler; + +impl AtHandler for VersionHandler { + fn handle( + &self, + _params: &[heapless::String<16>], + is_query: bool, + ) -> Result, crate::error::Error> { + if is_query { + let mut out = heapless::String::<64>::new(); + out.push_str("1.2.3") + .map_err(|_| crate::error::Error::InvalidUtf8)?; + Ok(out) + } else { + Err(crate::error::Error::NotAQuery) + } + } +} + +/// Example: Sum handler (takes two params, returns their sum) +pub struct SumHandler; + +impl AtHandler for SumHandler { + fn handle( + &self, + params: &[heapless::String<16>], + _is_query: bool, + ) -> Result, crate::error::Error> { + if params.len() != 2 { + return Err(crate::error::Error::ParamCount); + } + let a = params[0] + .parse::() + .map_err(|_| crate::error::Error::ParamValue)?; + let b = params[1] + .parse::() + .map_err(|_| crate::error::Error::ParamValue)?; + let mut out = heapless::String::<64>::new(); + write!(out, "{}", a + b).map_err(|_| crate::error::Error::InvalidUtf8)?; + Ok(out) + } +} diff --git a/firmware/src/at/mod.rs b/firmware/src/at/mod.rs new file mode 100644 index 0000000..f09ef09 --- /dev/null +++ b/firmware/src/at/mod.rs @@ -0,0 +1,74 @@ +// SPDX-FileCopyrightText: 2025 hexaTune LLC +// SPDX-License-Identifier: MIT + +#![allow(dead_code)] +use core::fmt::Write; + +use heapless::String; + +mod parser; +pub use parser::parse; + +mod handler; +pub use handler::AtHandler; + +mod commands; + +/// AT command dispatcher +pub struct AtDispatcher { + handlers: heapless::LinearMap, crate::at::commands::Handler, 8>, +} + +impl AtDispatcher { + /// Create a new dispatcher with all registered handlers + pub fn new() -> Self { + Self { + handlers: commands::register_all(), + } + } + + /// Dispatch an AT command string to the appropriate handler + pub fn dispatch(&self, input: &str) -> String<64> { + match parse(input) { + Ok(cmd) => match self.handlers.get(&cmd.name) { + Some(handler) => { + let result = match handler { + crate::at::commands::Handler::Version(h) => { + h.handle(&cmd.params, cmd.is_query) + } + crate::at::commands::Handler::Sum(h) => h.handle(&cmd.params, cmd.is_query), + }; + match result { + Ok(resp) => { + let encoded = crate::b64::encode(&resp); + let mut out = String::<64>::new(); + write!(out, "AT+OK={}", encoded).unwrap(); + out + } + Err(e) => { + let msg = e.description(); + let encoded = crate::b64::encode(msg); + let mut out = String::<64>::new(); + write!(out, "AT+ERROR={}", encoded).unwrap(); + out + } + } + } + None => { + let msg = crate::error::Error::UnknownCommand.description(); + let encoded = crate::b64::encode(msg); + let mut out = String::<64>::new(); + write!(out, "AT+ERROR={}", encoded).unwrap(); + out + } + }, + Err(e) => { + let msg = e.description(); + let encoded = crate::b64::encode(msg); + let mut out = String::<64>::new(); + write!(out, "AT+ERROR={}", encoded).unwrap(); + out + } + } + } +} diff --git a/firmware/src/at/parser.rs b/firmware/src/at/parser.rs new file mode 100644 index 0000000..4b8dba8 --- /dev/null +++ b/firmware/src/at/parser.rs @@ -0,0 +1,80 @@ +// SPDX-FileCopyrightText: 2025 hexaTune LLC +// SPDX-License-Identifier: MIT + +use defmt::info; +use heapless::{String, Vec}; + +/// Parsed AT command +pub struct ParsedAtCommand { + pub name: heapless::String<16>, + pub params: heapless::Vec, 8>, + pub is_query: bool, +} + +/// Parse AT command string into name, params, and query flag +pub fn parse(input: &str) -> Result { + let input = input.trim(); + if !input.starts_with("AT+") { + return Err(crate::error::Error::InvalidCommand); + } + let cmd = &input[3..]; + if let Some(eq_pos) = cmd.find('=') { + let (name, param_str) = cmd.split_at(eq_pos); + let param_str = ¶m_str[1..]; + let mut params = Vec::, 8>::new(); + for p in param_str + .split('#') + .map(|p| p.trim()) + .filter(|p| !p.is_empty()) + { + info!("Parsing param: {}", p); + match crate::b64::decode(p) { + Ok(decoded) => { + info!("Decoded param: {}", decoded.as_str()); + let mut s_hl = String::<16>::new(); + s_hl.push_str(&decoded) + .map_err(|_| crate::error::Error::InvalidUtf8)?; + params + .push(s_hl) + .map_err(|_| crate::error::Error::ParamCount)?; + } + Err(_) => { + info!("Base64 decode failed for param: {}", p); + return Err(crate::error::Error::InvalidBase64); + } + } + } + Ok(ParsedAtCommand { + name: { + let mut n = String::<16>::new(); + n.push_str(name) + .map_err(|_| crate::error::Error::InvalidUtf8)?; + n + }, + params, + is_query: false, + }) + } else if let Some(name) = cmd.strip_suffix('?') { + Ok(ParsedAtCommand { + name: { + let mut n = String::<16>::new(); + n.push_str(name) + .map_err(|_| crate::error::Error::InvalidUtf8)?; + n + }, + params: Vec::, 8>::new(), + is_query: true, + }) + } else { + Ok(ParsedAtCommand { + name: { + let mut n = String::<16>::new(); + n.push_str(cmd) + .map_err(|_| crate::error::Error::InvalidUtf8)?; + n + }, + params: Vec::, 8>::new(), + is_query: false, + }) + } +} diff --git a/firmware/src/b64/mod.rs b/firmware/src/b64/mod.rs new file mode 100644 index 0000000..952123d --- /dev/null +++ b/firmware/src/b64/mod.rs @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2025 hexaTune LLC +// SPDX-License-Identifier: MIT + +use base64ct::{Base64, Encoding}; +use heapless::String; + +pub fn decode(input: &str) -> Result, crate::error::Error> { + let mut buf = [0u8; 64]; + match Base64::decode(input, &mut buf) { + Ok(decoded) => match core::str::from_utf8(decoded) { + Ok(s) => { + let mut out = String::<64>::new(); + out.push_str(s) + .map_err(|_| crate::error::Error::InvalidUtf8)?; + Ok(out) + } + Err(_) => Err(crate::error::Error::InvalidUtf8), + }, + Err(_) => Err(crate::error::Error::InvalidBase64), + } +} + +pub fn encode(input: &str) -> String<64> { + let mut buf = [0u8; 64]; + let encoded = Base64::encode(input.as_bytes(), &mut buf).unwrap(); + let mut out = String::<64>::new(); + out.push_str(encoded).unwrap(); + out +} diff --git a/firmware/src/error/mod.rs b/firmware/src/error/mod.rs new file mode 100644 index 0000000..5d23991 --- /dev/null +++ b/firmware/src/error/mod.rs @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2025 hexaTune LLC +// SPDX-License-Identifier: MIT + +#[derive(Debug, Clone)] +pub enum Error { + InvalidCommand, // "Not an AT command" + InvalidBase64, // "Invalid base64 param" + InvalidUtf8, // "Invalid UTF-8 in param" + InvalidSysEx, // "Invalid SysEx" + ParamCount, // "Param count" + ParamValue, // "Param value" + NotAQuery, // "Not a query" + UnknownCommand, // "Unknown command" +} + +impl Error { + pub fn description(&self) -> &'static str { + match self { + Error::InvalidCommand => "Not an AT command", + Error::InvalidBase64 => "Invalid base64 param", + Error::InvalidUtf8 => "Invalid UTF-8 in param", + Error::InvalidSysEx => "Invalid SysEx", + Error::ParamCount => "Invalid param count", + Error::ParamValue => "Invalid param value", + Error::NotAQuery => "Not a query", + Error::UnknownCommand => "Unknown command", + } + } +} diff --git a/firmware/src/main.rs b/firmware/src/main.rs index 8caf0f5..6521e78 100644 --- a/firmware/src/main.rs +++ b/firmware/src/main.rs @@ -4,6 +4,7 @@ #![no_std] #![no_main] +use defmt::info; use embassy_executor::Spawner; use embassy_rp::bind_interrupts; use embassy_rp::gpio; @@ -14,6 +15,10 @@ use embassy_time::Timer; use gpio::{Level, Output}; use {defmt_rtt as _, panic_probe as _}; +mod at; +mod b64; +mod error; +mod sysex; mod usb; bind_interrupts!(struct Irqs { @@ -23,11 +28,13 @@ bind_interrupts!(struct Irqs { #[embassy_executor::main] async fn main(spawner: Spawner) { let p = embassy_rp::init(Default::default()); - + info!("Starting hexaGenMini firmware"); let driver = Driver::new(p.USB, Irqs); let usb::UsbMidi { device, midi } = usb::init(driver); + static DISPATCHER: static_cell::StaticCell = static_cell::StaticCell::new(); + let dispatcher = DISPATCHER.init(at::AtDispatcher::new()); spawner.spawn(usb::dev_task(device)).unwrap(); - spawner.spawn(usb::usb_io_task(midi)).unwrap(); + spawner.spawn(usb::usb_io_task(midi, dispatcher)).unwrap(); let mut led = Output::new(p.PIN_25, Level::Low); loop { diff --git a/firmware/src/sysex/mod.rs b/firmware/src/sysex/mod.rs new file mode 100644 index 0000000..ebddb38 --- /dev/null +++ b/firmware/src/sysex/mod.rs @@ -0,0 +1,98 @@ +// SPDX-FileCopyrightText: 2025 hexaTune LLC +// SPDX-License-Identifier: MIT + +use heapless::Vec as HVec; + +pub fn build_sysex(payload: &str) -> Option> { + let mut out: HVec = HVec::new(); + + // SysEx start + out.push(0xF0).ok()?; + + // Payload + for b in payload.as_bytes() { + out.push(*b).ok()?; + } + + // SysEx end + out.push(0xF7).ok()?; + + Some(out) +} + +pub fn sysex_to_usb_midi_packets(sysex: &[u8]) -> heapless::Vec<[u8; 4], M> { + use heapless::Vec; + let mut out: Vec<[u8; 4], M> = Vec::new(); + + let mut i = 0usize; + while i < sysex.len() { + let rem = sysex.len() - i; + if rem >= 3 { + if rem == 3 && sysex[sysex.len() - 1] == 0xF7 { + out.push([0x07, sysex[i], sysex[i + 1], sysex[i + 2]]).ok(); // end with 3 + i += 3; + } else { + out.push([0x04, sysex[i], sysex[i + 1], sysex[i + 2]]).ok(); // start/continue + i += 3; + } + } else if rem == 2 { + out.push([0x06, sysex[i], sysex[i + 1], 0x00]).ok(); // end with 2 + i += 2; + } else { + out.push([0x05, sysex[i], 0x00, 0x00]).ok(); // end with 1 + i += 1; + } + } + out +} + +pub fn extract_sysex_payload(data: &[u8]) -> Option> { + use heapless::Vec; + let mut out: Vec = Vec::new(); + + for chunk in data.chunks_exact(4) { + let cin = chunk[0] & 0x0F; + let b1 = chunk[1]; + let b2 = chunk[2]; + let b3 = chunk[3]; + + match cin { + 0x4 => { + // SysEx continue/start (3 byte data) + for &b in &[b1, b2, b3] { + if b != 0 { + out.push(b).ok(); + } + } + } + 0x5 => { + if b1 != 0 { + out.push(b1).ok(); + } + } // end with 1 + 0x6 => { + for &b in &[b1, b2] { + if b != 0 { + out.push(b).ok(); + } + } + } // end with 2 + 0x7 => { + for &b in &[b1, b2, b3] { + if b != 0 { + out.push(b).ok(); + } + } + } // end with 3 + _ => {} + } + } + + if out.first().copied() == Some(0xF0) && out.last().copied() == Some(0xF7) { + let mut payload: Vec = Vec::new(); + payload.extend_from_slice(&out[1..out.len() - 1]).ok()?; + Some(payload) + } else { + None + } +} diff --git a/firmware/src/usb/mod.rs b/firmware/src/usb/mod.rs index 62cd45c..96183f9 100644 --- a/firmware/src/usb/mod.rs +++ b/firmware/src/usb/mod.rs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2025 hexaTune LLC // SPDX-License-Identifier: MIT +use core::fmt::Write; use defmt::info; use embassy_usb::class::midi::MidiClass; use embassy_usb::{Builder, Config}; @@ -51,12 +52,76 @@ pub async fn dev_task(mut dev: MyUsbDevice<'static>) { } #[embassy_executor::task] -pub async fn usb_io_task(mut midi: MyMidiClass<'static>) { +pub async fn usb_io_task( + mut midi: MyMidiClass<'static>, + dispatcher: &'static crate::at::AtDispatcher, +) { let mut buf = [0; 64]; loop { let n = midi.read_packet(&mut buf).await.unwrap(); let data = &buf[..n]; - info!("data: {:x}", data); - midi.write_packet(data).await.unwrap(); + info!("Received MIDI packet: {:?}", data); + if let Some(payload) = crate::sysex::extract_sysex_payload(data) { + if let Ok(input) = core::str::from_utf8(&payload) { + let response = dispatcher.dispatch(input); + info!("Received AT command: {}", input); + info!("AT response: {}", response.as_str()); + if let Some(sysex) = crate::sysex::build_sysex::<64>(&response) { + let packets = crate::sysex::sysex_to_usb_midi_packets::<64>(&sysex); + for pkt in packets.iter() { + match midi.write_packet(pkt).await { + Ok(_) => { + info!("Sent AT response via SysEx"); + } + Err(e) => { + info!("Failed to send MIDI packet: {:?}", e); + } + } + } + } else { + info!("Response too long to fit in SysEx"); + } + } else { + let error = crate::error::Error::InvalidUtf8.description(); + let encoded = crate::b64::encode(error); + let mut response = heapless::String::<64>::new(); + write!(response, "AT+ERROR={}", encoded).unwrap(); + if let Some(sysex) = crate::sysex::build_sysex::<64>(&response) { + let packets = crate::sysex::sysex_to_usb_midi_packets::<64>(&sysex); + for pkt in packets.iter() { + match midi.write_packet(pkt).await { + Ok(_) => { + info!("Sent AT response via SysEx"); + } + Err(e) => { + info!("Failed to send MIDI packet: {:?}", e); + } + } + } + } else { + info!("Error response too long to fit in SysEx"); + } + } + } else { + let error = crate::error::Error::InvalidSysEx.description(); + let encoded = crate::b64::encode(error); + let mut response = heapless::String::<64>::new(); + write!(response, "AT+ERROR={}", encoded).unwrap(); + if let Some(sysex) = crate::sysex::build_sysex::<64>(&response) { + let packets = crate::sysex::sysex_to_usb_midi_packets::<64>(&sysex); + for pkt in packets.iter() { + match midi.write_packet(pkt).await { + Ok(_) => { + info!("Sent AT response via SysEx"); + } + Err(e) => { + info!("Failed to send MIDI packet: {:?}", e); + } + } + } + } else { + info!("Error response too long to fit in SysEx"); + } + } } } From 24c65b224a3f1975f9a4e86f7593b4f07f2b602f Mon Sep 17 00:00:00 2001 From: Husamettin ARABACI Date: Sat, 4 Oct 2025 21:28:32 +0300 Subject: [PATCH 02/16] feat(setup): setted up pre commit hooks (#20) Added reuse prettier commitlint hook --- .gitignore | 1 + .husky/commit-msg | 28 + .husky/pre-commit | 49 + REUSE.toml | 9 +- package-lock.json | 8291 +++++++++++++++++++++++++++++++++++++++++++++ package.json | 33 +- 6 files changed, 8404 insertions(+), 7 deletions(-) create mode 100644 .husky/commit-msg create mode 100644 .husky/pre-commit create mode 100644 package-lock.json diff --git a/.gitignore b/.gitignore index f4ceea7..412618a 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ **/target/ +node_modules/ diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100644 index 0000000..96b4b47 --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1,28 @@ + +# Husky v10: no shebang, no husky.sh + +CONFIG=".github/.commitlintrc.yml" +MSG_FILE="$1" + +echo "👉 Running Commitlint check..." + +# Check if commitlint is installed via npm +if npx --no-install commitlint --version >/dev/null 2>&1; then + # Config file check + if [ -f "$CONFIG" ]; then + npx --no-install commitlint --config "$CONFIG" --edit "$MSG_FILE" + else + echo "❌ Commitlint config not found at: $CONFIG" + echo " Please ensure this file exists before committing." + exit 1 + fi +else + echo "❌ Commitlint CLI not found (required)." + echo " Install it with:" + echo " npm install -D @commitlint/cli @commitlint/config-conventional" + echo " Commit aborted until Commitlint is installed." + exit 1 +fi + +echo "✅ Commitlint check passed." + diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..70cb910 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,49 @@ +# Husky v10+: no shebang, no sourcing, POSIX-sh safe +set -e + +# --- REUSE at repository root (strict check first) --- +if ! command -v reuse >/dev/null 2>&1; then + echo "❌ REUSE tool is required but not installed." + echo " Install it with: pip install reuse OR pipx install reuse" + exit 1 +fi + +echo "📜 REUSE license compliance (repo root)..." +if ! reuse lint; then + echo "❌ REUSE license compliance failed." + exit 1 +fi + +# --- Rust checks inside firmware directory --- +( + cd firmware || { + echo "⚠️ 'firmware' directory not found." + exit 1 + } + + echo "🔍 Checking code formatting (cargo fmt)..." + if ! cargo fmt --all -- --check; then + echo "❌ Code formatting issues detected. Run: cargo fmt --all" + exit 1 + fi + + echo "🧠 Running Clippy (RP2040 target)..." + if ! cargo clippy --target thumbv6m-none-eabi -- -D warnings; then + echo "❌ Clippy failed with warnings/errors." + exit 1 + fi + + echo "🏗️ Building (release, RP2040 target)..." + if ! cargo build --release --target thumbv6m-none-eabi; then + echo "❌ Build failed." + exit 1 + fi + + echo "✅ cargo check (RP2040 target)..." + if ! cargo check --target thumbv6m-none-eabi; then + echo "❌ cargo check failed." + exit 1 + fi +) + +echo "✅ All checks passed! Proceeding with commit..." diff --git a/REUSE.toml b/REUSE.toml index b2b7395..b1a8e41 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -1,7 +1,14 @@ version = 1 [[annotations]] -path = ["package.json", "CHANGELOG.md", ".gitignore", "docs/CNAME"] +path = [ + "package.json", + "CHANGELOG.md", + ".gitignore", + "docs/CNAME", + ".husky/**", + "package-lock.json", +] precedence = "aggregate" SPDX-FileCopyrightText = "2025 hexaTune LLC" SPDX-License-Identifier = "MIT" diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..bf9f2d9 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,8291 @@ +{ + "name": "hexagenmini", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "hexagenmini", + "version": "0.0.0", + "license": "ISC", + "devDependencies": { + "@commitlint/cli": "^20.1.0", + "@commitlint/config-conventional": "^20.0.0", + "@semantic-release/changelog": "^6.0.0", + "@semantic-release/commit-analyzer": "^9.0.0", + "@semantic-release/git": "^10.0.0", + "@semantic-release/github": "^9.0.0", + "@semantic-release/release-notes-generator": "^10.0.0", + "husky": "^9.1.7", + "semantic-release": "^22.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@commitlint/cli": { + "version": "20.1.0", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-20.1.0.tgz", + "integrity": "sha512-pW5ujjrOovhq5RcYv5xCpb4GkZxkO2+GtOdBW2/qrr0Ll9tl3PX0aBBobGQl3mdZUbOBgwAexEQLeH6uxL0VYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/format": "^20.0.0", + "@commitlint/lint": "^20.0.0", + "@commitlint/load": "^20.1.0", + "@commitlint/read": "^20.0.0", + "@commitlint/types": "^20.0.0", + "tinyexec": "^1.0.0", + "yargs": "^17.0.0" + }, + "bin": { + "commitlint": "cli.js" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/config-conventional": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-20.0.0.tgz", + "integrity": "sha512-q7JroPIkDBtyOkVe9Bca0p7kAUYxZMxkrBArCfuD3yN4KjRAenP9PmYwnn7rsw8Q+hHq1QB2BRmBh0/Z19ZoJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^20.0.0", + "conventional-changelog-conventionalcommits": "^7.0.2" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/config-validator": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-20.0.0.tgz", + "integrity": "sha512-BeyLMaRIJDdroJuYM2EGhDMGwVBMZna9UiIqV9hxj+J551Ctc6yoGuGSmghOy/qPhBSuhA6oMtbEiTmxECafsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^20.0.0", + "ajv": "^8.11.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/ensure": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-20.0.0.tgz", + "integrity": "sha512-WBV47Fffvabe68n+13HJNFBqiMH5U1Ryls4W3ieGwPC0C7kJqp3OVQQzG2GXqOALmzrgAB+7GXmyy8N9ct8/Fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^20.0.0", + "lodash.camelcase": "^4.3.0", + "lodash.kebabcase": "^4.1.1", + "lodash.snakecase": "^4.1.1", + "lodash.startcase": "^4.4.0", + "lodash.upperfirst": "^4.3.1" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/execute-rule": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-20.0.0.tgz", + "integrity": "sha512-xyCoOShoPuPL44gVa+5EdZsBVao/pNzpQhkzq3RdtlFdKZtjWcLlUFQHSWBuhk5utKYykeJPSz2i8ABHQA+ZZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/format": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-20.0.0.tgz", + "integrity": "sha512-zrZQXUcSDmQ4eGGrd+gFESiX0Rw+WFJk7nW4VFOmxub4mAATNKBQ4vNw5FgMCVehLUKG2OT2LjOqD0Hk8HvcRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^20.0.0", + "chalk": "^5.3.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/is-ignored": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-20.0.0.tgz", + "integrity": "sha512-ayPLicsqqGAphYIQwh9LdAYOVAQ9Oe5QCgTNTj+BfxZb9b/JW222V5taPoIBzYnAP0z9EfUtljgBk+0BN4T4Cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^20.0.0", + "semver": "^7.6.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/is-ignored/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@commitlint/lint": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-20.0.0.tgz", + "integrity": "sha512-kWrX8SfWk4+4nCexfLaQT3f3EcNjJwJBsSZ5rMBw6JCd6OzXufFHgel2Curos4LKIxwec9WSvs2YUD87rXlxNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/is-ignored": "^20.0.0", + "@commitlint/parse": "^20.0.0", + "@commitlint/rules": "^20.0.0", + "@commitlint/types": "^20.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/load": { + "version": "20.1.0", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-20.1.0.tgz", + "integrity": "sha512-qo9ER0XiAimATQR5QhvvzePfeDfApi/AFlC1G+YN+ZAY8/Ua6IRrDrxRvQAr+YXUKAxUsTDSp9KXeXLBPsNRWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/config-validator": "^20.0.0", + "@commitlint/execute-rule": "^20.0.0", + "@commitlint/resolve-extends": "^20.1.0", + "@commitlint/types": "^20.0.0", + "chalk": "^5.3.0", + "cosmiconfig": "^9.0.0", + "cosmiconfig-typescript-loader": "^6.1.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "lodash.uniq": "^4.5.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/load/node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@commitlint/load/node_modules/cosmiconfig-typescript-loader": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.1.0.tgz", + "integrity": "sha512-tJ1w35ZRUiM5FeTzT7DtYWAFFv37ZLqSRkGi2oeCK1gPhvaWjkAtfXvLmvE1pRfxxp9aQo6ba/Pvg1dKj05D4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "jiti": "^2.4.1" + }, + "engines": { + "node": ">=v18" + }, + "peerDependencies": { + "@types/node": "*", + "cosmiconfig": ">=9", + "typescript": ">=5" + } + }, + "node_modules/@commitlint/message": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-20.0.0.tgz", + "integrity": "sha512-gLX4YmKnZqSwkmSB9OckQUrI5VyXEYiv3J5JKZRxIp8jOQsWjZgHSG/OgEfMQBK9ibdclEdAyIPYggwXoFGXjQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/parse": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-20.0.0.tgz", + "integrity": "sha512-j/PHCDX2bGM5xGcWObOvpOc54cXjn9g6xScXzAeOLwTsScaL4Y+qd0pFC6HBwTtrH92NvJQc+2Lx9HFkVi48cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^20.0.0", + "conventional-changelog-angular": "^7.0.0", + "conventional-commits-parser": "^5.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/parse/node_modules/conventional-changelog-angular": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz", + "integrity": "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@commitlint/parse/node_modules/conventional-commits-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", + "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-text-path": "^2.0.0", + "JSONStream": "^1.3.5", + "meow": "^12.0.1", + "split2": "^4.0.0" + }, + "bin": { + "conventional-commits-parser": "cli.mjs" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@commitlint/parse/node_modules/is-text-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz", + "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "text-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@commitlint/parse/node_modules/meow": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", + "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/parse/node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/@commitlint/parse/node_modules/text-extensions": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz", + "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/read": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-20.0.0.tgz", + "integrity": "sha512-Ti7Y7aEgxsM1nkwA4ZIJczkTFRX/+USMjNrL9NXwWQHqNqrBX2iMi+zfuzZXqfZ327WXBjdkRaytJ+z5vNqTOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/top-level": "^20.0.0", + "@commitlint/types": "^20.0.0", + "git-raw-commits": "^4.0.0", + "minimist": "^1.2.8", + "tinyexec": "^1.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/resolve-extends": { + "version": "20.1.0", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-20.1.0.tgz", + "integrity": "sha512-cxKXQrqHjZT3o+XPdqDCwOWVFQiae++uwd9dUBC7f2MdV58ons3uUvASdW7m55eat5sRiQ6xUHyMWMRm6atZWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/config-validator": "^20.0.0", + "@commitlint/types": "^20.0.0", + "global-directory": "^4.0.1", + "import-meta-resolve": "^4.0.0", + "lodash.mergewith": "^4.6.2", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/rules": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-20.0.0.tgz", + "integrity": "sha512-gvg2k10I/RfvHn5I5sxvVZKM1fl72Sqrv2YY/BnM7lMHcYqO0E2jnRWoYguvBfEcZ39t+rbATlciggVe77E4zA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/ensure": "^20.0.0", + "@commitlint/message": "^20.0.0", + "@commitlint/to-lines": "^20.0.0", + "@commitlint/types": "^20.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/to-lines": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-20.0.0.tgz", + "integrity": "sha512-2l9gmwiCRqZNWgV+pX1X7z4yP0b3ex/86UmUFgoRt672Ez6cAM2lOQeHFRUTuE6sPpi8XBCGnd8Kh3bMoyHwJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/top-level": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-20.0.0.tgz", + "integrity": "sha512-drXaPSP2EcopukrUXvUXmsQMu3Ey/FuJDc/5oiW4heoCfoE5BdLQyuc7veGeE3aoQaTVqZnh4D5WTWe2vefYKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^7.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/top-level/node_modules/find-up": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz", + "integrity": "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^7.2.0", + "path-exists": "^5.0.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/@commitlint/top-level/node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/types": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-20.0.0.tgz", + "integrity": "sha512-bVUNBqG6aznYcYjTjnc3+Cat/iBgbgpflxbIBTnsHTX0YVpnmINPEkSRWymT2Q8aSH3Y7aKnEbunilkYe8TybA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/conventional-commits-parser": "^5.0.0", + "chalk": "^5.3.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@octokit/auth-token": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", + "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/core": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.2.tgz", + "integrity": "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/auth-token": "^4.0.0", + "@octokit/graphql": "^7.1.0", + "@octokit/request": "^8.4.1", + "@octokit/request-error": "^5.1.1", + "@octokit/types": "^13.0.0", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/endpoint": { + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz", + "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.1.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/graphql": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.1.tgz", + "integrity": "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/request": "^8.4.1", + "@octokit/types": "^13.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.2.tgz", + "integrity": "sha512-u3KYkGF7GcZnSD/3UP0S7K5XUFT2FkOQdcfXZGZQPGv3lm4F2Xbf71lvjldr8c1H3nNbF+33cLEkWYbokGWqiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^12.6.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz", + "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz", + "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^20.0.0" + } + }, + "node_modules/@octokit/plugin-retry": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-6.1.0.tgz", + "integrity": "sha512-WrO3bvq4E1Xh1r2mT9w6SDFg01gFmP81nIG77+p/MqW1JeXXgL++6umim3t6x0Zj5pZm3rXAN+0HEjmmdhIRig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^13.0.0", + "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@octokit/plugin-throttling": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-8.2.0.tgz", + "integrity": "sha512-nOpWtLayKFpgqmgD0y3GqXafMFuKcA4tRPZIfu7BArd2lEZeb1988nhWhwx4aZWmjDmUfdgVf7W+Tt4AmvRmMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^12.2.0", + "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "^5.0.0" + } + }, + "node_modules/@octokit/plugin-throttling/node_modules/@octokit/openapi-types": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz", + "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-throttling/node_modules/@octokit/types": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz", + "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^20.0.0" + } + }, + "node_modules/@octokit/request": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz", + "integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/endpoint": "^9.0.6", + "@octokit/request-error": "^5.1.1", + "@octokit/types": "^13.1.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/request-error": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz", + "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.1.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@pnpm/config.env-replace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", + "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnpm/network.ca-file": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", + "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "4.2.10" + }, + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true, + "license": "ISC" + }, + "node_modules/@pnpm/npm-conf": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.3.1.tgz", + "integrity": "sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pnpm/config.env-replace": "^1.1.0", + "@pnpm/network.ca-file": "^1.0.1", + "config-chain": "^1.1.11" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@semantic-release/changelog": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@semantic-release/changelog/-/changelog-6.0.3.tgz", + "integrity": "sha512-dZuR5qByyfe3Y03TpmCvAxCyTnp7r5XwtHRf/8vD9EAn4ZWbavUX8adMtXYzE86EVh0gyLA7lm5yW4IV30XUag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@semantic-release/error": "^3.0.0", + "aggregate-error": "^3.0.0", + "fs-extra": "^11.0.0", + "lodash": "^4.17.4" + }, + "engines": { + "node": ">=14.17" + }, + "peerDependencies": { + "semantic-release": ">=18.0.0" + } + }, + "node_modules/@semantic-release/commit-analyzer": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-9.0.2.tgz", + "integrity": "sha512-E+dr6L+xIHZkX4zNMe6Rnwg4YQrWNXK+rNsvwOPpdFppvZO1olE2fIgWhv89TkQErygevbjsZFSIxp+u6w2e5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-changelog-angular": "^5.0.0", + "conventional-commits-filter": "^2.0.0", + "conventional-commits-parser": "^3.2.3", + "debug": "^4.0.0", + "import-from": "^4.0.0", + "lodash": "^4.17.4", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=14.17" + }, + "peerDependencies": { + "semantic-release": ">=18.0.0-beta.1" + } + }, + "node_modules/@semantic-release/error": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-3.0.0.tgz", + "integrity": "sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@semantic-release/git": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@semantic-release/git/-/git-10.0.1.tgz", + "integrity": "sha512-eWrx5KguUcU2wUPaO6sfvZI0wPafUKAMNC18aXY4EnNcrZL86dEmpNVnC9uMpGZkmZJ9EfCVJBQx4pV4EMGT1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@semantic-release/error": "^3.0.0", + "aggregate-error": "^3.0.0", + "debug": "^4.0.0", + "dir-glob": "^3.0.0", + "execa": "^5.0.0", + "lodash": "^4.17.4", + "micromatch": "^4.0.0", + "p-reduce": "^2.0.0" + }, + "engines": { + "node": ">=14.17" + }, + "peerDependencies": { + "semantic-release": ">=18.0.0" + } + }, + "node_modules/@semantic-release/github": { + "version": "9.2.6", + "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-9.2.6.tgz", + "integrity": "sha512-shi+Lrf6exeNZF+sBhK+P011LSbhmIAoUEgEY6SsxF8irJ+J2stwI5jkyDQ+4gzYyDImzV6LCKdYB9FXnQRWKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/core": "^5.0.0", + "@octokit/plugin-paginate-rest": "^9.0.0", + "@octokit/plugin-retry": "^6.0.0", + "@octokit/plugin-throttling": "^8.0.0", + "@semantic-release/error": "^4.0.0", + "aggregate-error": "^5.0.0", + "debug": "^4.3.4", + "dir-glob": "^3.0.1", + "globby": "^14.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "issue-parser": "^6.0.0", + "lodash-es": "^4.17.21", + "mime": "^4.0.0", + "p-filter": "^4.0.0", + "url-join": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "semantic-release": ">=20.1.0" + } + }, + "node_modules/@semantic-release/github/node_modules/@semantic-release/error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz", + "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@semantic-release/github/node_modules/aggregate-error": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz", + "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^5.2.0", + "indent-string": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/github/node_modules/clean-stack": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.3.0.tgz", + "integrity": "sha512-9ngPTOhYGQqNVSfeJkYXHmF7AGWp4/nN5D/QqNQs3Dvxd1Kk/WpjHfNujKHYUQ/5CoGyOyFNoWSPk5afzP0QVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "5.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/github/node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-11.0.3.tgz", + "integrity": "sha512-KUsozQGhRBAnoVg4UMZj9ep436VEGwT536/jwSqB7vcEfA6oncCUU7UIYTRdLx7GvTtqn0kBjnkfLVkcnBa2YQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@semantic-release/error": "^4.0.0", + "aggregate-error": "^5.0.0", + "execa": "^8.0.0", + "fs-extra": "^11.0.0", + "lodash-es": "^4.17.21", + "nerf-dart": "^1.0.0", + "normalize-url": "^8.0.0", + "npm": "^10.5.0", + "rc": "^1.2.8", + "read-pkg": "^9.0.0", + "registry-auth-token": "^5.0.0", + "semver": "^7.1.2", + "tempy": "^3.0.0" + }, + "engines": { + "node": "^18.17 || >=20" + }, + "peerDependencies": { + "semantic-release": ">=20.1.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/@semantic-release/error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz", + "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@semantic-release/npm/node_modules/aggregate-error": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz", + "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^5.2.0", + "indent-string": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm/node_modules/clean-stack": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.3.0.tgz", + "integrity": "sha512-9ngPTOhYGQqNVSfeJkYXHmF7AGWp4/nN5D/QqNQs3Dvxd1Kk/WpjHfNujKHYUQ/5CoGyOyFNoWSPk5afzP0QVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "5.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/@semantic-release/npm/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm/node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/@semantic-release/npm/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm/node_modules/normalize-package-data": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm/node_modules/parse-json": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", + "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "index-to-position": "^1.1.0", + "type-fest": "^4.39.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm/node_modules/read-pkg": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz", + "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.3", + "normalize-package-data": "^6.0.0", + "parse-json": "^8.0.0", + "type-fest": "^4.6.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@semantic-release/npm/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@semantic-release/npm/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm/node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/release-notes-generator": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-10.0.3.tgz", + "integrity": "sha512-k4x4VhIKneOWoBGHkx0qZogNjCldLPRiAjnIpMnlUh6PtaWXp/T+C9U7/TaNDDtgDa5HMbHl4WlREdxHio6/3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-changelog-angular": "^5.0.0", + "conventional-changelog-writer": "^5.0.0", + "conventional-commits-filter": "^2.0.0", + "conventional-commits-parser": "^3.2.3", + "debug": "^4.0.0", + "get-stream": "^6.0.0", + "import-from": "^4.0.0", + "into-stream": "^6.0.0", + "lodash": "^4.17.4", + "read-pkg-up": "^7.0.0" + }, + "engines": { + "node": ">=14.17" + }, + "peerDependencies": { + "semantic-release": ">=18.0.0-beta.1" + } + }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@types/conventional-commits-parser": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/conventional-commits-parser/-/conventional-commits-parser-5.0.1.tgz", + "integrity": "sha512-7uz5EHdzz2TqoMfV7ee61Egf5y6NkcO4FB/1iCCQnbeiI1F3xzv3vK5dBCXUCLQgGYS+mUeigK1iKQzvED+QnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.6.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.6.2.tgz", + "integrity": "sha512-d2L25Y4j+W3ZlNAeMKcy7yDsK425ibcAOO2t7aPTz6gNMH0z2GThtwENCDc0d/Pw9wgyRqE5Px1wkV7naz8ang==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.13.0" + } + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true, + "license": "MIT" + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.1.tgz", + "integrity": "sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansicolors": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", + "integrity": "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/argv-formatter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/argv-formatter/-/argv-formatter-1.0.0.tgz", + "integrity": "sha512-F2+Hkm9xFaRg+GkaNnbwXNDV5O6pnCFEmqyhvfC/Ic5LbgOWjJh3L+mN/s91rxVL3znE7DYVpW0GJFT+4YBgWw==", + "dev": true, + "license": "MIT" + }, + "node_modules/array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", + "dev": true, + "license": "MIT" + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/before-after-hook": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/bottleneck": { + "version": "2.19.5", + "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", + "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cardinal": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", + "integrity": "sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansicolors": "~0.3.2", + "redeyed": "~2.1.0" + }, + "bin": { + "cdl": "bin/cdl.js" + } + }, + "node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/conventional-changelog-angular": { + "version": "5.0.13", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz", + "integrity": "sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0", + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-conventionalcommits": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-7.0.2.tgz", + "integrity": "sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/conventional-changelog-writer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-5.0.1.tgz", + "integrity": "sha512-5WsuKUfxW7suLblAbFnxAcrvf6r+0b7GvNaWUwUIk0bXMnENP/PEieGKVUQrjPqwPT4o3EPAASBXiY6iHooLOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-commits-filter": "^2.0.7", + "dateformat": "^3.0.0", + "handlebars": "^4.7.7", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "semver": "^6.0.0", + "split": "^1.0.0", + "through2": "^4.0.0" + }, + "bin": { + "conventional-changelog-writer": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-commits-filter": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz", + "integrity": "sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.ismatch": "^4.4.0", + "modify-values": "^1.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-commits-parser": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz", + "integrity": "sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-text-path": "^1.0.1", + "JSONStream": "^1.0.4", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "split2": "^3.0.0", + "through2": "^4.0.0" + }, + "bin": { + "conventional-commits-parser": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", + "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^1.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/crypto-random-string/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dargs": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-8.1.0.tgz", + "integrity": "sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dateformat": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", + "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "dev": true, + "license": "MIT", + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/emojilib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", + "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/env-ci": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-10.0.0.tgz", + "integrity": "sha512-U4xcd/utDYFgMh0yWj07R1H6L5fwhVbmxBCpnL0DbVSDZVnsC82HONw0wxtxNkIAcua3KtbomQvIk5xFZGAQJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^8.0.0", + "java-properties": "^1.0.2" + }, + "engines": { + "node": "^18.17 || >=20.6.1" + } + }, + "node_modules/env-ci/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/env-ci/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-ci/node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/env-ci/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-ci/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-ci/node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-ci/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-ci/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-ci/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/env-ci/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up-simple": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz", + "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-versions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-5.1.0.tgz", + "integrity": "sha512-+iwzCJ7C5v5KgcBuueqVoNiHVoQpwiUK5XFLjf0affFTep+Wcw93tPvmb8tqujDNmzhBDPddnWV/qgWSXgq+Hg==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver-regex": "^4.0.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "node_modules/fs-extra": { + "version": "11.3.2", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.2.tgz", + "integrity": "sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/git-log-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/git-log-parser/-/git-log-parser-1.2.1.tgz", + "integrity": "sha512-PI+sPDvHXNPl5WNOErAK05s3j0lgwUzMN6o8cyQrDaKfT3qd7TmNJKeXX+SknI5I0QhG5fVPAEwSY4tRGDtYoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "argv-formatter": "~1.0.0", + "spawn-error-forwarder": "~1.0.0", + "split2": "~1.0.0", + "stream-combiner2": "~1.1.1", + "through2": "~2.0.0", + "traverse": "0.6.8" + } + }, + "node_modules/git-log-parser/node_modules/split2": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-1.0.0.tgz", + "integrity": "sha512-NKywug4u4pX/AZBB1FCPzZ6/7O+Xhz1qMVbzTvvKvikjO99oPN87SkK08mEY9P63/5lWjK+wgOOgApnTg5r6qg==", + "dev": true, + "license": "ISC", + "dependencies": { + "through2": "~2.0.0" + } + }, + "node_modules/git-log-parser/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/git-raw-commits": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-4.0.0.tgz", + "integrity": "sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dargs": "^8.0.0", + "meow": "^12.0.1", + "split2": "^4.0.0" + }, + "bin": { + "git-raw-commits": "cli.mjs" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/git-raw-commits/node_modules/meow": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", + "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/git-raw-commits/node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/global-directory": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", + "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "4.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/global-directory/node_modules/ini": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", + "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/globby": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz", + "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.3", + "path-type": "^6.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/path-type": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", + "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hook-std": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-3.0.0.tgz", + "integrity": "sha512-jHRQzjSDzMtFy34AGj1DN+vq54WVuhSvKgrHf0OMiFQTwDD4L/qqofVEWjLOBMTn5+lCD3fPg32W9yOfnEJTTw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/husky": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", + "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/import-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-4.0.0.tgz", + "integrity": "sha512-P9J71vT5nLlDeV8FHs5nNxaLbrpfAV5cF5srvbZfpwpcJoM/xZR3hiv+q+SAnuSmuGbXMWud063iIMx/V/EWZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-from-esm": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/import-from-esm/-/import-from-esm-1.3.4.tgz", + "integrity": "sha512-7EyUlPFC0HOlBDpUFGfYstsU7XHxZJKAAMzCT8wZ0hMW7b+hG51LIKTDcsgtz8Pu6YC0HqRVbX+rVUtsGMUKvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "import-meta-resolve": "^4.0.0" + }, + "engines": { + "node": ">=16.20" + } + }, + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/index-to-position": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz", + "integrity": "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/into-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-6.0.0.tgz", + "integrity": "sha512-XHbaOAvP+uFKUFsOgoNPRjLkwB+I22JFPFe5OjTkQ0nwgj6+pSjb4NmB6VMxaPshLiOf+zcpOCBQuLwC1KHhZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "from2": "^2.3.0", + "p-is-promise": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-text-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", + "integrity": "sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "text-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/issue-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-6.0.0.tgz", + "integrity": "sha512-zKa/Dxq2lGsBIXQ7CUZWTHfvxPC2ej0KfO7fIPqLlHB9J2hJ7rGhZ5rilhuufylr4RXYPzJUeFjKxz305OsNlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.capitalize": "^4.2.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.uniqby": "^4.7.0" + }, + "engines": { + "node": ">=10.13" + } + }, + "node_modules/java-properties": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/java-properties/-/java-properties-1.0.2.tgz", + "integrity": "sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "license": "ISC" + }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "license": "MIT" + }, + "node_modules/JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "license": "(MIT OR Apache-2.0)", + "dependencies": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.capitalize": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", + "integrity": "sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.ismatch": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", + "integrity": "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.kebabcase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", + "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.startcase": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", + "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.uniqby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", + "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.upperfirst": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", + "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/marked": { + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/marked/-/marked-9.1.6.tgz", + "integrity": "sha512-jcByLnIFkd5gSXZmjNvS1TlmRhCXZjIzHYlaGkPlLIekG55JDR2Z4va9tZwCiP+/RDERiNhMOFu01xd6O5ct1Q==", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 16" + } + }, + "node_modules/marked-terminal": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-6.2.0.tgz", + "integrity": "sha512-ubWhwcBFHnXsjYNsu+Wndpg0zhY4CahSpPlA70PlO0rR9r2sZpkyU+rkCsOWH+KMEkx847UpALON+HWgxowFtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^6.2.0", + "cardinal": "^2.1.1", + "chalk": "^5.3.0", + "cli-table3": "^0.6.3", + "node-emoji": "^2.1.3", + "supports-hyperlinks": "^3.0.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "marked": ">=1 <12" + } + }, + "node_modules/meow": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", + "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.1.0.tgz", + "integrity": "sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa" + ], + "license": "MIT", + "bin": { + "mime": "bin/cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/modify-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", + "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nerf-dart": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/nerf-dart/-/nerf-dart-1.0.0.tgz", + "integrity": "sha512-EZSPZB70jiVsivaBLYDCyntd5eH8NTSMOn3rB+HxwdmKThGELLdYv8qVIMWvZEFy9w8ZZpW9h9OB32l1rGtj7g==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-emoji": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.2.0.tgz", + "integrity": "sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.6.0", + "char-regex": "^1.0.2", + "emojilib": "^2.4.0", + "skin-tone": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-url": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.1.0.tgz", + "integrity": "sha512-X06Mfd/5aKsRHc0O0J5CUedwnPmnDtLF2+nq+KN9KSDlJHkPuh0JUviWjEWMe0SW/9TDdSLVPuk7L5gGTIA1/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm": { + "version": "10.9.4", + "resolved": "https://registry.npmjs.org/npm/-/npm-10.9.4.tgz", + "integrity": "sha512-OnUG836FwboQIbqtefDNlyR0gTHzIfwRfE3DuiNewBvnMnWEpB0VEXwBlFVgqpNzIgYo/MHh3d2Hel/pszapAA==", + "bundleDependencies": [ + "@isaacs/string-locale-compare", + "@npmcli/arborist", + "@npmcli/config", + "@npmcli/fs", + "@npmcli/map-workspaces", + "@npmcli/package-json", + "@npmcli/promise-spawn", + "@npmcli/redact", + "@npmcli/run-script", + "@sigstore/tuf", + "abbrev", + "archy", + "cacache", + "chalk", + "ci-info", + "cli-columns", + "fastest-levenshtein", + "fs-minipass", + "glob", + "graceful-fs", + "hosted-git-info", + "ini", + "init-package-json", + "is-cidr", + "json-parse-even-better-errors", + "libnpmaccess", + "libnpmdiff", + "libnpmexec", + "libnpmfund", + "libnpmhook", + "libnpmorg", + "libnpmpack", + "libnpmpublish", + "libnpmsearch", + "libnpmteam", + "libnpmversion", + "make-fetch-happen", + "minimatch", + "minipass", + "minipass-pipeline", + "ms", + "node-gyp", + "nopt", + "normalize-package-data", + "npm-audit-report", + "npm-install-checks", + "npm-package-arg", + "npm-pick-manifest", + "npm-profile", + "npm-registry-fetch", + "npm-user-validate", + "p-map", + "pacote", + "parse-conflict-json", + "proc-log", + "qrcode-terminal", + "read", + "semver", + "spdx-expression-parse", + "ssri", + "supports-color", + "tar", + "text-table", + "tiny-relative-date", + "treeverse", + "validate-npm-package-name", + "which", + "write-file-atomic" + ], + "dev": true, + "license": "Artistic-2.0", + "workspaces": [ + "docs", + "smoke-tests", + "mock-globals", + "mock-registry", + "workspaces/*" + ], + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/arborist": "^8.0.1", + "@npmcli/config": "^9.0.0", + "@npmcli/fs": "^4.0.0", + "@npmcli/map-workspaces": "^4.0.2", + "@npmcli/package-json": "^6.2.0", + "@npmcli/promise-spawn": "^8.0.2", + "@npmcli/redact": "^3.2.2", + "@npmcli/run-script": "^9.1.0", + "@sigstore/tuf": "^3.1.1", + "abbrev": "^3.0.1", + "archy": "~1.0.0", + "cacache": "^19.0.1", + "chalk": "^5.4.1", + "ci-info": "^4.2.0", + "cli-columns": "^4.0.0", + "fastest-levenshtein": "^1.0.16", + "fs-minipass": "^3.0.3", + "glob": "^10.4.5", + "graceful-fs": "^4.2.11", + "hosted-git-info": "^8.1.0", + "ini": "^5.0.0", + "init-package-json": "^7.0.2", + "is-cidr": "^5.1.1", + "json-parse-even-better-errors": "^4.0.0", + "libnpmaccess": "^9.0.0", + "libnpmdiff": "^7.0.1", + "libnpmexec": "^9.0.1", + "libnpmfund": "^6.0.1", + "libnpmhook": "^11.0.0", + "libnpmorg": "^7.0.0", + "libnpmpack": "^8.0.1", + "libnpmpublish": "^10.0.1", + "libnpmsearch": "^8.0.0", + "libnpmteam": "^7.0.0", + "libnpmversion": "^7.0.0", + "make-fetch-happen": "^14.0.3", + "minimatch": "^9.0.5", + "minipass": "^7.1.1", + "minipass-pipeline": "^1.2.4", + "ms": "^2.1.2", + "node-gyp": "^11.2.0", + "nopt": "^8.1.0", + "normalize-package-data": "^7.0.0", + "npm-audit-report": "^6.0.0", + "npm-install-checks": "^7.1.1", + "npm-package-arg": "^12.0.2", + "npm-pick-manifest": "^10.0.0", + "npm-profile": "^11.0.1", + "npm-registry-fetch": "^18.0.2", + "npm-user-validate": "^3.0.0", + "p-map": "^7.0.3", + "pacote": "^19.0.1", + "parse-conflict-json": "^4.0.0", + "proc-log": "^5.0.0", + "qrcode-terminal": "^0.12.0", + "read": "^4.1.0", + "semver": "^7.7.2", + "spdx-expression-parse": "^4.0.0", + "ssri": "^12.0.0", + "supports-color": "^9.4.0", + "tar": "^6.2.1", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "treeverse": "^3.0.0", + "validate-npm-package-name": "^6.0.1", + "which": "^5.0.0", + "write-file-atomic": "^6.0.0" + }, + "bin": { + "npm": "bin/npm-cli.js", + "npx": "bin/npx-cli.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui": { + "version": "8.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/npm/node_modules/@isaacs/string-locale-compare": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/@npmcli/agent": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/arborist": { + "version": "8.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/fs": "^4.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/map-workspaces": "^4.0.1", + "@npmcli/metavuln-calculator": "^8.0.0", + "@npmcli/name-from-folder": "^3.0.0", + "@npmcli/node-gyp": "^4.0.0", + "@npmcli/package-json": "^6.0.1", + "@npmcli/query": "^4.0.0", + "@npmcli/redact": "^3.0.0", + "@npmcli/run-script": "^9.0.1", + "bin-links": "^5.0.0", + "cacache": "^19.0.1", + "common-ancestor-path": "^1.0.1", + "hosted-git-info": "^8.0.0", + "json-parse-even-better-errors": "^4.0.0", + "json-stringify-nice": "^1.1.4", + "lru-cache": "^10.2.2", + "minimatch": "^9.0.4", + "nopt": "^8.0.0", + "npm-install-checks": "^7.1.0", + "npm-package-arg": "^12.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.1", + "pacote": "^19.0.0", + "parse-conflict-json": "^4.0.0", + "proc-log": "^5.0.0", + "proggy": "^3.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^3.0.1", + "read-package-json-fast": "^4.0.0", + "semver": "^7.3.7", + "ssri": "^12.0.0", + "treeverse": "^3.0.0", + "walk-up-path": "^3.0.1" + }, + "bin": { + "arborist": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/config": { + "version": "9.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/map-workspaces": "^4.0.1", + "@npmcli/package-json": "^6.0.1", + "ci-info": "^4.0.0", + "ini": "^5.0.0", + "nopt": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "walk-up-path": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/fs": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/git": { + "version": "6.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^8.0.0", + "ini": "^5.0.0", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^10.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/installed-package-contents": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "bin": { + "installed-package-contents": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/map-workspaces": { + "version": "4.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/name-from-folder": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "glob": "^10.2.2", + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { + "version": "8.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cacache": "^19.0.0", + "json-parse-even-better-errors": "^4.0.0", + "pacote": "^20.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote": { + "version": "20.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "@npmcli/run-script": "^9.0.0", + "cacache": "^19.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^12.0.0", + "npm-packlist": "^9.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "sigstore": "^3.0.0", + "ssri": "^12.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/name-from-folder": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/node-gyp": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/package-json": { + "version": "6.2.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^8.0.0", + "json-parse-even-better-errors": "^4.0.0", + "proc-log": "^5.0.0", + "semver": "^7.5.3", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/promise-spawn": { + "version": "8.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/query": { + "version": "4.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/redact": { + "version": "3.2.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/run-script": { + "version": "9.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^4.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "node-gyp": "^11.0.0", + "proc-log": "^5.0.0", + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/@sigstore/protobuf-specs": { + "version": "0.4.3", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@sigstore/tuf": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.4.1", + "tuf-js": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@tufjs/canonical-json": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/abbrev": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/agent-base": { + "version": "7.1.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ansi-styles": { + "version": "6.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/aproba": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/archy": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/bin-links": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cmd-shim": "^7.0.0", + "npm-normalize-package-bin": "^4.0.0", + "proc-log": "^5.0.0", + "read-cmd-shim": "^5.0.0", + "write-file-atomic": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/binary-extensions": { + "version": "2.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/brace-expansion": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/npm/node_modules/cacache": { + "version": "19.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^4.0.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^7.0.2", + "ssri": "^12.0.0", + "tar": "^7.4.3", + "unique-filename": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/chownr": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/mkdirp": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/tar": { + "version": "7.4.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/yallist": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/chalk": { + "version": "5.4.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/npm/node_modules/chownr": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ci-info": { + "version": "4.2.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/cidr-regex": { + "version": "4.1.3", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "ip-regex": "^5.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/cli-columns": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/cmd-shim": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/npm/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/common-ancestor-path": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/cross-spawn": { + "version": "7.0.6", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cssesc": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/debug": { + "version": "4.4.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/npm/node_modules/diff": { + "version": "5.2.0", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/npm/node_modules/eastasianwidth": { + "version": "0.2.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/encoding": { + "version": "0.1.13", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/npm/node_modules/env-paths": { + "version": "2.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/err-code": { + "version": "2.0.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/exponential-backoff": { + "version": "3.1.2", + "dev": true, + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/npm/node_modules/fastest-levenshtein": { + "version": "1.0.16", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/npm/node_modules/foreground-child": { + "version": "3.3.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/fs-minipass": { + "version": "3.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/glob": { + "version": "10.4.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/graceful-fs": { + "version": "4.2.11", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/hosted-git-info": { + "version": "8.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/http-cache-semantics": { + "version": "4.2.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause" + }, + "node_modules/npm/node_modules/http-proxy-agent": { + "version": "7.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/https-proxy-agent": { + "version": "7.0.6", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/iconv-lite": { + "version": "0.6.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/ignore-walk": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/npm/node_modules/ini": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/init-package-json": { + "version": "7.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/package-json": "^6.0.0", + "npm-package-arg": "^12.0.0", + "promzard": "^2.0.0", + "read": "^4.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/ip-address": { + "version": "9.0.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/npm/node_modules/ip-regex": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/is-cidr": { + "version": "5.1.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "cidr-regex": "^4.1.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/jackspeak": { + "version": "3.4.3", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/npm/node_modules/jsbn": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/json-parse-even-better-errors": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/json-stringify-nice": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/jsonparse": { + "version": "1.3.1", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff": { + "version": "6.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff-apply": { + "version": "5.5.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/libnpmaccess": { + "version": "9.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-package-arg": "^12.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmdiff": { + "version": "7.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.1", + "@npmcli/installed-package-contents": "^3.0.0", + "binary-extensions": "^2.3.0", + "diff": "^5.1.0", + "minimatch": "^9.0.4", + "npm-package-arg": "^12.0.0", + "pacote": "^19.0.0", + "tar": "^6.2.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmexec": { + "version": "9.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.1", + "@npmcli/run-script": "^9.0.1", + "ci-info": "^4.0.0", + "npm-package-arg": "^12.0.0", + "pacote": "^19.0.0", + "proc-log": "^5.0.0", + "read": "^4.0.0", + "read-package-json-fast": "^4.0.0", + "semver": "^7.3.7", + "walk-up-path": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmfund": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmhook": { + "version": "11.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmorg": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmpack": { + "version": "8.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.1", + "@npmcli/run-script": "^9.0.1", + "npm-package-arg": "^12.0.0", + "pacote": "^19.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmpublish": { + "version": "10.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "ci-info": "^4.0.0", + "normalize-package-data": "^7.0.0", + "npm-package-arg": "^12.0.0", + "npm-registry-fetch": "^18.0.1", + "proc-log": "^5.0.0", + "semver": "^7.3.7", + "sigstore": "^3.0.0", + "ssri": "^12.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmsearch": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmteam": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmversion": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.1", + "@npmcli/run-script": "^9.0.1", + "json-parse-even-better-errors": "^4.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/lru-cache": { + "version": "10.4.3", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/make-fetch-happen": { + "version": "14.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/agent": "^3.0.0", + "cacache": "^19.0.1", + "http-cache-semantics": "^4.1.1", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^1.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "ssri": "^12.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/make-fetch-happen/node_modules/negotiator": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm/node_modules/minimatch": { + "version": "9.0.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/minipass": { + "version": "7.1.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-collect": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-fetch": { + "version": "4.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/npm/node_modules/minipass-flush": { + "version": "1.0.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline": { + "version": "1.2.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized": { + "version": "1.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minizlib": { + "version": "3.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/mkdirp": { + "version": "1.0.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/mute-stream": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/node-gyp": { + "version": "11.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^14.0.3", + "nopt": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "tar": "^7.4.3", + "tinyglobby": "^0.2.12", + "which": "^5.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/chownr": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/mkdirp": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/tar": { + "version": "7.4.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/yallist": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/nopt": { + "version": "8.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "abbrev": "^3.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/normalize-package-data": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^8.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-audit-report": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-bundled": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-install-checks": { + "version": "7.1.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-normalize-package-bin": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-package-arg": { + "version": "12.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-packlist": { + "version": "9.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "ignore-walk": "^7.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-pick-manifest": { + "version": "10.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^7.1.0", + "npm-normalize-package-bin": "^4.0.0", + "npm-package-arg": "^12.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-profile": { + "version": "11.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-registry-fetch": { + "version": "18.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/redact": "^3.0.0", + "jsonparse": "^1.3.1", + "make-fetch-happen": "^14.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minizlib": "^3.0.1", + "npm-package-arg": "^12.0.0", + "proc-log": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-user-validate": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/p-map": { + "version": "7.0.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/package-json-from-dist": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/npm/node_modules/pacote": { + "version": "19.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "@npmcli/run-script": "^9.0.0", + "cacache": "^19.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^12.0.0", + "npm-packlist": "^9.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "sigstore": "^3.0.0", + "ssri": "^12.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/parse-conflict-json": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "just-diff": "^6.0.0", + "just-diff-apply": "^5.2.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/path-key": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/path-scurry": { + "version": "1.11.1", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/postcss-selector-parser": { + "version": "7.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/proc-log": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/proggy": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/promise-all-reject-late": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-call-limit": { + "version": "3.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-retry": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/promzard": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "read": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/qrcode-terminal": { + "version": "0.12.0", + "dev": true, + "inBundle": true, + "bin": { + "qrcode-terminal": "bin/qrcode-terminal.js" + } + }, + "node_modules/npm/node_modules/read": { + "version": "4.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "mute-stream": "^2.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/read-cmd-shim": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/read-package-json-fast": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/retry": { + "version": "0.12.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm/node_modules/safer-buffer": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true + }, + "node_modules/npm/node_modules/semver": { + "version": "7.7.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/shebang-command": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/signal-exit": { + "version": "4.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/sigstore": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.1.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.4.0", + "@sigstore/sign": "^3.1.0", + "@sigstore/tuf": "^3.1.0", + "@sigstore/verify": "^2.1.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/bundle": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.4.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/core": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/sign": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.1.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.4.0", + "make-fetch-happen": "^14.0.2", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/verify": { + "version": "2.1.1", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.1.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.4.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/smart-buffer": { + "version": "4.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks": { + "version": "2.8.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks-proxy-agent": { + "version": "8.0.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/spdx-correct": { + "version": "3.2.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-correct/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-exceptions": { + "version": "2.5.0", + "dev": true, + "inBundle": true, + "license": "CC-BY-3.0" + }, + "node_modules/npm/node_modules/spdx-expression-parse": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-license-ids": { + "version": "3.0.21", + "dev": true, + "inBundle": true, + "license": "CC0-1.0" + }, + "node_modules/npm/node_modules/sprintf-js": { + "version": "1.1.3", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause" + }, + "node_modules/npm/node_modules/ssri": { + "version": "12.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/supports-color": { + "version": "9.4.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/npm/node_modules/tar": { + "version": "6.2.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/tar/node_modules/fs-minipass": { + "version": "2.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/minizlib": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/text-table": { + "version": "0.2.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/tiny-relative-date": { + "version": "1.3.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/tinyglobby": { + "version": "0.2.14", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/npm/node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.6", + "dev": true, + "inBundle": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/npm/node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/npm/node_modules/treeverse": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/tuf-js": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/models": "3.0.1", + "debug": "^4.3.6", + "make-fetch-happen": "^14.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/tuf-js/node_modules/@tufjs/models": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/unique-filename": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/unique-slug": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/util-deprecate": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/validate-npm-package-license": { + "version": "3.0.4", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-name": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/walk-up-path": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/which": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/which/node_modules/isexe": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/npm/node_modules/wrap-ansi": { + "version": "8.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/write-file-atomic": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-each-series": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-3.0.0.tgz", + "integrity": "sha512-lastgtAdoH9YaLyDa5i5z64q+kzOcQHsQ5SsZJD3q0VEyI8mq872S3geuNbRUQLVAE9siMfgKrpj7MloKFHruw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-filter": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-4.1.0.tgz", + "integrity": "sha512-37/tPdZ3oJwHaS3gNJdenCDB3Tz26i9sjhnguBtvN0vYlRIiDNnvTWkuh+0hETV9rLPdJ3rlL3yVOYPIAnM8rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-map": "^7.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-is-promise": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz", + "integrity": "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-map": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz", + "integrity": "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-reduce": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz", + "integrity": "sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", + "integrity": "sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^2.0.0", + "load-json-file": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true, + "license": "ISC" + }, + "node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "deprecated": "You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other.\n\n(For a CapTP with native promises, see @endo/eventual-send and @endo/captp)", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true, + "license": "ISC" + }, + "node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/read-pkg/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/redeyed": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz", + "integrity": "sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esprima": "~4.0.0" + } + }, + "node_modules/registry-auth-token": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.1.0.tgz", + "integrity": "sha512-GdekYuwLXLxMuFTwAPg5UKGLW/UXzQrZvH/Zj791BQif5T05T0RsaLfHc9q3ZOKi7n+BoprPD9mJ0O0k4xzUlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pnpm/npm-conf": "^2.1.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/semantic-release": { + "version": "22.0.12", + "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-22.0.12.tgz", + "integrity": "sha512-0mhiCR/4sZb00RVFJIUlMuiBkW3NMpVIW2Gse7noqEMoFGkvfPPAImEQbkBV8xga4KOPP4FdTRYuLLy32R1fPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@semantic-release/commit-analyzer": "^11.0.0", + "@semantic-release/error": "^4.0.0", + "@semantic-release/github": "^9.0.0", + "@semantic-release/npm": "^11.0.0", + "@semantic-release/release-notes-generator": "^12.0.0", + "aggregate-error": "^5.0.0", + "cosmiconfig": "^8.0.0", + "debug": "^4.0.0", + "env-ci": "^10.0.0", + "execa": "^8.0.0", + "figures": "^6.0.0", + "find-versions": "^5.1.0", + "get-stream": "^6.0.0", + "git-log-parser": "^1.2.0", + "hook-std": "^3.0.0", + "hosted-git-info": "^7.0.0", + "import-from-esm": "^1.3.1", + "lodash-es": "^4.17.21", + "marked": "^9.0.0", + "marked-terminal": "^6.0.0", + "micromatch": "^4.0.2", + "p-each-series": "^3.0.0", + "p-reduce": "^3.0.0", + "read-pkg-up": "^11.0.0", + "resolve-from": "^5.0.0", + "semver": "^7.3.2", + "semver-diff": "^4.0.0", + "signale": "^1.2.1", + "yargs": "^17.5.1" + }, + "bin": { + "semantic-release": "bin/semantic-release.js" + }, + "engines": { + "node": "^18.17 || >=20.6.1" + } + }, + "node_modules/semantic-release/node_modules/@semantic-release/commit-analyzer": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-11.1.0.tgz", + "integrity": "sha512-cXNTbv3nXR2hlzHjAMgbuiQVtvWHTlwwISt60B+4NZv01y/QRY7p2HcJm8Eh2StzcTJoNnflvKjHH/cjFS7d5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-changelog-angular": "^7.0.0", + "conventional-commits-filter": "^4.0.0", + "conventional-commits-parser": "^5.0.0", + "debug": "^4.0.0", + "import-from-esm": "^1.0.3", + "lodash-es": "^4.17.21", + "micromatch": "^4.0.2" + }, + "engines": { + "node": "^18.17 || >=20.6.1" + }, + "peerDependencies": { + "semantic-release": ">=20.1.0" + } + }, + "node_modules/semantic-release/node_modules/@semantic-release/error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz", + "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/semantic-release/node_modules/@semantic-release/release-notes-generator": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-12.1.0.tgz", + "integrity": "sha512-g6M9AjUKAZUZnxaJZnouNBeDNTCUrJ5Ltj+VJ60gJeDaRRahcHsry9HW8yKrnKkKNkx5lbWiEP1FPMqVNQz8Kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-changelog-angular": "^7.0.0", + "conventional-changelog-writer": "^7.0.0", + "conventional-commits-filter": "^4.0.0", + "conventional-commits-parser": "^5.0.0", + "debug": "^4.0.0", + "get-stream": "^7.0.0", + "import-from-esm": "^1.0.3", + "into-stream": "^7.0.0", + "lodash-es": "^4.17.21", + "read-pkg-up": "^11.0.0" + }, + "engines": { + "node": "^18.17 || >=20.6.1" + }, + "peerDependencies": { + "semantic-release": ">=20.1.0" + } + }, + "node_modules/semantic-release/node_modules/@semantic-release/release-notes-generator/node_modules/get-stream": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-7.0.1.tgz", + "integrity": "sha512-3M8C1EOFN6r8AMUhwUAACIoXZJEOufDU5+0gFFN5uNs6XYOralD2Pqkl7m046va6x77FwposWXbAhPPIOus7mQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/aggregate-error": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz", + "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^5.2.0", + "indent-string": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/clean-stack": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.3.0.tgz", + "integrity": "sha512-9ngPTOhYGQqNVSfeJkYXHmF7AGWp4/nN5D/QqNQs3Dvxd1Kk/WpjHfNujKHYUQ/5CoGyOyFNoWSPk5afzP0QVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "5.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/conventional-changelog-angular": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz", + "integrity": "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/semantic-release/node_modules/conventional-changelog-writer": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-7.0.1.tgz", + "integrity": "sha512-Uo+R9neH3r/foIvQ0MKcsXkX642hdm9odUp7TqgFS7BsalTcjzRlIfWZrZR1gbxOozKucaKt5KAbjW8J8xRSmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-commits-filter": "^4.0.0", + "handlebars": "^4.7.7", + "json-stringify-safe": "^5.0.1", + "meow": "^12.0.1", + "semver": "^7.5.2", + "split2": "^4.0.0" + }, + "bin": { + "conventional-changelog-writer": "cli.mjs" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/semantic-release/node_modules/conventional-commits-filter": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-4.0.0.tgz", + "integrity": "sha512-rnpnibcSOdFcdclpFwWa+pPlZJhXE7l+XK04zxhbWrhgpR96h33QLz8hITTXbcYICxVr3HZFtbtUAQ+4LdBo9A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/semantic-release/node_modules/conventional-commits-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", + "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-text-path": "^2.0.0", + "JSONStream": "^1.3.5", + "meow": "^12.0.1", + "split2": "^4.0.0" + }, + "bin": { + "conventional-commits-parser": "cli.mjs" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/semantic-release/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/semantic-release/node_modules/execa/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/semantic-release/node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/semantic-release/node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/into-stream": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-7.0.0.tgz", + "integrity": "sha512-2dYz766i9HprMBasCMvHMuazJ7u4WzhJwo5kb3iPSiW/iRYV6uPari3zHoqZlnuaR7V1bEiNMxikhp37rdBXbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "from2": "^2.3.0", + "p-is-promise": "^3.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/is-text-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz", + "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "text-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/semantic-release/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/semantic-release/node_modules/meow": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", + "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/normalize-package-data": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/semantic-release/node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/p-reduce": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-3.0.0.tgz", + "integrity": "sha512-xsrIUgI0Kn6iyDYm9StOpOeK29XM1aboGji26+QEortiFST1hGZaUQOLhtEbqHErPpGW/aSz6allwK2qcptp0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/parse-json": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", + "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "index-to-position": "^1.1.0", + "type-fest": "^4.39.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/read-pkg": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz", + "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.3", + "normalize-package-data": "^6.0.0", + "parse-json": "^8.0.0", + "type-fest": "^4.6.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/read-pkg-up": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-11.0.0.tgz", + "integrity": "sha512-LOVbvF1Q0SZdjClSefZ0Nz5z8u+tIE7mV5NibzmE9VYmDe9CaBbAVtz1veOSZbofrdsilxuDAYnFenukZVp8/Q==", + "deprecated": "Renamed to read-package-up", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up-simple": "^1.0.0", + "read-pkg": "^9.0.0", + "type-fest": "^4.6.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semantic-release/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/semantic-release/node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/semantic-release/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/text-extensions": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz", + "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/semver-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz", + "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semver-diff/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-regex": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-4.0.5.tgz", + "integrity": "sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/signale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/signale/-/signale-1.4.0.tgz", + "integrity": "sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^2.3.2", + "figures": "^2.0.0", + "pkg-conf": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/signale/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/signale/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/signale/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/signale/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/signale/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/signale/node_modules/figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/signale/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/signale/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/skin-tone": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", + "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "unicode-emoji-modifier-base": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spawn-error-forwarder": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz", + "integrity": "sha512-gRjMgK5uFjbCvdibeGJuy3I5OYz6VLoVdsOJdA6wV0WlfQVLFueoqMxwwYD9RODdgb6oUIvlRlsyFSiQkMKu0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.22", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz", + "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "dev": true, + "license": "ISC", + "dependencies": { + "readable-stream": "^3.0.0" + } + }, + "node_modules/split2/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/stream-combiner2": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "integrity": "sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "duplexer2": "~0.1.0", + "readable-stream": "^2.0.2" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz", + "integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/temp-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", + "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + } + }, + "node_modules/tempy": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-3.1.0.tgz", + "integrity": "sha512-7jDLIdD2Zp0bDe5r3D2qtkd1QOCacylBuL7oa4udvN6v2pqr4+LcCr67C8DR1zkpaZ8XosF5m1yQSabKAW6f2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-stream": "^3.0.0", + "temp-dir": "^3.0.0", + "type-fest": "^2.12.2", + "unique-string": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/text-extensions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", + "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "3" + } + }, + "node_modules/through2/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/tinyexec": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz", + "integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==", + "dev": true, + "license": "MIT" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/traverse": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.8.tgz", + "integrity": "sha512-aXJDbk6SnumuaZSANd21XAo15ucCDE38H4fkqiGsc3MhCK+wOlZvLP9cB/TvpHT0mOyWgC4Z8EwRlzqYSUzdsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/undici-types": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.13.0.tgz", + "integrity": "sha512-Ov2Rr9Sx+fRgagJ5AX0qvItZG/JKKoBRAVITs1zk7IqZGTJUwgUr7qoYBpWwakpWilTZFM98rG/AFRocu10iIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unicode-emoji-modifier-base": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz", + "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unique-string": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", + "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "crypto-random-string": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universal-user-agent": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", + "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/url-join": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz", + "integrity": "sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.1.tgz", + "integrity": "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json index 15422c3..b707adb 100644 --- a/package.json +++ b/package.json @@ -3,13 +3,34 @@ "version": "0.0.0", "description": "Semantic-release metadata for hexaGenMini", "private": true, - "repository": "https://github.com/hTuneSys/hexaGenMini", + "repository": { + "type": "git", + "url": "git+https://github.com/hTuneSys/hexaGenMini.git" + }, "devDependencies": { - "semantic-release": "^22.0.0", + "@commitlint/cli": "^20.1.0", + "@commitlint/config-conventional": "^20.0.0", "@semantic-release/changelog": "^6.0.0", - "@semantic-release/github": "^9.0.0", - "@semantic-release/git": "^10.0.0", "@semantic-release/commit-analyzer": "^9.0.0", - "@semantic-release/release-notes-generator": "^10.0.0" - } + "@semantic-release/git": "^10.0.0", + "@semantic-release/github": "^9.0.0", + "@semantic-release/release-notes-generator": "^10.0.0", + "husky": "^9.1.7", + "semantic-release": "^22.0.0" + }, + "main": "index.js", + "directories": { + "doc": "docs" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "prepare": "husky" + }, + "keywords": [], + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/hTuneSys/hexaGenMini/issues" + }, + "homepage": "https://github.com/hTuneSys/hexaGenMini#readme" } From ee3b66a1073e7d5cbc4e090c2ef4d342791bd266 Mon Sep 17 00:00:00 2001 From: Husamettin ARABACI Date: Sun, 5 Oct 2025 03:02:26 +0300 Subject: [PATCH 03/16] feat(setup): setted up pre commit hooks (#21) Added reuse prettier commitlint hook 3 --- .github/workflows/ci.yml | 2 ++ .github/workflows/commitlint.yml | 21 --------------------- 2 files changed, 2 insertions(+), 21 deletions(-) delete mode 100644 .github/workflows/commitlint.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e74e57e..9764eeb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,6 +9,8 @@ on: - develop - main - "release/**" + paths: + - "firmware/**" permissions: contents: read diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml deleted file mode 100644 index bd7cdf9..0000000 --- a/.github/workflows/commitlint.yml +++ /dev/null @@ -1,21 +0,0 @@ -# SPDX-FileCopyrightText: 2025 hexaTune LLC -# SPDX-License-Identifier: MIT - -name: 🧐 Commit Lint - -on: - pull_request: - types: [opened, edited, synchronize] - -jobs: - commitlint: - name: Commit Lint (hexaGenMini) - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Lint commits - uses: wagoid/commitlint-github-action@v5 - with: - configFile: .github/.commitlintrc.yml - failOnWarnings: true From 3f6af0fb0b185c9131d131d59467ded49210dbc2 Mon Sep 17 00:00:00 2001 From: Husamettin ARABACI Date: Wed, 15 Oct 2025 15:50:04 +0300 Subject: [PATCH 04/16] feat(async): setted up async tasks (#23) Added rgb usb at async tasks and channel before firmware update --- firmware/Makefile | 2 +- firmware/src/at/at_task.rs | 48 +++++++ firmware/src/at/command.rs | 131 ++++++++++++++++++ firmware/src/at/commands.rs | 22 --- firmware/src/at/dispatcher.rs | 53 +++++++ firmware/src/at/handler.rs | 73 ++++------ firmware/src/at/handlers/mod.rs | 7 + firmware/src/at/handlers/setrgb_handler.rs | 49 +++++++ firmware/src/at/handlers/version_handler.rs | 51 +++++++ firmware/src/at/mod.rs | 79 ++--------- firmware/src/at/parser.rs | 80 ----------- firmware/src/b64/mod.rs | 49 ++++--- firmware/src/channel/channel_manager.rs | 36 +++++ firmware/src/channel/channel_type.rs | 14 ++ firmware/src/channel/message_type.rs | 19 +++ firmware/src/channel/mod.rs | 11 ++ firmware/src/channel/module_type.rs | 10 ++ firmware/src/error/mod.rs | 37 ++--- firmware/src/hexa_config/mod.rs | 4 + firmware/src/main.rs | 90 +++++++++--- firmware/src/rgb/led.rs | 29 ++++ firmware/src/rgb/mod.rs | 7 + firmware/src/rgb/rgb_task.rs | 31 +++++ firmware/src/sysex/mod.rs | 12 +- firmware/src/usb/mod.rs | 128 +---------------- firmware/src/usb/usb_midi.rs | 46 ++++++ firmware/src/usb/usb_task.rs | 91 ++++++++++++ .../hexaGenMini-v1-2025-09-12_001942.zip | Bin 53396 -> 0 bytes .../hexaGenMini-v1-2025-10-09_100137.zip | Bin 0 -> 255361 bytes .../hexaGenMini-v1/hexaGenMini-v1.kicad_prl | 2 +- 30 files changed, 812 insertions(+), 399 deletions(-) create mode 100644 firmware/src/at/at_task.rs create mode 100644 firmware/src/at/command.rs delete mode 100644 firmware/src/at/commands.rs create mode 100644 firmware/src/at/dispatcher.rs create mode 100644 firmware/src/at/handlers/mod.rs create mode 100644 firmware/src/at/handlers/setrgb_handler.rs create mode 100644 firmware/src/at/handlers/version_handler.rs delete mode 100644 firmware/src/at/parser.rs create mode 100644 firmware/src/channel/channel_manager.rs create mode 100644 firmware/src/channel/channel_type.rs create mode 100644 firmware/src/channel/message_type.rs create mode 100644 firmware/src/channel/mod.rs create mode 100644 firmware/src/channel/module_type.rs create mode 100644 firmware/src/hexa_config/mod.rs create mode 100644 firmware/src/rgb/led.rs create mode 100644 firmware/src/rgb/mod.rs create mode 100644 firmware/src/rgb/rgb_task.rs create mode 100644 firmware/src/usb/usb_midi.rs create mode 100644 firmware/src/usb/usb_task.rs delete mode 100644 hardware/hexaGenMini-v1/hexaGenMini-v1-backups/hexaGenMini-v1-2025-09-12_001942.zip create mode 100644 hardware/hexaGenMini-v1/hexaGenMini-v1-backups/hexaGenMini-v1-2025-10-09_100137.zip diff --git a/firmware/Makefile b/firmware/Makefile index 39bb0f2..4f1d84c 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -26,7 +26,7 @@ all: load build: clean @echo "==> Building ($(PROFILE)) to $(CARGO_TARGET_DIR) for $(TRIPLE)" - @$(CARGO) build -p main --target $(TRIPLE) $(BUILD_FLAGS) + @$(CARGO) build --target $(TRIPLE) $(BUILD_FLAGS) elf: build @echo "==> Copying ELF to: $(ELF)" diff --git a/firmware/src/at/at_task.rs b/firmware/src/at/at_task.rs new file mode 100644 index 0000000..f560c63 --- /dev/null +++ b/firmware/src/at/at_task.rs @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: 2025 hexaTune LLC +// SPDX-License-Identifier: MIT + +use defmt::{error, info}; +use embassy_executor::Spawner; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; +use embassy_sync::channel::{Receiver, Sender}; +use {defmt_rtt as _, panic_probe as _}; + +use crate::at::{AtDispatcher, compile_at_error, compile_at_ok}; +use crate::channel::{CAP, Msg, MsgDirection}; + +#[embassy_executor::task] +pub async fn at_task( + dispatcher: &'static AtDispatcher, + spawner: Spawner, + at_rx: Receiver<'static, Cs, Msg, CAP>, + at_tx: Sender<'static, Cs, Msg, CAP>, + usb_tx: Sender<'static, Cs, Msg, CAP>, + rgb_tx: Sender<'static, Cs, Msg, CAP>, +) { + info!("Starting AT task"); + loop { + match at_rx.receive().await { + Msg::AtCmd(MsgDirection::Input, line) => { + if let Some(e) = dispatcher.dispatch(spawner, at_tx, rgb_tx, &line) { + error!("Dispatch error: {:?}", &e.code()); + let compiled = compile_at_error(e); + usb_tx.send(Msg::UsbTxLine(compiled)).await; + } + } + Msg::AtCmd(MsgDirection::Output, line) => { + usb_tx.send(Msg::UsbTxLine(line)).await; + } + Msg::Ok => { + let compiled = compile_at_ok(); + info!("Sending OK from AT task"); + usb_tx.send(Msg::UsbTxLine(compiled)).await; + } + Msg::Err(e) => { + error!("Error Code: {}", &e.code()); + let compiled = compile_at_error(e); + usb_tx.send(Msg::UsbTxLine(compiled)).await; + } + _ => {} + } + } +} diff --git a/firmware/src/at/command.rs b/firmware/src/at/command.rs new file mode 100644 index 0000000..b76c211 --- /dev/null +++ b/firmware/src/at/command.rs @@ -0,0 +1,131 @@ +// SPDX-FileCopyrightText: 2025 hexaTune LLC +// SPDX-License-Identifier: MIT + +use defmt::info; +use heapless::{String, Vec}; + +use crate::b64::{b64_decode, b64_encode}; +use crate::error::Error; + +/// Parsed AT command +pub struct AtCommand { + pub name: String<16>, + pub params: Vec, 8>, + pub is_query: bool, +} + +impl AtCommand { + /// Compile AT command back into string + pub fn compile(&self) -> String<64> { + let mut s = String::<64>::new(); + s.push_str("AT+").unwrap(); + s.push_str(self.name.as_str()).unwrap(); + if self.is_query { + s.push('?').unwrap(); + } else if !self.params.is_empty() { + s.push('=').unwrap(); + for (i, p) in self.params.iter().enumerate() { + if i > 0 { + s.push('#').unwrap(); + } + let encoded = b64_encode(p.as_str()); + s.push_str(&encoded).unwrap(); + } + } + s + } +} + +/// Parse AT command string into name, params, and query flag +pub fn parse(input: &str) -> Result { + let input = input.trim(); + if !input.starts_with("AT+") { + return Err(Error::InvalidCommand); + } + let cmd = &input[3..]; + if let Some(eq_pos) = cmd.find('=') { + let (name, param_str) = cmd.split_at(eq_pos); + let param_str = ¶m_str[1..]; + let mut params = Vec::, 8>::new(); + for p in param_str + .split('#') + .map(|p| p.trim()) + .filter(|p| !p.is_empty()) + { + info!("Parsing param: {}", p); + match b64_decode(p) { + Ok(decoded) => { + info!("Decoded param: {}", decoded.as_str()); + let mut s_hl = String::<16>::new(); + s_hl.push_str(&decoded).map_err(|_| Error::InvalidUtf8)?; + params.push(s_hl).map_err(|_| Error::ParamCount)?; + } + Err(_) => { + info!("Base64 decode failed for param: {}", p); + return Err(Error::InvalidBase64); + } + } + } + Ok(AtCommand { + name: { + let mut n = String::<16>::new(); + n.push_str(name).map_err(|_| Error::InvalidUtf8)?; + n + }, + params, + is_query: false, + }) + } else if let Some(name) = cmd.strip_suffix('?') { + Ok(AtCommand { + name: { + let mut n = String::<16>::new(); + n.push_str(name).map_err(|_| Error::InvalidUtf8)?; + n + }, + params: Vec::, 8>::new(), + is_query: true, + }) + } else { + Ok(AtCommand { + name: { + let mut n = String::<16>::new(); + n.push_str(cmd).map_err(|_| Error::InvalidUtf8)?; + n + }, + params: Vec::, 8>::new(), + is_query: false, + }) + } +} + +pub fn compile_at_ok() -> String<64> { + let at_cmd = AtCommand { + name: { + let mut n = String::<16>::new(); + n.push_str("OK").unwrap(); + n + }, + params: Vec::, 8>::new(), + is_query: false, + }; + at_cmd.compile() +} + +pub fn compile_at_error(e: Error) -> String<64> { + let at_cmd = AtCommand { + name: { + let mut n = String::<16>::new(); + n.push_str("ERROR").unwrap(); + n + }, + params: { + let mut p = Vec::, 8>::new(); + let mut code = String::<16>::new(); + code.push_str(e.code()).unwrap(); + p.push(code).unwrap(); + p + }, + is_query: false, + }; + at_cmd.compile() +} diff --git a/firmware/src/at/commands.rs b/firmware/src/at/commands.rs deleted file mode 100644 index 553ec88..0000000 --- a/firmware/src/at/commands.rs +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-FileCopyrightText: 2025 hexaTune LLC -// SPDX-License-Identifier: MIT - -use crate::at::handler::{SumHandler, VersionHandler}; -use heapless::{LinearMap, String}; - -pub enum Handler { - Version(VersionHandler), - Sum(SumHandler), -} - -pub fn register_all() -> LinearMap, Handler, 8> { - let mut map: LinearMap, Handler, 8> = LinearMap::new(); - let mut version_key = String::<16>::new(); - version_key.push_str("VERSION").unwrap(); - map.insert(version_key, Handler::Version(VersionHandler)) - .ok(); - let mut sum_key = String::<16>::new(); - sum_key.push_str("SUM").unwrap(); - map.insert(sum_key, Handler::Sum(SumHandler)).ok(); - map -} diff --git a/firmware/src/at/dispatcher.rs b/firmware/src/at/dispatcher.rs new file mode 100644 index 0000000..c425fe6 --- /dev/null +++ b/firmware/src/at/dispatcher.rs @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: 2025 hexaTune LLC +// SPDX-License-Identifier: MIT + +use defmt::{error, info}; +use embassy_executor::Spawner; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; +use embassy_sync::channel::Sender; +use heapless::{LinearMap, String}; + +use crate::at::handler::{AtHandler, Handler, register_all}; +use crate::at::parse; +use crate::channel::{CAP, Msg}; +use crate::error::Error; + +pub struct AtDispatcher { + handlers: LinearMap, Handler, 8>, +} + +impl AtDispatcher { + pub fn new() -> Self { + Self { + handlers: register_all(), + } + } + + pub fn dispatch( + &self, + spawner: Spawner, + at_tx: Sender<'static, Cs, Msg, CAP>, + rgb_tx: Sender<'static, Cs, Msg, CAP>, + input: &str, + ) -> Option { + match parse(input) { + Ok(cmd) => match self.handlers.get(&cmd.name) { + Some(handler) => match handler { + Handler::Version(h) => { + info!("Dispatching VERSION command"); + h.handle(spawner, at_tx, &cmd.params, cmd.is_query) + } + Handler::SetRgb(h) => { + info!("Dispatching SETRGB command"); + h.handle(spawner, rgb_tx, &cmd.params, cmd.is_query) + } + }, + None => Some(Error::UnknownCommand), + }, + Err(e) => { + error!("Failed to parse command: {}", &e.code()); + Some(e) + } + } + } +} diff --git a/firmware/src/at/handler.rs b/firmware/src/at/handler.rs index 76c9aa9..06d04b1 100644 --- a/firmware/src/at/handler.rs +++ b/firmware/src/at/handler.rs @@ -1,57 +1,44 @@ // SPDX-FileCopyrightText: 2025 hexaTune LLC // SPDX-License-Identifier: MIT -use core::fmt::Write; +use embassy_executor::Spawner; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; +use embassy_sync::channel::Sender; +use heapless::{LinearMap, String}; + +use crate::at::*; +use crate::channel::{CAP, Msg}; +use crate::error::Error; -/// Trait for AT command handlers pub trait AtHandler { fn handle( &self, - params: &[heapless::String<16>], + spawner: Spawner, + tx: Sender<'static, Cs, Msg, CAP>, + params: &[String<16>], is_query: bool, - ) -> Result, crate::error::Error>; + ) -> Option; } -/// Example: Version query handler -pub struct VersionHandler; - -impl AtHandler for VersionHandler { - fn handle( - &self, - _params: &[heapless::String<16>], - is_query: bool, - ) -> Result, crate::error::Error> { - if is_query { - let mut out = heapless::String::<64>::new(); - out.push_str("1.2.3") - .map_err(|_| crate::error::Error::InvalidUtf8)?; - Ok(out) - } else { - Err(crate::error::Error::NotAQuery) - } - } +pub enum Handler { + Version(VersionHandler), + SetRgb(SetRgbHandler), } -/// Example: Sum handler (takes two params, returns their sum) -pub struct SumHandler; +pub fn register_all() -> LinearMap, Handler, 8> { + let mut map: LinearMap, Handler, 8> = LinearMap::new(); -impl AtHandler for SumHandler { - fn handle( - &self, - params: &[heapless::String<16>], - _is_query: bool, - ) -> Result, crate::error::Error> { - if params.len() != 2 { - return Err(crate::error::Error::ParamCount); - } - let a = params[0] - .parse::() - .map_err(|_| crate::error::Error::ParamValue)?; - let b = params[1] - .parse::() - .map_err(|_| crate::error::Error::ParamValue)?; - let mut out = heapless::String::<64>::new(); - write!(out, "{}", a + b).map_err(|_| crate::error::Error::InvalidUtf8)?; - Ok(out) - } + map.insert( + String::<16>::try_from("VERSION").unwrap(), + Handler::Version(VersionHandler), + ) + .ok(); + + map.insert( + String::<16>::try_from("SETRGB").unwrap(), + Handler::SetRgb(SetRgbHandler), + ) + .ok(); + + map } diff --git a/firmware/src/at/handlers/mod.rs b/firmware/src/at/handlers/mod.rs new file mode 100644 index 0000000..c3d386b --- /dev/null +++ b/firmware/src/at/handlers/mod.rs @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2025 hexaTune LLC +// SPDX-License-Identifier: MIT + +mod version_handler; +pub use version_handler::*; +mod setrgb_handler; +pub use setrgb_handler::*; diff --git a/firmware/src/at/handlers/setrgb_handler.rs b/firmware/src/at/handlers/setrgb_handler.rs new file mode 100644 index 0000000..0425e6e --- /dev/null +++ b/firmware/src/at/handlers/setrgb_handler.rs @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2025 hexaTune LLC +// SPDX-License-Identifier: MIT + +use defmt::{error, info}; +use embassy_executor::Spawner; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; +use embassy_sync::channel::Sender; +use heapless::{String, Vec}; + +use crate::at::{AtCommand, AtHandler, compile_at_error}; +use crate::channel::{CAP, Msg, MsgDirection}; +use crate::error::Error; + +pub struct SetRgbHandler; + +impl AtHandler for SetRgbHandler { + fn handle( + &self, + spawner: Spawner, + rgb_tx: Sender<'static, Cs, Msg, CAP>, + params: &[String<16>], + _is_query: bool, + ) -> Option { + if params.len() != 3 { + error!("SETRGB command requires 3 parameters, got {}", params.len()); + return Some(Error::ParamCount); + } + // check params are u8 + let r = match params[0].parse::() { + Ok(v) => v, + Err(_) => return Some(Error::ParamValue), + }; + let g = match params[1].parse::() { + Ok(v) => v, + Err(_) => return Some(Error::ParamValue), + }; + let b = match params[2].parse::() { + Ok(v) => v, + Err(_) => return Some(Error::ParamValue), + }; + let _ = spawner.spawn(setrgb_task(rgb_tx, r, g, b)); + None + } +} + +#[embassy_executor::task] +async fn setrgb_task(rgb_tx: Sender<'static, Cs, Msg, CAP>, r: u8, g: u8, b: u8) { + rgb_tx.send(Msg::RgbWithValue(r, g, b)).await; +} diff --git a/firmware/src/at/handlers/version_handler.rs b/firmware/src/at/handlers/version_handler.rs new file mode 100644 index 0000000..1a5854d --- /dev/null +++ b/firmware/src/at/handlers/version_handler.rs @@ -0,0 +1,51 @@ +// SPDX-FileCopyrightText: 2025 hexaTune LLC +// SPDX-License-Identifier: MIT + +use defmt::{error, info}; +use embassy_executor::Spawner; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; +use embassy_sync::channel::Sender; +use heapless::{String, Vec}; + +use crate::at::{AtCommand, AtHandler}; +use crate::channel::{CAP, Msg, MsgDirection}; +use crate::error::Error; +use crate::hexa_config::CONF_VERSION; + +pub struct VersionHandler; + +impl AtHandler for VersionHandler { + fn handle( + &self, + spawner: Spawner, + at_tx: Sender<'static, Cs, Msg, CAP>, + _params: &[String<16>], + is_query: bool, + ) -> Option { + if is_query { + let _ = spawner.spawn(version_task(at_tx)); + None + } else { + error!("VERSION command is a query only"); + Some(Error::NotAQuery) + } + } +} + +#[embassy_executor::task] +async fn version_task(at_tx: Sender<'static, Cs, Msg, CAP>) { + let mut name = String::<16>::new(); + name.push_str("VERSION").unwrap(); + + let mut params = Vec::, 8>::new(); + let ver = String::<16>::try_from(CONF_VERSION).unwrap(); + params.push(ver).ok(); + + let compiled = AtCommand { + name, + params, + is_query: false, + } + .compile(); + at_tx.send(Msg::AtCmd(MsgDirection::Output, compiled)).await; +} diff --git a/firmware/src/at/mod.rs b/firmware/src/at/mod.rs index f09ef09..902ff41 100644 --- a/firmware/src/at/mod.rs +++ b/firmware/src/at/mod.rs @@ -1,74 +1,13 @@ // SPDX-FileCopyrightText: 2025 hexaTune LLC // SPDX-License-Identifier: MIT -#![allow(dead_code)] -use core::fmt::Write; - -use heapless::String; - -mod parser; -pub use parser::parse; - +mod command; +pub use command::*; +mod dispatcher; +pub use dispatcher::*; mod handler; -pub use handler::AtHandler; - -mod commands; - -/// AT command dispatcher -pub struct AtDispatcher { - handlers: heapless::LinearMap, crate::at::commands::Handler, 8>, -} - -impl AtDispatcher { - /// Create a new dispatcher with all registered handlers - pub fn new() -> Self { - Self { - handlers: commands::register_all(), - } - } - - /// Dispatch an AT command string to the appropriate handler - pub fn dispatch(&self, input: &str) -> String<64> { - match parse(input) { - Ok(cmd) => match self.handlers.get(&cmd.name) { - Some(handler) => { - let result = match handler { - crate::at::commands::Handler::Version(h) => { - h.handle(&cmd.params, cmd.is_query) - } - crate::at::commands::Handler::Sum(h) => h.handle(&cmd.params, cmd.is_query), - }; - match result { - Ok(resp) => { - let encoded = crate::b64::encode(&resp); - let mut out = String::<64>::new(); - write!(out, "AT+OK={}", encoded).unwrap(); - out - } - Err(e) => { - let msg = e.description(); - let encoded = crate::b64::encode(msg); - let mut out = String::<64>::new(); - write!(out, "AT+ERROR={}", encoded).unwrap(); - out - } - } - } - None => { - let msg = crate::error::Error::UnknownCommand.description(); - let encoded = crate::b64::encode(msg); - let mut out = String::<64>::new(); - write!(out, "AT+ERROR={}", encoded).unwrap(); - out - } - }, - Err(e) => { - let msg = e.description(); - let encoded = crate::b64::encode(msg); - let mut out = String::<64>::new(); - write!(out, "AT+ERROR={}", encoded).unwrap(); - out - } - } - } -} +pub use handler::*; +mod at_task; +pub use at_task::*; +mod handlers; +pub use handlers::*; diff --git a/firmware/src/at/parser.rs b/firmware/src/at/parser.rs deleted file mode 100644 index 4b8dba8..0000000 --- a/firmware/src/at/parser.rs +++ /dev/null @@ -1,80 +0,0 @@ -// SPDX-FileCopyrightText: 2025 hexaTune LLC -// SPDX-License-Identifier: MIT - -use defmt::info; -use heapless::{String, Vec}; - -/// Parsed AT command -pub struct ParsedAtCommand { - pub name: heapless::String<16>, - pub params: heapless::Vec, 8>, - pub is_query: bool, -} - -/// Parse AT command string into name, params, and query flag -pub fn parse(input: &str) -> Result { - let input = input.trim(); - if !input.starts_with("AT+") { - return Err(crate::error::Error::InvalidCommand); - } - let cmd = &input[3..]; - if let Some(eq_pos) = cmd.find('=') { - let (name, param_str) = cmd.split_at(eq_pos); - let param_str = ¶m_str[1..]; - let mut params = Vec::, 8>::new(); - for p in param_str - .split('#') - .map(|p| p.trim()) - .filter(|p| !p.is_empty()) - { - info!("Parsing param: {}", p); - match crate::b64::decode(p) { - Ok(decoded) => { - info!("Decoded param: {}", decoded.as_str()); - let mut s_hl = String::<16>::new(); - s_hl.push_str(&decoded) - .map_err(|_| crate::error::Error::InvalidUtf8)?; - params - .push(s_hl) - .map_err(|_| crate::error::Error::ParamCount)?; - } - Err(_) => { - info!("Base64 decode failed for param: {}", p); - return Err(crate::error::Error::InvalidBase64); - } - } - } - Ok(ParsedAtCommand { - name: { - let mut n = String::<16>::new(); - n.push_str(name) - .map_err(|_| crate::error::Error::InvalidUtf8)?; - n - }, - params, - is_query: false, - }) - } else if let Some(name) = cmd.strip_suffix('?') { - Ok(ParsedAtCommand { - name: { - let mut n = String::<16>::new(); - n.push_str(name) - .map_err(|_| crate::error::Error::InvalidUtf8)?; - n - }, - params: Vec::, 8>::new(), - is_query: true, - }) - } else { - Ok(ParsedAtCommand { - name: { - let mut n = String::<16>::new(); - n.push_str(cmd) - .map_err(|_| crate::error::Error::InvalidUtf8)?; - n - }, - params: Vec::, 8>::new(), - is_query: false, - }) - } -} diff --git a/firmware/src/b64/mod.rs b/firmware/src/b64/mod.rs index 952123d..1760c6b 100644 --- a/firmware/src/b64/mod.rs +++ b/firmware/src/b64/mod.rs @@ -4,26 +4,39 @@ use base64ct::{Base64, Encoding}; use heapless::String; -pub fn decode(input: &str) -> Result, crate::error::Error> { - let mut buf = [0u8; 64]; - match Base64::decode(input, &mut buf) { - Ok(decoded) => match core::str::from_utf8(decoded) { - Ok(s) => { - let mut out = String::<64>::new(); - out.push_str(s) - .map_err(|_| crate::error::Error::InvalidUtf8)?; - Ok(out) - } - Err(_) => Err(crate::error::Error::InvalidUtf8), - }, - Err(_) => Err(crate::error::Error::InvalidBase64), - } +use crate::error::Error; + +pub fn b64_decode(input: &str) -> Result, Error> { + // --- Base64 decode işlemi geçici olarak iptal edildi --- + // let mut buf = [0u8; 64]; + // match Base64::decode(input, &mut buf) { + // Ok(decoded) => match core::str::from_utf8(decoded) { + // Ok(s) => { + // let mut out = String::<64>::new(); + // out.push_str(s).map_err(|_| Error::InvalidUtf8)?; + // Ok(out) + // } + // Err(_) => Err(Error::InvalidUtf8), + // }, + // Err(_) => Err(Error::InvalidBase64), + // } + + // Doğrudan input'u geri döndür + let mut out = String::<64>::new(); + out.push_str(input).map_err(|_| Error::InvalidUtf8)?; + Ok(out) } -pub fn encode(input: &str) -> String<64> { - let mut buf = [0u8; 64]; - let encoded = Base64::encode(input.as_bytes(), &mut buf).unwrap(); +pub fn b64_encode(input: &str) -> String<64> { + // --- Base64 encode işlemi geçici olarak iptal edildi --- + // let mut buf = [0u8; 64]; + // let encoded = Base64::encode(input.as_bytes(), &mut buf).unwrap(); + // let mut out = String::<64>::new(); + // out.push_str(encoded).unwrap(); + // out + + // Doğrudan input'u geri döndür let mut out = String::<64>::new(); - out.push_str(encoded).unwrap(); + out.push_str(input).unwrap(); out } diff --git a/firmware/src/channel/channel_manager.rs b/firmware/src/channel/channel_manager.rs new file mode 100644 index 0000000..aa195fe --- /dev/null +++ b/firmware/src/channel/channel_manager.rs @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: 2025 hexaTune LLC +// SPDX-License-Identifier: MIT + +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; +use embassy_sync::channel::{Channel, Receiver, Sender}; +use heapless::LinearMap; + +use crate::channel::ModuleId; +use crate::channel::Msg; + +pub struct ChannelManager { + senders: LinearMap, MAX_CH>, +} + +impl ChannelManager { + pub const fn new() -> Self { + Self { + senders: LinearMap::new(), + } + } + + pub fn register( + &mut self, + id: ModuleId, + ch: &'static Channel, + ) -> Receiver<'static, Cs, Msg, CAP> { + let tx = ch.sender(); + let rx = ch.receiver(); + let _ = self.senders.insert(id, tx); + rx + } + + pub fn tx(&self, id: ModuleId) -> Option> { + self.senders.get(&id).copied() + } +} diff --git a/firmware/src/channel/channel_type.rs b/firmware/src/channel/channel_type.rs new file mode 100644 index 0000000..85e15bb --- /dev/null +++ b/firmware/src/channel/channel_type.rs @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2025 hexaTune LLC +// SPDX-License-Identifier: MIT + +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; +use embassy_sync::channel::Channel; + +use crate::channel::Msg; + +pub const CAP: usize = 16; + +pub static USB_CH: Channel = Channel::new(); +pub static AT_CH: Channel = Channel::new(); +pub static RGB_CH: Channel = Channel::new(); +pub static DDS_CH: Channel = Channel::new(); diff --git a/firmware/src/channel/message_type.rs b/firmware/src/channel/message_type.rs new file mode 100644 index 0000000..0d46b9e --- /dev/null +++ b/firmware/src/channel/message_type.rs @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2025 hexaTune LLC +// SPDX-License-Identifier: MIT + +use heapless::String; + +use crate::error::Error; + +pub enum MsgDirection { + Input, + Output, +} + +pub enum Msg { + AtCmd(MsgDirection, String<64>), + Ok, + Err(Error), + UsbTxLine(String<64>), + RgbWithValue(u8, u8, u8), +} diff --git a/firmware/src/channel/mod.rs b/firmware/src/channel/mod.rs new file mode 100644 index 0000000..a171e42 --- /dev/null +++ b/firmware/src/channel/mod.rs @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2025 hexaTune LLC +// SPDX-License-Identifier: MIT + +mod channel_type; +pub use channel_type::*; +mod message_type; +pub use message_type::*; +mod module_type; +pub use module_type::*; +mod channel_manager; +pub use channel_manager::*; diff --git a/firmware/src/channel/module_type.rs b/firmware/src/channel/module_type.rs new file mode 100644 index 0000000..d87fa1e --- /dev/null +++ b/firmware/src/channel/module_type.rs @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2025 hexaTune LLC +// SPDX-License-Identifier: MIT + +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub enum ModuleId { + Usb, + At, + Rgb, + Dds, +} diff --git a/firmware/src/error/mod.rs b/firmware/src/error/mod.rs index 5d23991..a4a3239 100644 --- a/firmware/src/error/mod.rs +++ b/firmware/src/error/mod.rs @@ -3,27 +3,30 @@ #[derive(Debug, Clone)] pub enum Error { - InvalidCommand, // "Not an AT command" - InvalidBase64, // "Invalid base64 param" - InvalidUtf8, // "Invalid UTF-8 in param" - InvalidSysEx, // "Invalid SysEx" - ParamCount, // "Param count" - ParamValue, // "Param value" - NotAQuery, // "Not a query" - UnknownCommand, // "Unknown command" + InvalidCommand, // "Not an AT command" + InvalidBase64, // "Invalid base64 param" + InvalidUtf8, // "Invalid UTF-8 in param" + InvalidSysEx, // "Invalid SysEx" + InvalidDataLenght, // "Invalid data length" + ParamCount, // "Param count" + ParamValue, // "Param value" + NotAQuery, // "Not a query" + UnknownCommand, // "Unknown command" } impl Error { - pub fn description(&self) -> &'static str { + pub fn code(&self) -> &'static str { match self { - Error::InvalidCommand => "Not an AT command", - Error::InvalidBase64 => "Invalid base64 param", - Error::InvalidUtf8 => "Invalid UTF-8 in param", - Error::InvalidSysEx => "Invalid SysEx", - Error::ParamCount => "Invalid param count", - Error::ParamValue => "Invalid param value", - Error::NotAQuery => "Not a query", - Error::UnknownCommand => "Unknown command", + // Command errors + Error::InvalidCommand => "E001001", + Error::InvalidBase64 => "E001002", + Error::InvalidUtf8 => "E001003", + Error::InvalidSysEx => "E001004", + Error::InvalidDataLenght => "E001005", + Error::ParamCount => "E001006", + Error::ParamValue => "E001007", + Error::NotAQuery => "E001008", + Error::UnknownCommand => "E001009", } } } diff --git a/firmware/src/hexa_config/mod.rs b/firmware/src/hexa_config/mod.rs new file mode 100644 index 0000000..b5677df --- /dev/null +++ b/firmware/src/hexa_config/mod.rs @@ -0,0 +1,4 @@ +// SPDX-FileCopyrightText: 2025 hexaTune LLC +// SPDX-License-Identifier: MIT + +pub const CONF_VERSION: &str = "v1.0.0"; diff --git a/firmware/src/main.rs b/firmware/src/main.rs index 6521e78..f610d4a 100644 --- a/firmware/src/main.rs +++ b/firmware/src/main.rs @@ -5,42 +5,100 @@ #![no_main] use defmt::info; -use embassy_executor::Spawner; -use embassy_rp::bind_interrupts; -use embassy_rp::gpio; -use embassy_rp::peripherals::USB; -use embassy_rp::usb::Driver; -use embassy_rp::usb::InterruptHandler; -use embassy_time::Timer; -use gpio::{Level, Output}; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; +use embassy_sync::mutex::Mutex; use {defmt_rtt as _, panic_probe as _}; mod at; mod b64; +mod channel; mod error; +mod hexa_config; +mod rgb; mod sysex; mod usb; -bind_interrupts!(struct Irqs { - USBCTRL_IRQ => InterruptHandler; +embassy_rp::bind_interrupts!(struct IrqUsb { + USBCTRL_IRQ => embassy_rp::usb::InterruptHandler; }); +embassy_rp::bind_interrupts!(struct IrqPio { + PIO0_IRQ_0 => embassy_rp::pio::InterruptHandler; +}); + +static CHANNEL_MANAGER: static_cell::StaticCell> = + static_cell::StaticCell::new(); + #[embassy_executor::main] -async fn main(spawner: Spawner) { +async fn main(spawner: embassy_executor::Spawner) { + //Initialize the RP2040 let p = embassy_rp::init(Default::default()); info!("Starting hexaGenMini firmware"); - let driver = Driver::new(p.USB, Irqs); + + //Create the channel manager + info!("Initializing channel manager"); + let mut mgr = channel::ChannelManager::<{ channel::CAP }, 8>::new(); + let usb_rx = mgr.register(channel::ModuleId::Usb, &channel::USB_CH); + let at_rx = mgr.register(channel::ModuleId::At, &channel::AT_CH); + let rgb_rx = mgr.register(channel::ModuleId::Rgb, &channel::RGB_CH); + let _dds_rx = mgr.register(channel::ModuleId::Dds, &channel::DDS_CH); + let mgr_ref: &'static channel::ChannelManager<{ channel::CAP }, 8> = CHANNEL_MANAGER.init(mgr); + let at_tx = mgr_ref.tx(channel::ModuleId::At).unwrap(); + let usb_tx = mgr_ref.tx(channel::ModuleId::Usb).unwrap(); + let rgb_tx = mgr_ref.tx(channel::ModuleId::Rgb).unwrap(); + //let dds_tx = mgr_ref.tx(channel::ModuleId::Dds).unwrap(); + + //USB module + info!("Initializing USB"); + let driver = embassy_rp::usb::Driver::new(p.USB, IrqUsb); let usb::UsbMidi { device, midi } = usb::init(driver); + static MIDI_CELL: static_cell::StaticCell>> = + static_cell::StaticCell::new(); + let midi_mutex: &'static Mutex> = + MIDI_CELL.init(Mutex::new(midi)); + + //AT module + info!("Initializing AT command dispatcher"); static DISPATCHER: static_cell::StaticCell = static_cell::StaticCell::new(); let dispatcher = DISPATCHER.init(at::AtDispatcher::new()); + + //Led module + info!("Initializing RGB LED"); + let embassy_rp::pio::Pio { + mut common, sm0, .. + } = embassy_rp::pio::Pio::new(p.PIO0, IrqPio); + let program = embassy_rp::pio_programs::ws2812::PioWs2812Program::new(&mut common); + let mut ws2812 = embassy_rp::pio_programs::ws2812::PioWs2812::new( + &mut common, + sm0, + p.DMA_CH0, + p.PIN_23, + &program, + ); + let mut rgb_led = rgb::RgbLed::new(ws2812); + + //Spawn tasks + info!("Spawning tasks"); + spawner + .spawn(at::at_task( + dispatcher, spawner, at_rx, at_tx, usb_tx, rgb_tx, + )) + .unwrap(); spawner.spawn(usb::dev_task(device)).unwrap(); - spawner.spawn(usb::usb_io_task(midi, dispatcher)).unwrap(); + spawner + .spawn(usb::usb_io_task(midi_mutex, usb_rx, at_tx)) + .unwrap(); + spawner + .spawn(rgb::rgb_task(rgb_led, rgb_rx, at_tx)) + .unwrap(); - let mut led = Output::new(p.PIN_25, Level::Low); + //Dummy task to blink the LED + let mut led = embassy_rp::gpio::Output::new(p.PIN_25, embassy_rp::gpio::Level::Low); loop { + info!("Blink!"); led.set_high(); - Timer::after_secs(1).await; + embassy_time::Timer::after_secs(5).await; led.set_low(); - Timer::after_secs(1).await; + embassy_time::Timer::after_secs(5).await; } } diff --git a/firmware/src/rgb/led.rs b/firmware/src/rgb/led.rs new file mode 100644 index 0000000..e1de1a4 --- /dev/null +++ b/firmware/src/rgb/led.rs @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2025 hexaTune LLC +// SPDX-License-Identifier: MIT + +use embassy_rp::peripherals::PIO0; +use embassy_rp::pio_programs::ws2812::PioWs2812; +use smart_leds::RGB8; + +pub struct RgbLed { + led: PioWs2812<'static, PIO0, 0, 1>, + buf: [RGB8; 1], +} + +impl RgbLed { + pub fn new(led: PioWs2812<'static, PIO0, 0, 1>) -> Self { + Self { + led, + buf: [RGB8 { r: 0, g: 0, b: 0 }], + } + } + + pub async fn set_rgb(&mut self, r: u8, g: u8, b: u8) { + self.buf[0] = RGB8 { r, g, b }; + self.led.write(&self.buf).await; + } + + pub async fn off(&mut self) { + self.set_rgb(0, 0, 0).await; + } +} diff --git a/firmware/src/rgb/mod.rs b/firmware/src/rgb/mod.rs new file mode 100644 index 0000000..1d21697 --- /dev/null +++ b/firmware/src/rgb/mod.rs @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2025 hexaTune LLC +// SPDX-License-Identifier: MIT + +mod rgb_task; +pub use rgb_task::*; +mod led; +pub use led::*; diff --git a/firmware/src/rgb/rgb_task.rs b/firmware/src/rgb/rgb_task.rs new file mode 100644 index 0000000..8c19db4 --- /dev/null +++ b/firmware/src/rgb/rgb_task.rs @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: 2025 hexaTune LLC +// SPDX-License-Identifier: MIT + +use defmt::info; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; +use embassy_sync::channel::{Receiver, Sender}; +use {defmt_rtt as _, panic_probe as _}; + +use crate::channel::{CAP, Msg}; +use crate::rgb::RgbLed; + +#[embassy_executor::task] +pub async fn rgb_task( + mut rgb_led: RgbLed, + rgb_rx: Receiver<'static, Cs, Msg, CAP>, + at_tx: Sender<'static, Cs, Msg, CAP>, +) { + info!("Starting RGB task"); + loop { + match rgb_rx.receive().await { + Msg::RgbWithValue(r, g, b) => { + info!("Setting RGB to ({}, {}, {})", r, g, b); + rgb_led.set_rgb(r, g, b).await; + info!("RGB set"); + info!("Sending OK from RGB task"); + at_tx.send(Msg::Ok).await; + } + _ => {} + } + } +} diff --git a/firmware/src/sysex/mod.rs b/firmware/src/sysex/mod.rs index ebddb38..11eb6a3 100644 --- a/firmware/src/sysex/mod.rs +++ b/firmware/src/sysex/mod.rs @@ -1,10 +1,10 @@ // SPDX-FileCopyrightText: 2025 hexaTune LLC // SPDX-License-Identifier: MIT -use heapless::Vec as HVec; +use heapless::Vec; -pub fn build_sysex(payload: &str) -> Option> { - let mut out: HVec = HVec::new(); +pub fn build_sysex(payload: &str) -> Option> { + let mut out: Vec = Vec::new(); // SysEx start out.push(0xF0).ok()?; @@ -20,8 +20,7 @@ pub fn build_sysex(payload: &str) -> Option> { Some(out) } -pub fn sysex_to_usb_midi_packets(sysex: &[u8]) -> heapless::Vec<[u8; 4], M> { - use heapless::Vec; +pub fn sysex_to_usb_midi_packets(sysex: &[u8]) -> Vec<[u8; 4], M> { let mut out: Vec<[u8; 4], M> = Vec::new(); let mut i = 0usize; @@ -46,8 +45,7 @@ pub fn sysex_to_usb_midi_packets(sysex: &[u8]) -> heapless::Vec< out } -pub fn extract_sysex_payload(data: &[u8]) -> Option> { - use heapless::Vec; +pub fn extract_sysex_payload(data: &[u8]) -> Option> { let mut out: Vec = Vec::new(); for chunk in data.chunks_exact(4) { diff --git a/firmware/src/usb/mod.rs b/firmware/src/usb/mod.rs index 96183f9..f620d7d 100644 --- a/firmware/src/usb/mod.rs +++ b/firmware/src/usb/mod.rs @@ -1,127 +1,7 @@ // SPDX-FileCopyrightText: 2025 hexaTune LLC // SPDX-License-Identifier: MIT -use core::fmt::Write; -use defmt::info; -use embassy_usb::class::midi::MidiClass; -use embassy_usb::{Builder, Config}; -use static_cell::StaticCell; -use {defmt_rtt as _, panic_probe as _}; - -pub type MyDriver<'d> = embassy_rp::usb::Driver<'d, embassy_rp::peripherals::USB>; -pub type MyUsbDevice<'d> = embassy_usb::UsbDevice<'d, MyDriver<'d>>; -pub type MyMidiClass<'d> = embassy_usb::class::midi::MidiClass<'d, MyDriver<'d>>; - -pub struct UsbMidi { - pub device: MyUsbDevice<'static>, - pub midi: MyMidiClass<'static>, -} - -pub fn init(driver: MyDriver<'static>) -> UsbMidi { - let mut cfg = Config::new(0x2E8A, 0x0010); - cfg.manufacturer = Some("hexaTune"); - cfg.product = Some("hexaGenMini MIDI"); - cfg.serial_number = Some("HTS-0001"); - cfg.max_power = 100; - cfg.max_packet_size_0 = 64; - //cfg.device_class = 0x00; - //cfg.device_sub_class = 0x00; - //cfg.device_protocol = 0x00; - - static CONFIG_DESC: StaticCell<[u8; 256]> = StaticCell::new(); - static BOS_DESC: StaticCell<[u8; 256]> = StaticCell::new(); - static MS_OS_DESC: StaticCell<[u8; 256]> = StaticCell::new(); - static CONTROL_BUF: StaticCell<[u8; 64]> = StaticCell::new(); - - let config_desc = CONFIG_DESC.init([0; 256]); - let bos_desc = BOS_DESC.init([0; 256]); - let ms_os_desc = MS_OS_DESC.init([0; 256]); - let control_buf = CONTROL_BUF.init([0; 64]); - - let mut builder = Builder::new(driver, cfg, config_desc, bos_desc, ms_os_desc, control_buf); - - let midi = MidiClass::new(&mut builder, 1, 1, 64); - - let dev = builder.build(); - UsbMidi { device: dev, midi } -} - -#[embassy_executor::task] -pub async fn dev_task(mut dev: MyUsbDevice<'static>) { - dev.run().await; -} - -#[embassy_executor::task] -pub async fn usb_io_task( - mut midi: MyMidiClass<'static>, - dispatcher: &'static crate::at::AtDispatcher, -) { - let mut buf = [0; 64]; - loop { - let n = midi.read_packet(&mut buf).await.unwrap(); - let data = &buf[..n]; - info!("Received MIDI packet: {:?}", data); - if let Some(payload) = crate::sysex::extract_sysex_payload(data) { - if let Ok(input) = core::str::from_utf8(&payload) { - let response = dispatcher.dispatch(input); - info!("Received AT command: {}", input); - info!("AT response: {}", response.as_str()); - if let Some(sysex) = crate::sysex::build_sysex::<64>(&response) { - let packets = crate::sysex::sysex_to_usb_midi_packets::<64>(&sysex); - for pkt in packets.iter() { - match midi.write_packet(pkt).await { - Ok(_) => { - info!("Sent AT response via SysEx"); - } - Err(e) => { - info!("Failed to send MIDI packet: {:?}", e); - } - } - } - } else { - info!("Response too long to fit in SysEx"); - } - } else { - let error = crate::error::Error::InvalidUtf8.description(); - let encoded = crate::b64::encode(error); - let mut response = heapless::String::<64>::new(); - write!(response, "AT+ERROR={}", encoded).unwrap(); - if let Some(sysex) = crate::sysex::build_sysex::<64>(&response) { - let packets = crate::sysex::sysex_to_usb_midi_packets::<64>(&sysex); - for pkt in packets.iter() { - match midi.write_packet(pkt).await { - Ok(_) => { - info!("Sent AT response via SysEx"); - } - Err(e) => { - info!("Failed to send MIDI packet: {:?}", e); - } - } - } - } else { - info!("Error response too long to fit in SysEx"); - } - } - } else { - let error = crate::error::Error::InvalidSysEx.description(); - let encoded = crate::b64::encode(error); - let mut response = heapless::String::<64>::new(); - write!(response, "AT+ERROR={}", encoded).unwrap(); - if let Some(sysex) = crate::sysex::build_sysex::<64>(&response) { - let packets = crate::sysex::sysex_to_usb_midi_packets::<64>(&sysex); - for pkt in packets.iter() { - match midi.write_packet(pkt).await { - Ok(_) => { - info!("Sent AT response via SysEx"); - } - Err(e) => { - info!("Failed to send MIDI packet: {:?}", e); - } - } - } - } else { - info!("Error response too long to fit in SysEx"); - } - } - } -} +mod usb_midi; +pub use usb_midi::*; +mod usb_task; +pub use usb_task::*; diff --git a/firmware/src/usb/usb_midi.rs b/firmware/src/usb/usb_midi.rs new file mode 100644 index 0000000..83481c3 --- /dev/null +++ b/firmware/src/usb/usb_midi.rs @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: 2025 hexaTune LLC +// SPDX-License-Identifier: MIT + +use embassy_usb::class::midi::MidiClass; +use embassy_usb::{Builder, Config}; +use static_cell::StaticCell; + +use {defmt_rtt as _, panic_probe as _}; + +pub type MyDriver<'d> = embassy_rp::usb::Driver<'d, embassy_rp::peripherals::USB>; +pub type MyUsbDevice<'d> = embassy_usb::UsbDevice<'d, MyDriver<'d>>; +pub type MyMidiClass<'d> = embassy_usb::class::midi::MidiClass<'d, MyDriver<'d>>; + +pub struct UsbMidi { + pub device: MyUsbDevice<'static>, + pub midi: MyMidiClass<'static>, +} + +pub fn init(driver: MyDriver<'static>) -> UsbMidi { + let mut cfg = Config::new(0x2E8A, 0x0010); + cfg.manufacturer = Some("hexaTune"); + cfg.product = Some("hexaGenMini MIDI"); + cfg.serial_number = Some("HTS-0001"); + cfg.max_power = 100; + cfg.max_packet_size_0 = 64; + //cfg.device_class = 0x00; + //cfg.device_sub_class = 0x00; + //cfg.device_protocol = 0x00; + + static CONFIG_DESC: StaticCell<[u8; 256]> = StaticCell::new(); + static BOS_DESC: StaticCell<[u8; 256]> = StaticCell::new(); + static MS_OS_DESC: StaticCell<[u8; 256]> = StaticCell::new(); + static CONTROL_BUF: StaticCell<[u8; 64]> = StaticCell::new(); + + let config_desc = CONFIG_DESC.init([0; 256]); + let bos_desc = BOS_DESC.init([0; 256]); + let ms_os_desc = MS_OS_DESC.init([0; 256]); + let control_buf = CONTROL_BUF.init([0; 64]); + + let mut builder = Builder::new(driver, cfg, config_desc, bos_desc, ms_os_desc, control_buf); + + let midi = MidiClass::new(&mut builder, 1, 1, 64); + + let dev = builder.build(); + UsbMidi { device: dev, midi } +} diff --git a/firmware/src/usb/usb_task.rs b/firmware/src/usb/usb_task.rs new file mode 100644 index 0000000..2444f88 --- /dev/null +++ b/firmware/src/usb/usb_task.rs @@ -0,0 +1,91 @@ +// SPDX-FileCopyrightText: 2025 hexaTune LLC +// SPDX-License-Identifier: MIT + +use defmt::{error, info}; +use embassy_futures::select::{Either, select}; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; +use embassy_sync::channel::{Receiver, Sender}; +use embassy_sync::mutex::Mutex; + +use crate::channel::{CAP, Msg, MsgDirection}; +use crate::error::Error; +use crate::sysex::{build_sysex, extract_sysex_payload, sysex_to_usb_midi_packets}; +use crate::usb::{MyMidiClass, MyUsbDevice}; + +#[embassy_executor::task] +pub async fn dev_task(mut dev: MyUsbDevice<'static>) { + info!("Starting USB device task"); + dev.run().await; +} + +#[embassy_executor::task] +pub async fn usb_io_task( + midi: &'static Mutex>, + mut usb_rx: Receiver<'static, Cs, Msg, CAP>, + at_tx: Sender<'static, Cs, Msg, CAP>, +) { + info!("Starting unified USB IO task"); + + loop { + let read_fut = async { + let mut buf = [0u8; 64]; + let n = { + let mut m = midi.lock().await; + m.read_packet(&mut buf).await.unwrap() + }; + (n, buf) + }; + + let tx_fut = async { usb_rx.receive().await }; + + match select(read_fut, tx_fut).await { + // USB'DEN OKUNDU → AT'ye gönder + Either::First((n, buf)) => { + let data = &buf[..n]; + info!("Received MIDI packet: {:?}", data); + + if let Some(payload) = extract_sysex_payload(data) { + if let Ok(input) = core::str::from_utf8(&payload) { + match heapless::String::<64>::try_from(input) { + Ok(line) => { + at_tx.send(Msg::AtCmd(MsgDirection::Input, line)).await; + } + Err(_) => { + error!("Error Code: {}", Error::InvalidDataLenght.code()); + at_tx.send(Msg::Err(Error::InvalidDataLenght)).await; + } + } + } else { + error!("Error Code: {}", Error::InvalidUtf8.code()); + at_tx.send(Msg::Err(Error::InvalidUtf8)).await; + } + } else { + error!("Error Code: {}", Error::InvalidSysEx.code()); + at_tx.send(Msg::Err(Error::InvalidSysEx)).await; + } + } + + Either::Second(msg) => match msg { + Msg::UsbTxLine(line) => { + if let Some(sysex) = build_sysex::<64>(&line) { + let packets = sysex_to_usb_midi_packets::<64>(&sysex); + info!("Sending {} MIDI packets", packets.len()); + + for pkt in packets.iter() { + info!("Sending MIDI packet: {:?}", pkt); + let mut m = midi.lock().await; + if let Err(e) = m.write_packet(pkt).await { + error!("USB write error: {:?}", e); + } + } + } else { + error!("UsbTxLine too long to fit into SysEx"); + } + } + _ => { + info!("USB not TX line"); + } + }, + } + } +} diff --git a/hardware/hexaGenMini-v1/hexaGenMini-v1-backups/hexaGenMini-v1-2025-09-12_001942.zip b/hardware/hexaGenMini-v1/hexaGenMini-v1-backups/hexaGenMini-v1-2025-09-12_001942.zip deleted file mode 100644 index f63a3c5d9f4bda35b39516aee35979f63123fa69..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53396 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%0Ar0IYDaR+9P7t}6|ZZ%`=G(6P{B&R04{UjB(a9R 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 Date: Wed, 15 Oct 2025 22:22:59 +0300 Subject: [PATCH 05/16] feat(firmware): added basic firmware update (#24) Added simple bootsel FWUPDATE command to At --- firmware/Cargo.lock | 986 ++----------------- firmware/Cargo.toml | 49 +- firmware/src/at/at_task.rs | 2 +- firmware/src/at/dispatcher.rs | 9 + firmware/src/at/handler.rs | 14 + firmware/src/at/handlers/fwupdate_handler.rs | 37 + firmware/src/at/handlers/mod.rs | 4 + firmware/src/at/handlers/reset_handler.rs | 26 + firmware/src/at/handlers/setrgb_handler.rs | 8 +- firmware/src/at/handlers/version_handler.rs | 2 +- firmware/src/b64/mod.rs | 6 +- firmware/src/error/mod.rs | 32 +- firmware/src/main.rs | 22 +- firmware/src/usb/usb_task.rs | 4 +- 14 files changed, 209 insertions(+), 992 deletions(-) create mode 100644 firmware/src/at/handlers/fwupdate_handler.rs create mode 100644 firmware/src/at/handlers/reset_handler.rs diff --git a/firmware/Cargo.lock b/firmware/Cargo.lock index a7b0df4..34559ee 100644 --- a/firmware/Cargo.lock +++ b/firmware/Cargo.lock @@ -2,41 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "aead" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" -dependencies = [ - "crypto-common", - "generic-array 0.14.7", -] - -[[package]] -name = "aes" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - -[[package]] -name = "aes-gcm" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" -dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", - "subtle", -] - [[package]] name = "ahash" version = "0.8.12" @@ -64,18 +29,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" -[[package]] -name = "as-slice" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45403b49e3954a4b8428a0ac21a4b7afadccf92bfd96273f1a58cd4812496ae0" -dependencies = [ - "generic-array 0.12.4", - "generic-array 0.13.3", - "generic-array 0.14.7", - "stable_deref_trait", -] - [[package]] name = "ascii-canvas" version = "4.0.0" @@ -85,22 +38,6 @@ dependencies = [ "term", ] -[[package]] -name = "assign-resources" -version = "0.4.0" -source = "git+https://github.com/adamgreig/assign-resources?rev=bd22cb7a92031fb16f74a5da42469d466c33383e#bd22cb7a92031fb16f74a5da42469d466c33383e" - -[[package]] -name = "async-trait" -version = "0.1.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - [[package]] name = "atomic-polyfill" version = "1.0.3" @@ -131,23 +68,11 @@ dependencies = [ "rustc_version", ] -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - [[package]] name = "base64" -version = "0.21.7" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - -[[package]] -name = "base64ct" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bit-set" @@ -194,49 +119,9 @@ version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "bt-hci" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a496a585c0e2e00195e558af18342f79afd67598d3edb32e78c006e606a31386" -dependencies = [ - "btuuid", - "defmt 1.0.1", - "embassy-sync", - "embedded-io", - "embedded-io-async", - "futures-intrusive", - "heapless 0.8.0", -] - -[[package]] -name = "btuuid" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0acfef8a77a02866e04f7e2ad3f4c7b32d575696c49c4bbad742b4aecb8e4a3" -dependencies = [ - "defmt 0.3.100", -] - -[[package]] -name = "buffered-io" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5698b2eda4613b62f3aa3119805df1ca6739e00167a2600b3a234ac49b14803" -dependencies = [ - "embedded-io", - "embedded-io-async", + "generic-array", ] -[[package]] -name = "byte-slice-cast" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" - [[package]] name = "bytemuck" version = "1.23.2" @@ -255,16 +140,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - [[package]] name = "codespan-reporting" version = "0.11.1" @@ -275,12 +150,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - [[package]] name = "cortex-m" version = "0.7.7" @@ -343,70 +212,16 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" -[[package]] -name = "crypto-bigint" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" -dependencies = [ - "generic-array 0.14.7", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - [[package]] name = "crypto-common" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array 0.14.7", + "generic-array", "typenum", ] -[[package]] -name = "ctr" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" -dependencies = [ - "cipher", -] - -[[package]] -name = "cyw43" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85d050df7c6c6d253d1044fd5f011873ec6f434f77c202e7e7e88d5b12a1e943" -dependencies = [ - "bt-hci", - "cortex-m", - "cortex-m-rt", - "defmt 1.0.1", - "embassy-futures", - "embassy-net-driver-channel", - "embassy-sync", - "embassy-time", - "embedded-hal 1.0.0", - "embedded-io-async", - "futures", - "heapless 0.8.0", - "num_enum 0.5.11", -] - -[[package]] -name = "cyw43-pio" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86bd7dea4d32a73557be29bfadaaa916e758115ed8b42cfca4fda0a111f07644" -dependencies = [ - "cyw43", - "defmt 1.0.1", - "embassy-rp", - "fixed", -] - [[package]] name = "darling" version = "0.20.11" @@ -448,15 +263,6 @@ version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f578e8e2c440e7297e008bb5486a3a8a194775224bbc23729b0dbdfaeebf162e" -[[package]] -name = "defmt" -version = "0.3.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0963443817029b2024136fc4dd07a5107eb8f977eaf18fcd1fdeb11306b64ad" -dependencies = [ - "defmt 1.0.1", -] - [[package]] name = "defmt" version = "1.0.1" @@ -496,17 +302,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2cac3b8a5644a9e02b75085ebad3b6deafdbdbdec04bb25086523828aa4dfd1" dependencies = [ "critical-section", - "defmt 1.0.1", -] - -[[package]] -name = "der" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" -dependencies = [ - "const-oid", - "zeroize", + "defmt", ] [[package]] @@ -517,25 +313,6 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", - "subtle", -] - -[[package]] -name = "display-interface" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba2aab1ef3793e6f7804162debb5ac5edb93b3d650fbcc5aeb72fcd0e6c03a0" - -[[package]] -name = "display-interface-spi" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f86b9ec30048b1955da2038fcc3c017f419ab21bb0001879d16c0a3749dc6b7a" -dependencies = [ - "byte-slice-cast", - "display-interface", - "embedded-hal 1.0.0", - "embedded-hal-async", ] [[package]] @@ -554,22 +331,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] -name = "elliptic-curve" -version = "0.13.8" +name = "embassy-boot" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +checksum = "21b8138aeafd7dd004d77bbf42a581060a018112ffaa3ffc1996da5d3867f345" dependencies = [ - "base16ct", - "crypto-bigint", "digest", - "ff", - "generic-array 0.14.7", - "group", - "hkdf", - "rand_core 0.6.4", - "sec1", - "subtle", - "zeroize", + "embassy-embedded-hal", + "embassy-sync", + "embedded-storage", + "embedded-storage-async", + "signature", +] + +[[package]] +name = "embassy-boot-rp" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0765ebdfff04f57e59ed305c9a6ad721f66da0b1f9fc04718ebbc2e6cf23cc09" +dependencies = [ + "cfg-if", + "cortex-m", + "cortex-m-rt", + "embassy-boot", + "embassy-rp", + "embassy-sync", + "embassy-time", + "embedded-storage", + "embedded-storage-async", ] [[package]] @@ -578,7 +367,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "554e3e840696f54b4c9afcf28a0f24da431c927f4151040020416e7393d6d0d8" dependencies = [ - "defmt 1.0.1", + "defmt", "embassy-futures", "embassy-hal-internal", "embassy-sync", @@ -598,7 +387,7 @@ checksum = "06070468370195e0e86f241c8e5004356d696590a678d47d6676795b2e439c6b" dependencies = [ "cortex-m", "critical-section", - "defmt 1.0.1", + "defmt", "document-features", "embassy-executor-macros", "embassy-executor-timer-queue", @@ -636,36 +425,15 @@ checksum = "95285007a91b619dc9f26ea8f55452aa6c60f7115a4edc05085cd2bd3127cd7a" dependencies = [ "cortex-m", "critical-section", - "defmt 1.0.1", + "defmt", "num-traits", ] -[[package]] -name = "embassy-net" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0558a231a47e7d4a06a28b5278c92e860f1200f24821d2f365a2f40fe3f3c7b2" -dependencies = [ - "defmt 1.0.1", - "document-features", - "embassy-net-driver", - "embassy-sync", - "embassy-time", - "embedded-io-async", - "embedded-nal-async", - "heapless 0.8.0", - "managed", - "smoltcp", -] - [[package]] name = "embassy-net-driver" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524eb3c489760508f71360112bca70f6e53173e6fe48fc5f0efd0f5ab217751d" -dependencies = [ - "defmt 0.3.100", -] [[package]] name = "embassy-net-driver-channel" @@ -678,20 +446,6 @@ dependencies = [ "embassy-sync", ] -[[package]] -name = "embassy-net-wiznet" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3035678b1fcb8322bbcc3ee9a32ea3ed04e6e388528057abd11ee8382a63abdf" -dependencies = [ - "defmt 1.0.1", - "embassy-futures", - "embassy-net-driver-channel", - "embassy-time", - "embedded-hal 1.0.0", - "embedded-hal-async", -] - [[package]] name = "embassy-rp" version = "0.8.0" @@ -703,7 +457,7 @@ dependencies = [ "cortex-m", "cortex-m-rt", "critical-section", - "defmt 1.0.1", + "defmt", "document-features", "embassy-embedded-hal", "embassy-futures", @@ -740,7 +494,7 @@ checksum = "73974a3edbd0bd286759b3d483540f0ebef705919a5f56f4fc7709066f71689b" dependencies = [ "cfg-if", "critical-section", - "defmt 1.0.1", + "defmt", "embedded-io-async", "futures-core", "futures-sink", @@ -755,7 +509,7 @@ checksum = "f4fa65b9284d974dad7a23bb72835c4ec85c0b540d86af7fc4098c88cff51d65" dependencies = [ "cfg-if", "critical-section", - "defmt 1.0.1", + "defmt", "document-features", "embassy-time-driver", "embedded-hal 0.2.7", @@ -789,7 +543,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc4462e48b19a4f401a11901bdd981aab80c6a826608016a0bdc73cbbab31954" dependencies = [ - "defmt 1.0.1", + "defmt", "embassy-futures", "embassy-net-driver-channel", "embassy-sync", @@ -806,45 +560,10 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17119855ccc2d1f7470a39756b12068454ae27a3eabb037d940b5c03d9c77b7a" dependencies = [ - "defmt 1.0.1", + "defmt", "embedded-io-async", ] -[[package]] -name = "embassy-usb-logger" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deed6d36715838d6adbbff13b215b03a9deeaa66a64d5fccd6353708ccfb8b8f" -dependencies = [ - "embassy-futures", - "embassy-sync", - "embassy-usb", - "log", -] - -[[package]] -name = "embedded-graphics" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0649998afacf6d575d126d83e68b78c0ab0e00ca2ac7e9b3db11b4cbe8274ef0" -dependencies = [ - "az", - "byteorder", - "embedded-graphics-core", - "float-cmp", - "micromath", -] - -[[package]] -name = "embedded-graphics-core" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba9ecd261f991856250d2207f6d8376946cd9f412a2165d3b75bc87a0bc7a044" -dependencies = [ - "az", - "byteorder", -] - [[package]] name = "embedded-hal" version = "0.2.7" @@ -870,17 +589,6 @@ dependencies = [ "embedded-hal 1.0.0", ] -[[package]] -name = "embedded-hal-bus" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "513e0b3a8fb7d3013a8ae17a834283f170deaf7d0eeab0a7c1a36ad4dd356d22" -dependencies = [ - "critical-section", - "embedded-hal 1.0.0", - "embedded-hal-async", -] - [[package]] name = "embedded-hal-nb" version = "1.0.0" @@ -896,9 +604,6 @@ name = "embedded-io" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" -dependencies = [ - "defmt 0.3.100", -] [[package]] name = "embedded-io-async" @@ -906,40 +611,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ff09972d4073aa8c299395be75161d582e7629cd663171d62af73c8d50dba3f" dependencies = [ - "defmt 0.3.100", - "embedded-io", -] - -[[package]] -name = "embedded-nal" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c56a28be191a992f28f178ec338a0bf02f63d7803244add736d026a471e6ed77" -dependencies = [ - "nb 1.1.0", -] - -[[package]] -name = "embedded-nal-async" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76959917cd2b86f40a98c28dd5624eddd1fa69d746241c8257eac428d83cb211" -dependencies = [ - "embedded-io-async", - "embedded-nal", -] - -[[package]] -name = "embedded-sdmmc" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce3c7f9ea039eeafc4a49597b7bd5ae3a1c8e51b2803a381cb0f29ce90fe1ec6" -dependencies = [ - "byteorder", - "embedded-hal 1.0.0", "embedded-io", - "heapless 0.8.0", - "log", ] [[package]] @@ -957,29 +629,6 @@ dependencies = [ "embedded-storage", ] -[[package]] -name = "embedded-tls" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6efb76fdd004a4ef787640177237b83449e6c5847765ea50bf15900061fd601" -dependencies = [ - "aes-gcm", - "atomic-polyfill", - "defmt 0.3.100", - "digest", - "embedded-io", - "embedded-io-async", - "generic-array 0.14.7", - "heapless 0.6.1", - "heapless 0.8.0", - "hkdf", - "hmac", - "p256", - "rand_core 0.6.4", - "sha2", - "typenum", -] - [[package]] name = "ena" version = "0.14.3" @@ -1001,184 +650,41 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" -[[package]] -name = "ff" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" -dependencies = [ - "rand_core 0.6.4", - "subtle", -] - [[package]] name = "fixed" version = "1.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "707070ccf8c4173548210893a0186e29c266901b71ed20cd9e2ca0193dfe95c3" -dependencies = [ - "az", - "bytemuck", - "half", - "typenum", -] - -[[package]] -name = "fixed-macro" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0c48af8cb14e02868f449f8a2187bd78af7a08da201fdc78d518ecb1675bc" -dependencies = [ - "fixed", - "fixed-macro-impl", - "fixed-macro-types", -] - -[[package]] -name = "fixed-macro-impl" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c93086f471c0a1b9c5e300ea92f5cd990ac6d3f8edf27616ef624b8fa6402d4b" -dependencies = [ - "fixed", - "paste", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "fixed-macro-types" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "044a61b034a2264a7f65aa0c3cd112a01b4d4ee58baace51fead3f21b993c7e4" -dependencies = [ - "fixed", - "fixed-macro-impl", -] - -[[package]] -name = "fixedbitset" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" - -[[package]] -name = "float-cmp" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" -dependencies = [ - "num-traits", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "futures" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" - -[[package]] -name = "futures-intrusive" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" -dependencies = [ - "futures-core", - "lock_api", -] - -[[package]] -name = "futures-io" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" - -[[package]] -name = "futures-macro" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", +dependencies = [ + "az", + "bytemuck", + "half", + "typenum", ] [[package]] -name = "futures-sink" -version = "0.3.31" +name = "fixedbitset" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] -name = "futures-task" -version = "0.3.31" +name = "fnv" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] -name = "futures-util" +name = "futures-core" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" -dependencies = [ - "futures-core", - "futures-macro", - "futures-sink", - "futures-task", - "pin-project-lite", - "pin-utils", -] - -[[package]] -name = "generic-array" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" -dependencies = [ - "typenum", -] +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] -name = "generic-array" -version = "0.13.3" +name = "futures-sink" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f797e67af32588215eaaab8327027ee8e71b9dd0b2b26996aedf20c030fce309" -dependencies = [ - "typenum", -] +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "generic-array" @@ -1188,28 +694,6 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", - "zeroize", -] - -[[package]] -name = "ghash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" -dependencies = [ - "opaque-debug", - "polyval", -] - -[[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff", - "rand_core 0.6.4", - "subtle", ] [[package]] @@ -1222,15 +706,6 @@ dependencies = [ "crunchy", ] -[[package]] -name = "hash32" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4041af86e63ac4298ce40e5cca669066e75b6f1aa3390fe2561ffa5e1d9f4cc" -dependencies = [ - "byteorder", -] - [[package]] name = "hash32" version = "0.3.1" @@ -1255,27 +730,13 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" -[[package]] -name = "heapless" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634bd4d29cbf24424d0a4bfcbf80c6960129dc24424752a7d1d1390607023422" -dependencies = [ - "as-slice", - "generic-array 0.14.7", - "hash32 0.1.1", - "stable_deref_trait", -] - [[package]] name = "heapless" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" dependencies = [ - "defmt 0.3.100", - "hash32 0.3.1", - "serde", + "hash32", "stable_deref_trait", ] @@ -1285,91 +746,37 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1edcd5a338e64688fbdcb7531a846cfd3476a54784dcb918a0844682bc7ada5" dependencies = [ - "hash32 0.3.1", + "hash32", "stable_deref_trait", ] -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - [[package]] name = "hexagenmini" version = "0.1.0" dependencies = [ - "assign-resources", - "async-trait", - "base64ct", - "byte-slice-cast", + "base64", "cortex-m", "cortex-m-rt", "critical-section", - "cyw43", - "cyw43-pio", - "defmt 1.0.1", + "defmt", "defmt-rtt", - "display-interface", - "display-interface-spi", + "embassy-boot-rp", "embassy-embedded-hal", "embassy-executor", "embassy-futures", - "embassy-net", - "embassy-net-wiznet", "embassy-rp", "embassy-sync", "embassy-time", "embassy-usb", - "embassy-usb-logger", - "embedded-graphics", - "embedded-hal 1.0.0", - "embedded-hal-async", - "embedded-hal-bus", - "embedded-io-async", - "embedded-sdmmc", "embedded-storage", - "fixed", - "fixed-macro", "heapless 0.9.1", - "log", - "mipidsi", "panic-probe", "portable-atomic", - "rand", - "reqwless", - "serde", - "serde-json-core", + "sha2", "smart-leds", "static_cell", - "usbd-hid", - "usbd-midi", -] - -[[package]] -name = "hkdf" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" -dependencies = [ - "hmac", -] - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", ] -[[package]] -name = "httparse" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" - [[package]] name = "ident_case" version = "1.0.1" @@ -1386,15 +793,6 @@ dependencies = [ "hashbrown 0.16.0", ] -[[package]] -name = "inout" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" -dependencies = [ - "generic-array 0.14.7", -] - [[package]] name = "itertools" version = "0.14.0" @@ -1473,36 +871,12 @@ version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" -[[package]] -name = "managed" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" - [[package]] name = "memchr" version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" -[[package]] -name = "micromath" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c8dda44ff03a2f238717214da50f65d5a53b45cd213a7370424ffdb6fae815" - -[[package]] -name = "mipidsi" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ba34dcbf61182ffa6992b5a4d9b566d5a99df127fd93f6d314213347329e92f" -dependencies = [ - "embedded-graphics-core", - "embedded-hal 1.0.0", - "heapless 0.8.0", - "nb 1.1.0", -] - [[package]] name = "nb" version = "0.1.3" @@ -1524,15 +898,6 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" -[[package]] -name = "nourl" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa07b0722c63805057dec824444fdc814bdfd30d1c782a3a8f63bbcf67c4ed1c" -dependencies = [ - "defmt 0.3.100", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -1542,36 +907,16 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_enum" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" -dependencies = [ - "num_enum_derive 0.5.11", -] - [[package]] name = "num_enum" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" dependencies = [ - "num_enum_derive 0.7.4", + "num_enum_derive", "rustversion", ] -[[package]] -name = "num_enum_derive" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "num_enum_derive" version = "0.7.4" @@ -1589,22 +934,6 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" -[[package]] -name = "opaque-debug" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" - -[[package]] -name = "p256" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" -dependencies = [ - "elliptic-curve", - "primeorder", -] - [[package]] name = "panic-probe" version = "1.0.0" @@ -1612,7 +941,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd402d00b0fb94c5aee000029204a46884b1262e0c443f166d86d2c0747e1a1a" dependencies = [ "cortex-m", - "defmt 1.0.1", + "defmt", ] [[package]] @@ -1669,18 +998,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" -[[package]] -name = "pin-project-lite" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - [[package]] name = "pio" version = "0.3.0" @@ -1698,7 +1015,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61d90fddc3d67f21bbf93683bc461b05d6a29c708caf3ffb79947d7ff7095406" dependencies = [ "arrayvec", - "num_enum 0.7.4", + "num_enum", "paste", ] @@ -1729,18 +1046,6 @@ dependencies = [ "syn 2.0.106", ] -[[package]] -name = "polyval" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" -dependencies = [ - "cfg-if", - "cpufeatures", - "opaque-debug", - "universal-hash", -] - [[package]] name = "portable-atomic" version = "1.11.1" @@ -1750,54 +1055,12 @@ dependencies = [ "critical-section", ] -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - [[package]] name = "precomputed-hash" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" -[[package]] -name = "primeorder" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" -dependencies = [ - "elliptic-curve", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - [[package]] name = "proc-macro-error-attr2" version = "2.0.0" @@ -1838,25 +1101,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" -dependencies = [ - "rand_core 0.9.3", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - [[package]] name = "rand_core" version = "0.6.4" @@ -1907,27 +1151,6 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" -[[package]] -name = "reqwless" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb1be74cb817fa6dbda417110f575d9b9ad5488817f1eb65f2f6468fe6d5d663" -dependencies = [ - "base64", - "buffered-io", - "defmt 0.3.100", - "embedded-io", - "embedded-io-async", - "embedded-nal-async", - "embedded-tls", - "heapless 0.8.0", - "hex", - "httparse", - "nourl", - "rand_chacha", - "rand_core 0.6.4", -] - [[package]] name = "rgb" version = "0.8.52" @@ -1971,12 +1194,6 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" -[[package]] -name = "ryu" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" - [[package]] name = "same-file" version = "1.0.6" @@ -1992,19 +1209,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct", - "der", - "generic-array 0.14.7", - "subtle", - "zeroize", -] - [[package]] name = "semver" version = "0.9.0" @@ -2027,18 +1231,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80ece43fc6fbed4eb5392ab50c07334d3e577cbf40997ee896fe7af40bba4245" dependencies = [ "serde_core", - "serde_derive", -] - -[[package]] -name = "serde-json-core" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b81787e655bd59cecadc91f7b6b8651330b2be6c33246039a65e5cd6f4e0828" -dependencies = [ - "heapless 0.8.0", - "ryu", - "serde", ] [[package]] @@ -2088,6 +1280,12 @@ dependencies = [ "keccak", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" + [[package]] name = "siphasher" version = "1.0.1" @@ -2118,20 +1316,6 @@ dependencies = [ "rgb", ] -[[package]] -name = "smoltcp" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad095989c1533c1c266d9b1e8d70a1329dd3723c3edac6d03bbd67e7bf6f4bb" -dependencies = [ - "bitflags 1.3.2", - "byteorder", - "cfg-if", - "defmt 0.3.100", - "heapless 0.8.0", - "managed", -] - [[package]] name = "ssmarshal" version = "1.0.0" @@ -2175,12 +1359,6 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - [[package]] name = "syn" version = "1.0.109" @@ -2265,16 +1443,6 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" -[[package]] -name = "universal-hash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" -dependencies = [ - "crypto-common", - "subtle", -] - [[package]] name = "usb-device" version = "0.3.2" @@ -2322,16 +1490,6 @@ dependencies = [ "usbd-hid-descriptors", ] -[[package]] -name = "usbd-midi" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a88782a02de4ea460cd84dc5cc8007a27618a5a8bb14514699dba5e7dcf9a590" -dependencies = [ - "num_enum 0.7.4", - "usb-device", -] - [[package]] name = "vcell" version = "0.1.3" @@ -2476,9 +1634,3 @@ dependencies = [ "quote", "syn 2.0.106", ] - -[[package]] -name = "zeroize" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/firmware/Cargo.toml b/firmware/Cargo.toml index e7cdd0b..b934231 100644 --- a/firmware/Cargo.toml +++ b/firmware/Cargo.toml @@ -42,65 +42,28 @@ embassy-rp = { version = "0.8.0", features = [ "rp2040", ] } embassy-usb = { version = "0.5.1", features = ["defmt"] } -embassy-net = { version = "0.7.1", features = [ - "defmt", - "icmp", - "tcp", - "udp", - "raw", - "dhcpv4", - "medium-ethernet", - "dns", - "proto-ipv4", - "proto-ipv6", - "multicast", -] } -embassy-net-wiznet = { version = "0.2.1", features = ["defmt"] } embassy-futures = { version = "0.1.2" } -embassy-usb-logger = { version = "0.5.1" } -cyw43 = { version = "0.5.0", features = ["defmt", "firmware-logs"] } -cyw43-pio = { version = "0.8.0", features = ["defmt"] } +embassy-boot-rp = { version = "0.8", default-features = false } defmt = "1.0.1" defmt-rtt = "1.0.0" -fixed = "1.23.1" -fixed-macro = "1.2" -# for web request example -reqwless = { version = "0.13.0", features = ["defmt"] } -serde = { version = "1.0.203", default-features = false, features = ["derive"] } -serde-json-core = "0.6.0" - -# for assign resources example -assign-resources = { git = "https://github.com/adamgreig/assign-resources", rev = "bd22cb7a92031fb16f74a5da42469d466c33383e" } +embedded-storage = { version = "0.3" } #cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m = { version = "0.7.6", features = ["inline-asm"] } cortex-m-rt = "0.7.0" critical-section = "1.1" + panic-probe = { version = "1.0.0", features = ["print-defmt"] } -display-interface-spi = "0.5.0" -embedded-graphics = "0.8.1" -mipidsi = "0.9.0" -display-interface = "0.5.0" -byte-slice-cast = { version = "1.2.0", default-features = false } smart-leds = "0.4.0" heapless = "0.9.1" -usbd-hid = "0.8.1" -usbd-midi = "0.5.0" -embedded-hal-1 = { package = "embedded-hal", version = "1.0" } -embedded-hal-async = "1.0" -embedded-hal-bus = { version = "0.3", features = ["async"] } -embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } -embedded-storage = { version = "0.3" } static_cell = "2.1" portable-atomic = { version = "1.5", features = ["critical-section"] } -log = "0.4" -rand = { version = "0.9.0", default-features = false } -embedded-sdmmc = "0.9.0" -base64ct = "1.8.0" -async-trait = "0.1.89" + +sha2 = { version = "0.10", default-features = false } +base64 = { version = "0.22.1", default-features = false } [profile.release] # Enable generation of debug symbols even on release builds diff --git a/firmware/src/at/at_task.rs b/firmware/src/at/at_task.rs index f560c63..56c7837 100644 --- a/firmware/src/at/at_task.rs +++ b/firmware/src/at/at_task.rs @@ -23,7 +23,7 @@ pub async fn at_task( loop { match at_rx.receive().await { Msg::AtCmd(MsgDirection::Input, line) => { - if let Some(e) = dispatcher.dispatch(spawner, at_tx, rgb_tx, &line) { + if let Some(e) = dispatcher.dispatch(spawner, at_tx, usb_tx, rgb_tx, &line) { error!("Dispatch error: {:?}", &e.code()); let compiled = compile_at_error(e); usb_tx.send(Msg::UsbTxLine(compiled)).await; diff --git a/firmware/src/at/dispatcher.rs b/firmware/src/at/dispatcher.rs index c425fe6..49be6cb 100644 --- a/firmware/src/at/dispatcher.rs +++ b/firmware/src/at/dispatcher.rs @@ -27,6 +27,7 @@ impl AtDispatcher { &self, spawner: Spawner, at_tx: Sender<'static, Cs, Msg, CAP>, + usb_tx: Sender<'static, Cs, Msg, CAP>, rgb_tx: Sender<'static, Cs, Msg, CAP>, input: &str, ) -> Option { @@ -41,6 +42,14 @@ impl AtDispatcher { info!("Dispatching SETRGB command"); h.handle(spawner, rgb_tx, &cmd.params, cmd.is_query) } + Handler::Reset(h) => { + info!("Dispatching RESET command"); + h.handle(spawner, at_tx, &cmd.params, cmd.is_query) + } + Handler::FwUpdate(h) => { + info!("Dispatching FWUPDATE command"); + h.handle(spawner, at_tx, &cmd.params, cmd.is_query) + } }, None => Some(Error::UnknownCommand), }, diff --git a/firmware/src/at/handler.rs b/firmware/src/at/handler.rs index 06d04b1..b77ce11 100644 --- a/firmware/src/at/handler.rs +++ b/firmware/src/at/handler.rs @@ -23,6 +23,8 @@ pub trait AtHandler { pub enum Handler { Version(VersionHandler), SetRgb(SetRgbHandler), + Reset(ResetHandler), + FwUpdate(FwUpdateHandler), } pub fn register_all() -> LinearMap, Handler, 8> { @@ -40,5 +42,17 @@ pub fn register_all() -> LinearMap, Handler, 8> { ) .ok(); + map.insert( + String::<16>::try_from("RESET").unwrap(), + Handler::Reset(ResetHandler), + ) + .ok(); + + map.insert( + String::<16>::try_from("FWUPDATE").unwrap(), + Handler::FwUpdate(FwUpdateHandler), + ) + .ok(); + map } diff --git a/firmware/src/at/handlers/fwupdate_handler.rs b/firmware/src/at/handlers/fwupdate_handler.rs new file mode 100644 index 0000000..d758893 --- /dev/null +++ b/firmware/src/at/handlers/fwupdate_handler.rs @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2025 hexaTune LLC +// SPDX-License-Identifier: MIT + +use defmt::info; +use embassy_executor::Spawner; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; +use embassy_sync::channel::Sender; +use heapless::String; + +use crate::at::AtHandler; +use crate::channel::{CAP, Msg}; +use crate::error::Error; + +pub struct FwUpdateHandler; + +impl AtHandler for FwUpdateHandler { + fn handle( + &self, + spawner: Spawner, + _at_tx: Sender<'static, Cs, Msg, CAP>, + _params: &[String<16>], + _is_query: bool, + ) -> Option { + info!("Firmware update requested - spawning BOOTSEL task"); + let _ = spawner.spawn(fwupdate_task()); + None + } +} + +#[embassy_executor::task] +async fn fwupdate_task() { + info!("Entering BOOTSEL mode for firmware update"); + embassy_time::Timer::after_millis(100).await; + unsafe { + embassy_rp::rom_data::reset_to_usb_boot(0, 0); + } +} diff --git a/firmware/src/at/handlers/mod.rs b/firmware/src/at/handlers/mod.rs index c3d386b..c03c46e 100644 --- a/firmware/src/at/handlers/mod.rs +++ b/firmware/src/at/handlers/mod.rs @@ -5,3 +5,7 @@ mod version_handler; pub use version_handler::*; mod setrgb_handler; pub use setrgb_handler::*; +mod fwupdate_handler; +pub use fwupdate_handler::*; +mod reset_handler; +pub use reset_handler::*; diff --git a/firmware/src/at/handlers/reset_handler.rs b/firmware/src/at/handlers/reset_handler.rs new file mode 100644 index 0000000..3b17833 --- /dev/null +++ b/firmware/src/at/handlers/reset_handler.rs @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2025 hexaTune LLC +// SPDX-License-Identifier: MIT + +use cortex_m::peripheral::SCB; +use embassy_executor::Spawner; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; +use embassy_sync::channel::Sender; +use heapless::String; + +use crate::at::AtHandler; +use crate::channel::{CAP, Msg}; +use crate::error::Error; + +pub struct ResetHandler; + +impl AtHandler for ResetHandler { + fn handle( + &self, + _spawner: Spawner, + _at_tx: Sender<'static, Cs, Msg, CAP>, + _params: &[String<16>], + _is_query: bool, + ) -> Option { + SCB::sys_reset(); + } +} diff --git a/firmware/src/at/handlers/setrgb_handler.rs b/firmware/src/at/handlers/setrgb_handler.rs index 0425e6e..c9cfd62 100644 --- a/firmware/src/at/handlers/setrgb_handler.rs +++ b/firmware/src/at/handlers/setrgb_handler.rs @@ -1,14 +1,14 @@ // SPDX-FileCopyrightText: 2025 hexaTune LLC // SPDX-License-Identifier: MIT -use defmt::{error, info}; +use defmt::error; use embassy_executor::Spawner; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; use embassy_sync::channel::Sender; -use heapless::{String, Vec}; +use heapless::String; -use crate::at::{AtCommand, AtHandler, compile_at_error}; -use crate::channel::{CAP, Msg, MsgDirection}; +use crate::at::AtHandler; +use crate::channel::{CAP, Msg}; use crate::error::Error; pub struct SetRgbHandler; diff --git a/firmware/src/at/handlers/version_handler.rs b/firmware/src/at/handlers/version_handler.rs index 1a5854d..a9a7ac6 100644 --- a/firmware/src/at/handlers/version_handler.rs +++ b/firmware/src/at/handlers/version_handler.rs @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: 2025 hexaTune LLC // SPDX-License-Identifier: MIT -use defmt::{error, info}; +use defmt::error; use embassy_executor::Spawner; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; use embassy_sync::channel::Sender; diff --git a/firmware/src/b64/mod.rs b/firmware/src/b64/mod.rs index 1760c6b..9cbe0a2 100644 --- a/firmware/src/b64/mod.rs +++ b/firmware/src/b64/mod.rs @@ -1,13 +1,12 @@ // SPDX-FileCopyrightText: 2025 hexaTune LLC // SPDX-License-Identifier: MIT -use base64ct::{Base64, Encoding}; +//use base64ct::{Base64, Encoding}; use heapless::String; use crate::error::Error; pub fn b64_decode(input: &str) -> Result, Error> { - // --- Base64 decode işlemi geçici olarak iptal edildi --- // let mut buf = [0u8; 64]; // match Base64::decode(input, &mut buf) { // Ok(decoded) => match core::str::from_utf8(decoded) { @@ -21,21 +20,18 @@ pub fn b64_decode(input: &str) -> Result, Error> { // Err(_) => Err(Error::InvalidBase64), // } - // Doğrudan input'u geri döndür let mut out = String::<64>::new(); out.push_str(input).map_err(|_| Error::InvalidUtf8)?; Ok(out) } pub fn b64_encode(input: &str) -> String<64> { - // --- Base64 encode işlemi geçici olarak iptal edildi --- // let mut buf = [0u8; 64]; // let encoded = Base64::encode(input.as_bytes(), &mut buf).unwrap(); // let mut out = String::<64>::new(); // out.push_str(encoded).unwrap(); // out - // Doğrudan input'u geri döndür let mut out = String::<64>::new(); out.push_str(input).unwrap(); out diff --git a/firmware/src/error/mod.rs b/firmware/src/error/mod.rs index a4a3239..a306238 100644 --- a/firmware/src/error/mod.rs +++ b/firmware/src/error/mod.rs @@ -3,15 +3,22 @@ #[derive(Debug, Clone)] pub enum Error { - InvalidCommand, // "Not an AT command" - InvalidBase64, // "Invalid base64 param" - InvalidUtf8, // "Invalid UTF-8 in param" - InvalidSysEx, // "Invalid SysEx" - InvalidDataLenght, // "Invalid data length" - ParamCount, // "Param count" - ParamValue, // "Param value" - NotAQuery, // "Not a query" - UnknownCommand, // "Unknown command" + InvalidCommand, // "Not an AT command" + InvalidBase64, // "Invalid base64 param" + InvalidUtf8, // "Invalid UTF-8 in param" + InvalidSysEx, // "Invalid SysEx" + InvalidDataLenght, // "Invalid data length" + ParamCount, // "Param count" + ParamValue, // "Param value" + NotAQuery, // "Not a query" + UnknownCommand, // "Unknown command" + FirmwareUpdateFlow, // "Firmware update flow error" + UpdateNotStarted, // "FW update not started" + InvalidOffset, // "Invalid offset" + FlashWrite, // "Flash write error" + LengthMismatch, // "Length mismatch" + CrcMismatch, // Crc mismatch" + CommitFailed, // Commit failed" } impl Error { @@ -27,6 +34,13 @@ impl Error { Error::ParamValue => "E001007", Error::NotAQuery => "E001008", Error::UnknownCommand => "E001009", + Error::FirmwareUpdateFlow => "E001010", + Error::UpdateNotStarted => "E001011", + Error::InvalidOffset => "E001012", + Error::FlashWrite => "E001013", + Error::LengthMismatch => "E001014", + Error::CrcMismatch => "E001015", + Error::CommitFailed => "E001016", } } } diff --git a/firmware/src/main.rs b/firmware/src/main.rs index f610d4a..71de930 100644 --- a/firmware/src/main.rs +++ b/firmware/src/main.rs @@ -4,9 +4,10 @@ #![no_std] #![no_main] +use core::cell::RefCell; use defmt::info; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; -use embassy_sync::mutex::Mutex; +use embassy_sync::mutex::Mutex as AsyncMutex; use {defmt_rtt as _, panic_probe as _}; mod at; @@ -18,17 +19,16 @@ mod rgb; mod sysex; mod usb; +static CHANNEL_MANAGER: static_cell::StaticCell> = + static_cell::StaticCell::new(); + embassy_rp::bind_interrupts!(struct IrqUsb { USBCTRL_IRQ => embassy_rp::usb::InterruptHandler; }); - embassy_rp::bind_interrupts!(struct IrqPio { PIO0_IRQ_0 => embassy_rp::pio::InterruptHandler; }); -static CHANNEL_MANAGER: static_cell::StaticCell> = - static_cell::StaticCell::new(); - #[embassy_executor::main] async fn main(spawner: embassy_executor::Spawner) { //Initialize the RP2040 @@ -52,10 +52,11 @@ async fn main(spawner: embassy_executor::Spawner) { info!("Initializing USB"); let driver = embassy_rp::usb::Driver::new(p.USB, IrqUsb); let usb::UsbMidi { device, midi } = usb::init(driver); - static MIDI_CELL: static_cell::StaticCell>> = + + static MIDI_CELL: static_cell::StaticCell>> = static_cell::StaticCell::new(); - let midi_mutex: &'static Mutex> = - MIDI_CELL.init(Mutex::new(midi)); + let midi_mutex: &'static AsyncMutex> = + MIDI_CELL.init(AsyncMutex::new(midi)); //AT module info!("Initializing AT command dispatcher"); @@ -68,14 +69,14 @@ async fn main(spawner: embassy_executor::Spawner) { mut common, sm0, .. } = embassy_rp::pio::Pio::new(p.PIO0, IrqPio); let program = embassy_rp::pio_programs::ws2812::PioWs2812Program::new(&mut common); - let mut ws2812 = embassy_rp::pio_programs::ws2812::PioWs2812::new( + let ws2812 = embassy_rp::pio_programs::ws2812::PioWs2812::new( &mut common, sm0, p.DMA_CH0, p.PIN_23, &program, ); - let mut rgb_led = rgb::RgbLed::new(ws2812); + let rgb_led = rgb::RgbLed::new(ws2812); //Spawn tasks info!("Spawning tasks"); @@ -85,6 +86,7 @@ async fn main(spawner: embassy_executor::Spawner) { )) .unwrap(); spawner.spawn(usb::dev_task(device)).unwrap(); + // midi_mutex now matches the expected type for usb_io_task (AsyncMutex) spawner .spawn(usb::usb_io_task(midi_mutex, usb_rx, at_tx)) .unwrap(); diff --git a/firmware/src/usb/usb_task.rs b/firmware/src/usb/usb_task.rs index 2444f88..c1117d2 100644 --- a/firmware/src/usb/usb_task.rs +++ b/firmware/src/usb/usb_task.rs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2025 hexaTune LLC // SPDX-License-Identifier: MIT +use core::sync::atomic::{AtomicBool, Ordering}; use defmt::{error, info}; use embassy_futures::select::{Either, select}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; @@ -21,7 +22,7 @@ pub async fn dev_task(mut dev: MyUsbDevice<'static>) { #[embassy_executor::task] pub async fn usb_io_task( midi: &'static Mutex>, - mut usb_rx: Receiver<'static, Cs, Msg, CAP>, + usb_rx: Receiver<'static, Cs, Msg, CAP>, at_tx: Sender<'static, Cs, Msg, CAP>, ) { info!("Starting unified USB IO task"); @@ -39,7 +40,6 @@ pub async fn usb_io_task( let tx_fut = async { usb_rx.receive().await }; match select(read_fut, tx_fut).await { - // USB'DEN OKUNDU → AT'ye gönder Either::First((n, buf)) => { let data = &buf[..n]; info!("Received MIDI packet: {:?}", data); From d5ddb2fb6a93fa78f9975f5f20158759092d39f5 Mon Sep 17 00:00:00 2001 From: Husamettin ARABACI Date: Fri, 17 Oct 2025 11:43:17 +0300 Subject: [PATCH 06/16] feat(com): passed first communication (#25) Communicated with mobile app first time --- REUSE.toml | 1 + firmware/src/at/at_task.rs | 2 +- firmware/src/at/dispatcher.rs | 1 - firmware/src/at/handlers/fwupdate_handler.rs | 4 +- firmware/src/error/mod.rs | 32 +- firmware/src/main.rs | 1 - firmware/src/rgb/led.rs | 4 +- firmware/src/rgb/rgb_task.rs | 15 +- firmware/src/usb/usb_task.rs | 1 - pnpm-lock.yaml | 3165 ++++++++++++++++++ 10 files changed, 3185 insertions(+), 41 deletions(-) create mode 100644 pnpm-lock.yaml diff --git a/REUSE.toml b/REUSE.toml index b1a8e41..c63a2e2 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -8,6 +8,7 @@ path = [ "docs/CNAME", ".husky/**", "package-lock.json", + "pnpm-lock.yaml", ] precedence = "aggregate" SPDX-FileCopyrightText = "2025 hexaTune LLC" diff --git a/firmware/src/at/at_task.rs b/firmware/src/at/at_task.rs index 56c7837..f560c63 100644 --- a/firmware/src/at/at_task.rs +++ b/firmware/src/at/at_task.rs @@ -23,7 +23,7 @@ pub async fn at_task( loop { match at_rx.receive().await { Msg::AtCmd(MsgDirection::Input, line) => { - if let Some(e) = dispatcher.dispatch(spawner, at_tx, usb_tx, rgb_tx, &line) { + if let Some(e) = dispatcher.dispatch(spawner, at_tx, rgb_tx, &line) { error!("Dispatch error: {:?}", &e.code()); let compiled = compile_at_error(e); usb_tx.send(Msg::UsbTxLine(compiled)).await; diff --git a/firmware/src/at/dispatcher.rs b/firmware/src/at/dispatcher.rs index 49be6cb..46431db 100644 --- a/firmware/src/at/dispatcher.rs +++ b/firmware/src/at/dispatcher.rs @@ -27,7 +27,6 @@ impl AtDispatcher { &self, spawner: Spawner, at_tx: Sender<'static, Cs, Msg, CAP>, - usb_tx: Sender<'static, Cs, Msg, CAP>, rgb_tx: Sender<'static, Cs, Msg, CAP>, input: &str, ) -> Option { diff --git a/firmware/src/at/handlers/fwupdate_handler.rs b/firmware/src/at/handlers/fwupdate_handler.rs index d758893..aaf4b40 100644 --- a/firmware/src/at/handlers/fwupdate_handler.rs +++ b/firmware/src/at/handlers/fwupdate_handler.rs @@ -31,7 +31,5 @@ impl AtHandler for FwUpdateHandler { async fn fwupdate_task() { info!("Entering BOOTSEL mode for firmware update"); embassy_time::Timer::after_millis(100).await; - unsafe { - embassy_rp::rom_data::reset_to_usb_boot(0, 0); - } + embassy_rp::rom_data::reset_to_usb_boot(0, 0); } diff --git a/firmware/src/error/mod.rs b/firmware/src/error/mod.rs index a306238..a4a3239 100644 --- a/firmware/src/error/mod.rs +++ b/firmware/src/error/mod.rs @@ -3,22 +3,15 @@ #[derive(Debug, Clone)] pub enum Error { - InvalidCommand, // "Not an AT command" - InvalidBase64, // "Invalid base64 param" - InvalidUtf8, // "Invalid UTF-8 in param" - InvalidSysEx, // "Invalid SysEx" - InvalidDataLenght, // "Invalid data length" - ParamCount, // "Param count" - ParamValue, // "Param value" - NotAQuery, // "Not a query" - UnknownCommand, // "Unknown command" - FirmwareUpdateFlow, // "Firmware update flow error" - UpdateNotStarted, // "FW update not started" - InvalidOffset, // "Invalid offset" - FlashWrite, // "Flash write error" - LengthMismatch, // "Length mismatch" - CrcMismatch, // Crc mismatch" - CommitFailed, // Commit failed" + InvalidCommand, // "Not an AT command" + InvalidBase64, // "Invalid base64 param" + InvalidUtf8, // "Invalid UTF-8 in param" + InvalidSysEx, // "Invalid SysEx" + InvalidDataLenght, // "Invalid data length" + ParamCount, // "Param count" + ParamValue, // "Param value" + NotAQuery, // "Not a query" + UnknownCommand, // "Unknown command" } impl Error { @@ -34,13 +27,6 @@ impl Error { Error::ParamValue => "E001007", Error::NotAQuery => "E001008", Error::UnknownCommand => "E001009", - Error::FirmwareUpdateFlow => "E001010", - Error::UpdateNotStarted => "E001011", - Error::InvalidOffset => "E001012", - Error::FlashWrite => "E001013", - Error::LengthMismatch => "E001014", - Error::CrcMismatch => "E001015", - Error::CommitFailed => "E001016", } } } diff --git a/firmware/src/main.rs b/firmware/src/main.rs index 71de930..827c5d4 100644 --- a/firmware/src/main.rs +++ b/firmware/src/main.rs @@ -4,7 +4,6 @@ #![no_std] #![no_main] -use core::cell::RefCell; use defmt::info; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; use embassy_sync::mutex::Mutex as AsyncMutex; diff --git a/firmware/src/rgb/led.rs b/firmware/src/rgb/led.rs index e1de1a4..5ee9357 100644 --- a/firmware/src/rgb/led.rs +++ b/firmware/src/rgb/led.rs @@ -23,7 +23,7 @@ impl RgbLed { self.led.write(&self.buf).await; } - pub async fn off(&mut self) { + /*pub async fn off(&mut self) { self.set_rgb(0, 0, 0).await; - } + }*/ } diff --git a/firmware/src/rgb/rgb_task.rs b/firmware/src/rgb/rgb_task.rs index 8c19db4..5a70624 100644 --- a/firmware/src/rgb/rgb_task.rs +++ b/firmware/src/rgb/rgb_task.rs @@ -17,15 +17,12 @@ pub async fn rgb_task( ) { info!("Starting RGB task"); loop { - match rgb_rx.receive().await { - Msg::RgbWithValue(r, g, b) => { - info!("Setting RGB to ({}, {}, {})", r, g, b); - rgb_led.set_rgb(r, g, b).await; - info!("RGB set"); - info!("Sending OK from RGB task"); - at_tx.send(Msg::Ok).await; - } - _ => {} + if let Msg::RgbWithValue(r, g, b) = rgb_rx.receive().await { + info!("Setting RGB to ({}, {}, {})", r, g, b); + rgb_led.set_rgb(r, g, b).await; + info!("RGB set"); + info!("Sending OK from RGB task"); + at_tx.send(Msg::Ok).await; } } } diff --git a/firmware/src/usb/usb_task.rs b/firmware/src/usb/usb_task.rs index c1117d2..0222ed5 100644 --- a/firmware/src/usb/usb_task.rs +++ b/firmware/src/usb/usb_task.rs @@ -1,7 +1,6 @@ // SPDX-FileCopyrightText: 2025 hexaTune LLC // SPDX-License-Identifier: MIT -use core::sync::atomic::{AtomicBool, Ordering}; use defmt::{error, info}; use embassy_futures::select::{Either, select}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..be20429 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,3165 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + '@commitlint/cli': + specifier: ^20.1.0 + version: 20.1.0(@types/node@24.8.1)(typescript@5.9.3) + '@commitlint/config-conventional': + specifier: ^20.0.0 + version: 20.0.0 + '@semantic-release/changelog': + specifier: ^6.0.0 + version: 6.0.3(semantic-release@22.0.12(typescript@5.9.3)) + '@semantic-release/commit-analyzer': + specifier: ^9.0.0 + version: 9.0.2(semantic-release@22.0.12(typescript@5.9.3)) + '@semantic-release/git': + specifier: ^10.0.0 + version: 10.0.1(semantic-release@22.0.12(typescript@5.9.3)) + '@semantic-release/github': + specifier: ^9.0.0 + version: 9.2.6(semantic-release@22.0.12(typescript@5.9.3)) + '@semantic-release/release-notes-generator': + specifier: ^10.0.0 + version: 10.0.3(semantic-release@22.0.12(typescript@5.9.3)) + husky: + specifier: ^9.1.7 + version: 9.1.7 + semantic-release: + specifier: ^22.0.0 + version: 22.0.12(typescript@5.9.3) + +packages: + + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + engines: {node: '>=6.9.0'} + + '@colors/colors@1.5.0': + resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} + engines: {node: '>=0.1.90'} + + '@commitlint/cli@20.1.0': + resolution: {integrity: sha512-pW5ujjrOovhq5RcYv5xCpb4GkZxkO2+GtOdBW2/qrr0Ll9tl3PX0aBBobGQl3mdZUbOBgwAexEQLeH6uxL0VYg==} + engines: {node: '>=v18'} + hasBin: true + + '@commitlint/config-conventional@20.0.0': + resolution: {integrity: sha512-q7JroPIkDBtyOkVe9Bca0p7kAUYxZMxkrBArCfuD3yN4KjRAenP9PmYwnn7rsw8Q+hHq1QB2BRmBh0/Z19ZoJw==} + engines: {node: '>=v18'} + + '@commitlint/config-validator@20.0.0': + resolution: {integrity: sha512-BeyLMaRIJDdroJuYM2EGhDMGwVBMZna9UiIqV9hxj+J551Ctc6yoGuGSmghOy/qPhBSuhA6oMtbEiTmxECafsg==} + engines: {node: '>=v18'} + + '@commitlint/ensure@20.0.0': + resolution: {integrity: sha512-WBV47Fffvabe68n+13HJNFBqiMH5U1Ryls4W3ieGwPC0C7kJqp3OVQQzG2GXqOALmzrgAB+7GXmyy8N9ct8/Fg==} + engines: {node: '>=v18'} + + '@commitlint/execute-rule@20.0.0': + resolution: {integrity: sha512-xyCoOShoPuPL44gVa+5EdZsBVao/pNzpQhkzq3RdtlFdKZtjWcLlUFQHSWBuhk5utKYykeJPSz2i8ABHQA+ZZw==} + engines: {node: '>=v18'} + + '@commitlint/format@20.0.0': + resolution: {integrity: sha512-zrZQXUcSDmQ4eGGrd+gFESiX0Rw+WFJk7nW4VFOmxub4mAATNKBQ4vNw5FgMCVehLUKG2OT2LjOqD0Hk8HvcRg==} + engines: {node: '>=v18'} + + '@commitlint/is-ignored@20.0.0': + resolution: {integrity: sha512-ayPLicsqqGAphYIQwh9LdAYOVAQ9Oe5QCgTNTj+BfxZb9b/JW222V5taPoIBzYnAP0z9EfUtljgBk+0BN4T4Cw==} + engines: {node: '>=v18'} + + '@commitlint/lint@20.0.0': + resolution: {integrity: sha512-kWrX8SfWk4+4nCexfLaQT3f3EcNjJwJBsSZ5rMBw6JCd6OzXufFHgel2Curos4LKIxwec9WSvs2YUD87rXlxNQ==} + engines: {node: '>=v18'} + + '@commitlint/load@20.1.0': + resolution: {integrity: sha512-qo9ER0XiAimATQR5QhvvzePfeDfApi/AFlC1G+YN+ZAY8/Ua6IRrDrxRvQAr+YXUKAxUsTDSp9KXeXLBPsNRWg==} + engines: {node: '>=v18'} + + '@commitlint/message@20.0.0': + resolution: {integrity: sha512-gLX4YmKnZqSwkmSB9OckQUrI5VyXEYiv3J5JKZRxIp8jOQsWjZgHSG/OgEfMQBK9ibdclEdAyIPYggwXoFGXjQ==} + engines: {node: '>=v18'} + + '@commitlint/parse@20.0.0': + resolution: {integrity: sha512-j/PHCDX2bGM5xGcWObOvpOc54cXjn9g6xScXzAeOLwTsScaL4Y+qd0pFC6HBwTtrH92NvJQc+2Lx9HFkVi48cg==} + engines: {node: '>=v18'} + + '@commitlint/read@20.0.0': + resolution: {integrity: sha512-Ti7Y7aEgxsM1nkwA4ZIJczkTFRX/+USMjNrL9NXwWQHqNqrBX2iMi+zfuzZXqfZ327WXBjdkRaytJ+z5vNqTOA==} + engines: {node: '>=v18'} + + '@commitlint/resolve-extends@20.1.0': + resolution: {integrity: sha512-cxKXQrqHjZT3o+XPdqDCwOWVFQiae++uwd9dUBC7f2MdV58ons3uUvASdW7m55eat5sRiQ6xUHyMWMRm6atZWw==} + engines: {node: '>=v18'} + + '@commitlint/rules@20.0.0': + resolution: {integrity: sha512-gvg2k10I/RfvHn5I5sxvVZKM1fl72Sqrv2YY/BnM7lMHcYqO0E2jnRWoYguvBfEcZ39t+rbATlciggVe77E4zA==} + engines: {node: '>=v18'} + + '@commitlint/to-lines@20.0.0': + resolution: {integrity: sha512-2l9gmwiCRqZNWgV+pX1X7z4yP0b3ex/86UmUFgoRt672Ez6cAM2lOQeHFRUTuE6sPpi8XBCGnd8Kh3bMoyHwJw==} + engines: {node: '>=v18'} + + '@commitlint/top-level@20.0.0': + resolution: {integrity: sha512-drXaPSP2EcopukrUXvUXmsQMu3Ey/FuJDc/5oiW4heoCfoE5BdLQyuc7veGeE3aoQaTVqZnh4D5WTWe2vefYKg==} + engines: {node: '>=v18'} + + '@commitlint/types@20.0.0': + resolution: {integrity: sha512-bVUNBqG6aznYcYjTjnc3+Cat/iBgbgpflxbIBTnsHTX0YVpnmINPEkSRWymT2Q8aSH3Y7aKnEbunilkYe8TybA==} + engines: {node: '>=v18'} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@octokit/auth-token@4.0.0': + resolution: {integrity: sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==} + engines: {node: '>= 18'} + + '@octokit/core@5.2.2': + resolution: {integrity: sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==} + engines: {node: '>= 18'} + + '@octokit/endpoint@9.0.6': + resolution: {integrity: sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==} + engines: {node: '>= 18'} + + '@octokit/graphql@7.1.1': + resolution: {integrity: sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==} + engines: {node: '>= 18'} + + '@octokit/openapi-types@20.0.0': + resolution: {integrity: sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==} + + '@octokit/openapi-types@24.2.0': + resolution: {integrity: sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==} + + '@octokit/plugin-paginate-rest@9.2.2': + resolution: {integrity: sha512-u3KYkGF7GcZnSD/3UP0S7K5XUFT2FkOQdcfXZGZQPGv3lm4F2Xbf71lvjldr8c1H3nNbF+33cLEkWYbokGWqiQ==} + engines: {node: '>= 18'} + peerDependencies: + '@octokit/core': '5' + + '@octokit/plugin-retry@6.1.0': + resolution: {integrity: sha512-WrO3bvq4E1Xh1r2mT9w6SDFg01gFmP81nIG77+p/MqW1JeXXgL++6umim3t6x0Zj5pZm3rXAN+0HEjmmdhIRig==} + engines: {node: '>= 18'} + peerDependencies: + '@octokit/core': '5' + + '@octokit/plugin-throttling@8.2.0': + resolution: {integrity: sha512-nOpWtLayKFpgqmgD0y3GqXafMFuKcA4tRPZIfu7BArd2lEZeb1988nhWhwx4aZWmjDmUfdgVf7W+Tt4AmvRmMQ==} + engines: {node: '>= 18'} + peerDependencies: + '@octokit/core': ^5.0.0 + + '@octokit/request-error@5.1.1': + resolution: {integrity: sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==} + engines: {node: '>= 18'} + + '@octokit/request@8.4.1': + resolution: {integrity: sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==} + engines: {node: '>= 18'} + + '@octokit/types@12.6.0': + resolution: {integrity: sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==} + + '@octokit/types@13.10.0': + resolution: {integrity: sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==} + + '@pnpm/config.env-replace@1.1.0': + resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} + engines: {node: '>=12.22.0'} + + '@pnpm/network.ca-file@1.0.2': + resolution: {integrity: sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==} + engines: {node: '>=12.22.0'} + + '@pnpm/npm-conf@2.3.1': + resolution: {integrity: sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==} + engines: {node: '>=12'} + + '@semantic-release/changelog@6.0.3': + resolution: {integrity: sha512-dZuR5qByyfe3Y03TpmCvAxCyTnp7r5XwtHRf/8vD9EAn4ZWbavUX8adMtXYzE86EVh0gyLA7lm5yW4IV30XUag==} + engines: {node: '>=14.17'} + peerDependencies: + semantic-release: '>=18.0.0' + + '@semantic-release/commit-analyzer@11.1.0': + resolution: {integrity: sha512-cXNTbv3nXR2hlzHjAMgbuiQVtvWHTlwwISt60B+4NZv01y/QRY7p2HcJm8Eh2StzcTJoNnflvKjHH/cjFS7d5g==} + engines: {node: ^18.17 || >=20.6.1} + peerDependencies: + semantic-release: '>=20.1.0' + + '@semantic-release/commit-analyzer@9.0.2': + resolution: {integrity: sha512-E+dr6L+xIHZkX4zNMe6Rnwg4YQrWNXK+rNsvwOPpdFppvZO1olE2fIgWhv89TkQErygevbjsZFSIxp+u6w2e5g==} + engines: {node: '>=14.17'} + peerDependencies: + semantic-release: '>=18.0.0-beta.1' + + '@semantic-release/error@3.0.0': + resolution: {integrity: sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw==} + engines: {node: '>=14.17'} + + '@semantic-release/error@4.0.0': + resolution: {integrity: sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==} + engines: {node: '>=18'} + + '@semantic-release/git@10.0.1': + resolution: {integrity: sha512-eWrx5KguUcU2wUPaO6sfvZI0wPafUKAMNC18aXY4EnNcrZL86dEmpNVnC9uMpGZkmZJ9EfCVJBQx4pV4EMGT1w==} + engines: {node: '>=14.17'} + peerDependencies: + semantic-release: '>=18.0.0' + + '@semantic-release/github@9.2.6': + resolution: {integrity: sha512-shi+Lrf6exeNZF+sBhK+P011LSbhmIAoUEgEY6SsxF8irJ+J2stwI5jkyDQ+4gzYyDImzV6LCKdYB9FXnQRWKA==} + engines: {node: '>=18'} + peerDependencies: + semantic-release: '>=20.1.0' + + '@semantic-release/npm@11.0.3': + resolution: {integrity: sha512-KUsozQGhRBAnoVg4UMZj9ep436VEGwT536/jwSqB7vcEfA6oncCUU7UIYTRdLx7GvTtqn0kBjnkfLVkcnBa2YQ==} + engines: {node: ^18.17 || >=20} + peerDependencies: + semantic-release: '>=20.1.0' + + '@semantic-release/release-notes-generator@10.0.3': + resolution: {integrity: sha512-k4x4VhIKneOWoBGHkx0qZogNjCldLPRiAjnIpMnlUh6PtaWXp/T+C9U7/TaNDDtgDa5HMbHl4WlREdxHio6/3w==} + engines: {node: '>=14.17'} + peerDependencies: + semantic-release: '>=18.0.0-beta.1' + + '@semantic-release/release-notes-generator@12.1.0': + resolution: {integrity: sha512-g6M9AjUKAZUZnxaJZnouNBeDNTCUrJ5Ltj+VJ60gJeDaRRahcHsry9HW8yKrnKkKNkx5lbWiEP1FPMqVNQz8Kg==} + engines: {node: ^18.17 || >=20.6.1} + peerDependencies: + semantic-release: '>=20.1.0' + + '@sindresorhus/is@4.6.0': + resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} + engines: {node: '>=10'} + + '@sindresorhus/merge-streams@2.3.0': + resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} + engines: {node: '>=18'} + + '@types/conventional-commits-parser@5.0.1': + resolution: {integrity: sha512-7uz5EHdzz2TqoMfV7ee61Egf5y6NkcO4FB/1iCCQnbeiI1F3xzv3vK5dBCXUCLQgGYS+mUeigK1iKQzvED+QnQ==} + + '@types/minimist@1.2.5': + resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==} + + '@types/node@24.8.1': + resolution: {integrity: sha512-alv65KGRadQVfVcG69MuB4IzdYVpRwMG/mq8KWOaoOdyY617P5ivaDiMCGOFDWD2sAn5Q0mR3mRtUOgm99hL9Q==} + + '@types/normalize-package-data@2.4.4': + resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} + + JSONStream@1.3.5: + resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} + hasBin: true + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + aggregate-error@3.1.0: + resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} + engines: {node: '>=8'} + + aggregate-error@5.0.0: + resolution: {integrity: sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==} + engines: {node: '>=18'} + + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + + ansi-escapes@6.2.1: + resolution: {integrity: sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==} + engines: {node: '>=14.16'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansicolors@0.3.2: + resolution: {integrity: sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + argv-formatter@1.0.0: + resolution: {integrity: sha512-F2+Hkm9xFaRg+GkaNnbwXNDV5O6pnCFEmqyhvfC/Ic5LbgOWjJh3L+mN/s91rxVL3znE7DYVpW0GJFT+4YBgWw==} + + array-ify@1.0.0: + resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} + + arrify@1.0.1: + resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} + engines: {node: '>=0.10.0'} + + before-after-hook@2.2.3: + resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==} + + bottleneck@2.19.5: + resolution: {integrity: sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camelcase-keys@6.2.2: + resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==} + engines: {node: '>=8'} + + camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + + cardinal@2.1.1: + resolution: {integrity: sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw==} + hasBin: true + + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + char-regex@1.0.2: + resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} + engines: {node: '>=10'} + + clean-stack@2.2.0: + resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} + engines: {node: '>=6'} + + clean-stack@5.3.0: + resolution: {integrity: sha512-9ngPTOhYGQqNVSfeJkYXHmF7AGWp4/nN5D/QqNQs3Dvxd1Kk/WpjHfNujKHYUQ/5CoGyOyFNoWSPk5afzP0QVg==} + engines: {node: '>=14.16'} + + cli-table3@0.6.5: + resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} + engines: {node: 10.* || >= 12.*} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + compare-func@2.0.0: + resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} + + config-chain@1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + + conventional-changelog-angular@5.0.13: + resolution: {integrity: sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==} + engines: {node: '>=10'} + + conventional-changelog-angular@7.0.0: + resolution: {integrity: sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==} + engines: {node: '>=16'} + + conventional-changelog-conventionalcommits@7.0.2: + resolution: {integrity: sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==} + engines: {node: '>=16'} + + conventional-changelog-writer@5.0.1: + resolution: {integrity: sha512-5WsuKUfxW7suLblAbFnxAcrvf6r+0b7GvNaWUwUIk0bXMnENP/PEieGKVUQrjPqwPT4o3EPAASBXiY6iHooLOQ==} + engines: {node: '>=10'} + hasBin: true + + conventional-changelog-writer@7.0.1: + resolution: {integrity: sha512-Uo+R9neH3r/foIvQ0MKcsXkX642hdm9odUp7TqgFS7BsalTcjzRlIfWZrZR1gbxOozKucaKt5KAbjW8J8xRSmA==} + engines: {node: '>=16'} + hasBin: true + + conventional-commits-filter@2.0.7: + resolution: {integrity: sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA==} + engines: {node: '>=10'} + + conventional-commits-filter@4.0.0: + resolution: {integrity: sha512-rnpnibcSOdFcdclpFwWa+pPlZJhXE7l+XK04zxhbWrhgpR96h33QLz8hITTXbcYICxVr3HZFtbtUAQ+4LdBo9A==} + engines: {node: '>=16'} + + conventional-commits-parser@3.2.4: + resolution: {integrity: sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==} + engines: {node: '>=10'} + hasBin: true + + conventional-commits-parser@5.0.0: + resolution: {integrity: sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==} + engines: {node: '>=16'} + hasBin: true + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + cosmiconfig-typescript-loader@6.2.0: + resolution: {integrity: sha512-GEN39v7TgdxgIoNcdkRE3uiAzQt3UXLyHbRHD6YoL048XAeOomyxaP+Hh/+2C6C2wYjxJ2onhJcsQp+L4YEkVQ==} + engines: {node: '>=v18'} + peerDependencies: + '@types/node': '*' + cosmiconfig: '>=9' + typescript: '>=5' + + cosmiconfig@8.3.6: + resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + cosmiconfig@9.0.0: + resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + crypto-random-string@4.0.0: + resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==} + engines: {node: '>=12'} + + dargs@8.1.0: + resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==} + engines: {node: '>=12'} + + dateformat@3.0.3: + resolution: {integrity: sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decamelize-keys@1.1.1: + resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} + engines: {node: '>=0.10.0'} + + decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + + deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + + deprecation@2.3.1: + resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + dot-prop@5.3.0: + resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} + engines: {node: '>=8'} + + duplexer2@0.1.4: + resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emojilib@2.4.0: + resolution: {integrity: sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==} + + env-ci@10.0.0: + resolution: {integrity: sha512-U4xcd/utDYFgMh0yWj07R1H6L5fwhVbmxBCpnL0DbVSDZVnsC82HONw0wxtxNkIAcua3KtbomQvIk5xFZGAQJw==} + engines: {node: ^18.17 || >=20.6.1} + + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + + execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-uri@3.1.0: + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + + figures@2.0.0: + resolution: {integrity: sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==} + engines: {node: '>=4'} + + figures@6.1.0: + resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} + engines: {node: '>=18'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up-simple@1.0.1: + resolution: {integrity: sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==} + engines: {node: '>=18'} + + find-up@2.1.0: + resolution: {integrity: sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==} + engines: {node: '>=4'} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + find-up@7.0.0: + resolution: {integrity: sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==} + engines: {node: '>=18'} + + find-versions@5.1.0: + resolution: {integrity: sha512-+iwzCJ7C5v5KgcBuueqVoNiHVoQpwiUK5XFLjf0affFTep+Wcw93tPvmb8tqujDNmzhBDPddnWV/qgWSXgq+Hg==} + engines: {node: '>=12'} + + from2@2.3.0: + resolution: {integrity: sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==} + + fs-extra@11.3.2: + resolution: {integrity: sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==} + engines: {node: '>=14.14'} + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + + get-stream@7.0.1: + resolution: {integrity: sha512-3M8C1EOFN6r8AMUhwUAACIoXZJEOufDU5+0gFFN5uNs6XYOralD2Pqkl7m046va6x77FwposWXbAhPPIOus7mQ==} + engines: {node: '>=16'} + + get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + + git-log-parser@1.2.1: + resolution: {integrity: sha512-PI+sPDvHXNPl5WNOErAK05s3j0lgwUzMN6o8cyQrDaKfT3qd7TmNJKeXX+SknI5I0QhG5fVPAEwSY4tRGDtYoQ==} + + git-raw-commits@4.0.0: + resolution: {integrity: sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==} + engines: {node: '>=16'} + hasBin: true + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + global-directory@4.0.1: + resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} + engines: {node: '>=18'} + + globby@14.1.0: + resolution: {integrity: sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==} + engines: {node: '>=18'} + + graceful-fs@4.2.10: + resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + handlebars@4.7.8: + resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} + engines: {node: '>=0.4.7'} + hasBin: true + + hard-rejection@2.1.0: + resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} + engines: {node: '>=6'} + + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hook-std@3.0.0: + resolution: {integrity: sha512-jHRQzjSDzMtFy34AGj1DN+vq54WVuhSvKgrHf0OMiFQTwDD4L/qqofVEWjLOBMTn5+lCD3fPg32W9yOfnEJTTw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + hosted-git-info@2.8.9: + resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} + + hosted-git-info@4.1.0: + resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} + engines: {node: '>=10'} + + hosted-git-info@7.0.2: + resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==} + engines: {node: ^16.14.0 || >=18.0.0} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + + human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} + + husky@9.1.7: + resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} + engines: {node: '>=18'} + hasBin: true + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + import-from-esm@1.3.4: + resolution: {integrity: sha512-7EyUlPFC0HOlBDpUFGfYstsU7XHxZJKAAMzCT8wZ0hMW7b+hG51LIKTDcsgtz8Pu6YC0HqRVbX+rVUtsGMUKvg==} + engines: {node: '>=16.20'} + + import-from@4.0.0: + resolution: {integrity: sha512-P9J71vT5nLlDeV8FHs5nNxaLbrpfAV5cF5srvbZfpwpcJoM/xZR3hiv+q+SAnuSmuGbXMWud063iIMx/V/EWZQ==} + engines: {node: '>=12.2'} + + import-meta-resolve@4.2.0: + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + + indent-string@5.0.0: + resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} + engines: {node: '>=12'} + + index-to-position@1.2.0: + resolution: {integrity: sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==} + engines: {node: '>=18'} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + ini@4.1.1: + resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + into-stream@6.0.0: + resolution: {integrity: sha512-XHbaOAvP+uFKUFsOgoNPRjLkwB+I22JFPFe5OjTkQ0nwgj6+pSjb4NmB6VMxaPshLiOf+zcpOCBQuLwC1KHhZA==} + engines: {node: '>=10'} + + into-stream@7.0.0: + resolution: {integrity: sha512-2dYz766i9HprMBasCMvHMuazJ7u4WzhJwo5kb3iPSiW/iRYV6uPari3zHoqZlnuaR7V1bEiNMxikhp37rdBXbw==} + engines: {node: '>=12'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-obj@2.0.0: + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} + + is-plain-obj@1.1.0: + resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} + engines: {node: '>=0.10.0'} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + is-text-path@1.0.1: + resolution: {integrity: sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==} + engines: {node: '>=0.10.0'} + + is-text-path@2.0.0: + resolution: {integrity: sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==} + engines: {node: '>=8'} + + is-unicode-supported@2.1.0: + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + engines: {node: '>=18'} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + issue-parser@6.0.0: + resolution: {integrity: sha512-zKa/Dxq2lGsBIXQ7CUZWTHfvxPC2ej0KfO7fIPqLlHB9J2hJ7rGhZ5rilhuufylr4RXYPzJUeFjKxz305OsNlA==} + engines: {node: '>=10.13'} + + java-properties@1.0.2: + resolution: {integrity: sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ==} + engines: {node: '>= 0.6.0'} + + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + hasBin: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + json-parse-better-errors@1.0.2: + resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-stringify-safe@5.0.1: + resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} + + jsonfile@6.2.0: + resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} + + jsonparse@1.3.1: + resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} + engines: {'0': node >= 0.2.0} + + kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + load-json-file@4.0.0: + resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} + engines: {node: '>=4'} + + locate-path@2.0.0: + resolution: {integrity: sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==} + engines: {node: '>=4'} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + locate-path@7.2.0: + resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + lodash-es@4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + + lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + + lodash.capitalize@4.2.1: + resolution: {integrity: sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw==} + + lodash.escaperegexp@4.1.2: + resolution: {integrity: sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==} + + lodash.ismatch@4.4.0: + resolution: {integrity: sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + + lodash.isstring@4.0.1: + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + + lodash.kebabcase@4.1.1: + resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.mergewith@4.6.2: + resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==} + + lodash.snakecase@4.1.1: + resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==} + + lodash.startcase@4.4.0: + resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} + + lodash.uniq@4.5.0: + resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} + + lodash.uniqby@4.7.0: + resolution: {integrity: sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==} + + lodash.upperfirst@4.3.1: + resolution: {integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + + map-obj@1.0.1: + resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} + engines: {node: '>=0.10.0'} + + map-obj@4.3.0: + resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} + engines: {node: '>=8'} + + marked-terminal@6.2.0: + resolution: {integrity: sha512-ubWhwcBFHnXsjYNsu+Wndpg0zhY4CahSpPlA70PlO0rR9r2sZpkyU+rkCsOWH+KMEkx847UpALON+HWgxowFtw==} + engines: {node: '>=16.0.0'} + peerDependencies: + marked: '>=1 <12' + + marked@9.1.6: + resolution: {integrity: sha512-jcByLnIFkd5gSXZmjNvS1TlmRhCXZjIzHYlaGkPlLIekG55JDR2Z4va9tZwCiP+/RDERiNhMOFu01xd6O5ct1Q==} + engines: {node: '>= 16'} + hasBin: true + + meow@12.1.1: + resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} + engines: {node: '>=16.10'} + + meow@8.1.2: + resolution: {integrity: sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==} + engines: {node: '>=10'} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime@4.1.0: + resolution: {integrity: sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==} + engines: {node: '>=16'} + hasBin: true + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + + min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + + minimist-options@4.1.0: + resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} + engines: {node: '>= 6'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + modify-values@1.0.1: + resolution: {integrity: sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==} + engines: {node: '>=0.10.0'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + + nerf-dart@1.0.0: + resolution: {integrity: sha512-EZSPZB70jiVsivaBLYDCyntd5eH8NTSMOn3rB+HxwdmKThGELLdYv8qVIMWvZEFy9w8ZZpW9h9OB32l1rGtj7g==} + + node-emoji@2.2.0: + resolution: {integrity: sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==} + engines: {node: '>=18'} + + normalize-package-data@2.5.0: + resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} + + normalize-package-data@3.0.3: + resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} + engines: {node: '>=10'} + + normalize-package-data@6.0.2: + resolution: {integrity: sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==} + engines: {node: ^16.14.0 || >=18.0.0} + + normalize-url@8.1.0: + resolution: {integrity: sha512-X06Mfd/5aKsRHc0O0J5CUedwnPmnDtLF2+nq+KN9KSDlJHkPuh0JUviWjEWMe0SW/9TDdSLVPuk7L5gGTIA1/w==} + engines: {node: '>=14.16'} + + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + + npm-run-path@5.3.0: + resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + npm@10.9.4: + resolution: {integrity: sha512-OnUG836FwboQIbqtefDNlyR0gTHzIfwRfE3DuiNewBvnMnWEpB0VEXwBlFVgqpNzIgYo/MHh3d2Hel/pszapAA==} + engines: {node: ^18.17.0 || >=20.5.0} + hasBin: true + bundledDependencies: + - '@isaacs/string-locale-compare' + - '@npmcli/arborist' + - '@npmcli/config' + - '@npmcli/fs' + - '@npmcli/map-workspaces' + - '@npmcli/package-json' + - '@npmcli/promise-spawn' + - '@npmcli/redact' + - '@npmcli/run-script' + - '@sigstore/tuf' + - abbrev + - archy + - cacache + - chalk + - ci-info + - cli-columns + - fastest-levenshtein + - fs-minipass + - glob + - graceful-fs + - hosted-git-info + - ini + - init-package-json + - is-cidr + - json-parse-even-better-errors + - libnpmaccess + - libnpmdiff + - libnpmexec + - libnpmfund + - libnpmhook + - libnpmorg + - libnpmpack + - libnpmpublish + - libnpmsearch + - libnpmteam + - libnpmversion + - make-fetch-happen + - minimatch + - minipass + - minipass-pipeline + - ms + - node-gyp + - nopt + - normalize-package-data + - npm-audit-report + - npm-install-checks + - npm-package-arg + - npm-pick-manifest + - npm-profile + - npm-registry-fetch + - npm-user-validate + - p-map + - pacote + - parse-conflict-json + - proc-log + - qrcode-terminal + - read + - semver + - spdx-expression-parse + - ssri + - supports-color + - tar + - text-table + - tiny-relative-date + - treeverse + - validate-npm-package-name + - which + - write-file-atomic + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + + p-each-series@3.0.0: + resolution: {integrity: sha512-lastgtAdoH9YaLyDa5i5z64q+kzOcQHsQ5SsZJD3q0VEyI8mq872S3geuNbRUQLVAE9siMfgKrpj7MloKFHruw==} + engines: {node: '>=12'} + + p-filter@4.1.0: + resolution: {integrity: sha512-37/tPdZ3oJwHaS3gNJdenCDB3Tz26i9sjhnguBtvN0vYlRIiDNnvTWkuh+0hETV9rLPdJ3rlL3yVOYPIAnM8rw==} + engines: {node: '>=18'} + + p-is-promise@3.0.0: + resolution: {integrity: sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==} + engines: {node: '>=8'} + + p-limit@1.3.0: + resolution: {integrity: sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==} + engines: {node: '>=4'} + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-locate@2.0.0: + resolution: {integrity: sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==} + engines: {node: '>=4'} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-locate@6.0.0: + resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-map@7.0.3: + resolution: {integrity: sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==} + engines: {node: '>=18'} + + p-reduce@2.1.0: + resolution: {integrity: sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==} + engines: {node: '>=8'} + + p-reduce@3.0.0: + resolution: {integrity: sha512-xsrIUgI0Kn6iyDYm9StOpOeK29XM1aboGji26+QEortiFST1hGZaUQOLhtEbqHErPpGW/aSz6allwK2qcptp0Q==} + engines: {node: '>=12'} + + p-try@1.0.0: + resolution: {integrity: sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==} + engines: {node: '>=4'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-json@4.0.0: + resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} + engines: {node: '>=4'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + parse-json@8.3.0: + resolution: {integrity: sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==} + engines: {node: '>=18'} + + path-exists@3.0.0: + resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} + engines: {node: '>=4'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-exists@5.0.0: + resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + path-type@6.0.0: + resolution: {integrity: sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==} + engines: {node: '>=18'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + pify@3.0.0: + resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} + engines: {node: '>=4'} + + pkg-conf@2.1.0: + resolution: {integrity: sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g==} + engines: {node: '>=4'} + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + + q@1.5.1: + resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==} + engines: {node: '>=0.6.0', teleport: '>=0.2.0'} + deprecated: |- + You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other. + + (For a CapTP with native promises, see @endo/eventual-send and @endo/captp) + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + quick-lru@4.0.1: + resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==} + engines: {node: '>=8'} + + rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + + read-pkg-up@11.0.0: + resolution: {integrity: sha512-LOVbvF1Q0SZdjClSefZ0Nz5z8u+tIE7mV5NibzmE9VYmDe9CaBbAVtz1veOSZbofrdsilxuDAYnFenukZVp8/Q==} + engines: {node: '>=18'} + deprecated: Renamed to read-package-up + + read-pkg-up@7.0.1: + resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} + engines: {node: '>=8'} + + read-pkg@5.2.0: + resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} + engines: {node: '>=8'} + + read-pkg@9.0.1: + resolution: {integrity: sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==} + engines: {node: '>=18'} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + redent@3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + + redeyed@2.1.1: + resolution: {integrity: sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ==} + + registry-auth-token@5.1.0: + resolution: {integrity: sha512-GdekYuwLXLxMuFTwAPg5UKGLW/UXzQrZvH/Zj791BQif5T05T0RsaLfHc9q3ZOKi7n+BoprPD9mJ0O0k4xzUlw==} + engines: {node: '>=14'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve@1.22.10: + resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + engines: {node: '>= 0.4'} + hasBin: true + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + semantic-release@22.0.12: + resolution: {integrity: sha512-0mhiCR/4sZb00RVFJIUlMuiBkW3NMpVIW2Gse7noqEMoFGkvfPPAImEQbkBV8xga4KOPP4FdTRYuLLy32R1fPw==} + engines: {node: ^18.17 || >=20.6.1} + hasBin: true + + semver-diff@4.0.0: + resolution: {integrity: sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==} + engines: {node: '>=12'} + + semver-regex@4.0.5: + resolution: {integrity: sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==} + engines: {node: '>=12'} + + semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + engines: {node: '>=10'} + hasBin: true + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + signale@1.4.0: + resolution: {integrity: sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==} + engines: {node: '>=6'} + + skin-tone@2.0.0: + resolution: {integrity: sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==} + engines: {node: '>=8'} + + slash@5.1.0: + resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} + engines: {node: '>=14.16'} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + spawn-error-forwarder@1.0.0: + resolution: {integrity: sha512-gRjMgK5uFjbCvdibeGJuy3I5OYz6VLoVdsOJdA6wV0WlfQVLFueoqMxwwYD9RODdgb6oUIvlRlsyFSiQkMKu0g==} + + spdx-correct@3.2.0: + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + + spdx-exceptions@2.5.0: + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + + spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + + spdx-license-ids@3.0.22: + resolution: {integrity: sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==} + + split2@1.0.0: + resolution: {integrity: sha512-NKywug4u4pX/AZBB1FCPzZ6/7O+Xhz1qMVbzTvvKvikjO99oPN87SkK08mEY9P63/5lWjK+wgOOgApnTg5r6qg==} + + split2@3.2.2: + resolution: {integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + split@1.0.1: + resolution: {integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==} + + stream-combiner2@1.1.1: + resolution: {integrity: sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + + strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + + strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + + strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-hyperlinks@3.2.0: + resolution: {integrity: sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==} + engines: {node: '>=14.18'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + temp-dir@3.0.0: + resolution: {integrity: sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==} + engines: {node: '>=14.16'} + + tempy@3.1.0: + resolution: {integrity: sha512-7jDLIdD2Zp0bDe5r3D2qtkd1QOCacylBuL7oa4udvN6v2pqr4+LcCr67C8DR1zkpaZ8XosF5m1yQSabKAW6f2g==} + engines: {node: '>=14.16'} + + text-extensions@1.9.0: + resolution: {integrity: sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==} + engines: {node: '>=0.10'} + + text-extensions@2.4.0: + resolution: {integrity: sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==} + engines: {node: '>=8'} + + through2@2.0.5: + resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} + + through2@4.0.2: + resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==} + + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + + tinyexec@1.0.1: + resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + traverse@0.6.8: + resolution: {integrity: sha512-aXJDbk6SnumuaZSANd21XAo15ucCDE38H4fkqiGsc3MhCK+wOlZvLP9cB/TvpHT0mOyWgC4Z8EwRlzqYSUzdsA==} + engines: {node: '>= 0.4'} + + trim-newlines@3.0.1: + resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==} + engines: {node: '>=8'} + + type-fest@0.18.1: + resolution: {integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==} + engines: {node: '>=10'} + + type-fest@0.6.0: + resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} + engines: {node: '>=8'} + + type-fest@0.8.1: + resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} + engines: {node: '>=8'} + + type-fest@1.4.0: + resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==} + engines: {node: '>=10'} + + type-fest@2.19.0: + resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} + engines: {node: '>=12.20'} + + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + uglify-js@3.19.3: + resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} + engines: {node: '>=0.8.0'} + hasBin: true + + undici-types@7.14.0: + resolution: {integrity: sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==} + + unicode-emoji-modifier-base@1.0.0: + resolution: {integrity: sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==} + engines: {node: '>=4'} + + unicorn-magic@0.1.0: + resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} + engines: {node: '>=18'} + + unicorn-magic@0.3.0: + resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} + engines: {node: '>=18'} + + unique-string@3.0.0: + resolution: {integrity: sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==} + engines: {node: '>=12'} + + universal-user-agent@6.0.1: + resolution: {integrity: sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + url-join@5.0.0: + resolution: {integrity: sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + validate-npm-package-license@3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + wordwrap@1.0.0: + resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yocto-queue@1.2.1: + resolution: {integrity: sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==} + engines: {node: '>=12.20'} + +snapshots: + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.27.1 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/helper-validator-identifier@7.27.1': {} + + '@colors/colors@1.5.0': + optional: true + + '@commitlint/cli@20.1.0(@types/node@24.8.1)(typescript@5.9.3)': + dependencies: + '@commitlint/format': 20.0.0 + '@commitlint/lint': 20.0.0 + '@commitlint/load': 20.1.0(@types/node@24.8.1)(typescript@5.9.3) + '@commitlint/read': 20.0.0 + '@commitlint/types': 20.0.0 + tinyexec: 1.0.1 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - typescript + + '@commitlint/config-conventional@20.0.0': + dependencies: + '@commitlint/types': 20.0.0 + conventional-changelog-conventionalcommits: 7.0.2 + + '@commitlint/config-validator@20.0.0': + dependencies: + '@commitlint/types': 20.0.0 + ajv: 8.17.1 + + '@commitlint/ensure@20.0.0': + dependencies: + '@commitlint/types': 20.0.0 + lodash.camelcase: 4.3.0 + lodash.kebabcase: 4.1.1 + lodash.snakecase: 4.1.1 + lodash.startcase: 4.4.0 + lodash.upperfirst: 4.3.1 + + '@commitlint/execute-rule@20.0.0': {} + + '@commitlint/format@20.0.0': + dependencies: + '@commitlint/types': 20.0.0 + chalk: 5.6.2 + + '@commitlint/is-ignored@20.0.0': + dependencies: + '@commitlint/types': 20.0.0 + semver: 7.7.3 + + '@commitlint/lint@20.0.0': + dependencies: + '@commitlint/is-ignored': 20.0.0 + '@commitlint/parse': 20.0.0 + '@commitlint/rules': 20.0.0 + '@commitlint/types': 20.0.0 + + '@commitlint/load@20.1.0(@types/node@24.8.1)(typescript@5.9.3)': + dependencies: + '@commitlint/config-validator': 20.0.0 + '@commitlint/execute-rule': 20.0.0 + '@commitlint/resolve-extends': 20.1.0 + '@commitlint/types': 20.0.0 + chalk: 5.6.2 + cosmiconfig: 9.0.0(typescript@5.9.3) + cosmiconfig-typescript-loader: 6.2.0(@types/node@24.8.1)(cosmiconfig@9.0.0(typescript@5.9.3))(typescript@5.9.3) + lodash.isplainobject: 4.0.6 + lodash.merge: 4.6.2 + lodash.uniq: 4.5.0 + transitivePeerDependencies: + - '@types/node' + - typescript + + '@commitlint/message@20.0.0': {} + + '@commitlint/parse@20.0.0': + dependencies: + '@commitlint/types': 20.0.0 + conventional-changelog-angular: 7.0.0 + conventional-commits-parser: 5.0.0 + + '@commitlint/read@20.0.0': + dependencies: + '@commitlint/top-level': 20.0.0 + '@commitlint/types': 20.0.0 + git-raw-commits: 4.0.0 + minimist: 1.2.8 + tinyexec: 1.0.1 + + '@commitlint/resolve-extends@20.1.0': + dependencies: + '@commitlint/config-validator': 20.0.0 + '@commitlint/types': 20.0.0 + global-directory: 4.0.1 + import-meta-resolve: 4.2.0 + lodash.mergewith: 4.6.2 + resolve-from: 5.0.0 + + '@commitlint/rules@20.0.0': + dependencies: + '@commitlint/ensure': 20.0.0 + '@commitlint/message': 20.0.0 + '@commitlint/to-lines': 20.0.0 + '@commitlint/types': 20.0.0 + + '@commitlint/to-lines@20.0.0': {} + + '@commitlint/top-level@20.0.0': + dependencies: + find-up: 7.0.0 + + '@commitlint/types@20.0.0': + dependencies: + '@types/conventional-commits-parser': 5.0.1 + chalk: 5.6.2 + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.19.1 + + '@octokit/auth-token@4.0.0': {} + + '@octokit/core@5.2.2': + dependencies: + '@octokit/auth-token': 4.0.0 + '@octokit/graphql': 7.1.1 + '@octokit/request': 8.4.1 + '@octokit/request-error': 5.1.1 + '@octokit/types': 13.10.0 + before-after-hook: 2.2.3 + universal-user-agent: 6.0.1 + + '@octokit/endpoint@9.0.6': + dependencies: + '@octokit/types': 13.10.0 + universal-user-agent: 6.0.1 + + '@octokit/graphql@7.1.1': + dependencies: + '@octokit/request': 8.4.1 + '@octokit/types': 13.10.0 + universal-user-agent: 6.0.1 + + '@octokit/openapi-types@20.0.0': {} + + '@octokit/openapi-types@24.2.0': {} + + '@octokit/plugin-paginate-rest@9.2.2(@octokit/core@5.2.2)': + dependencies: + '@octokit/core': 5.2.2 + '@octokit/types': 12.6.0 + + '@octokit/plugin-retry@6.1.0(@octokit/core@5.2.2)': + dependencies: + '@octokit/core': 5.2.2 + '@octokit/request-error': 5.1.1 + '@octokit/types': 13.10.0 + bottleneck: 2.19.5 + + '@octokit/plugin-throttling@8.2.0(@octokit/core@5.2.2)': + dependencies: + '@octokit/core': 5.2.2 + '@octokit/types': 12.6.0 + bottleneck: 2.19.5 + + '@octokit/request-error@5.1.1': + dependencies: + '@octokit/types': 13.10.0 + deprecation: 2.3.1 + once: 1.4.0 + + '@octokit/request@8.4.1': + dependencies: + '@octokit/endpoint': 9.0.6 + '@octokit/request-error': 5.1.1 + '@octokit/types': 13.10.0 + universal-user-agent: 6.0.1 + + '@octokit/types@12.6.0': + dependencies: + '@octokit/openapi-types': 20.0.0 + + '@octokit/types@13.10.0': + dependencies: + '@octokit/openapi-types': 24.2.0 + + '@pnpm/config.env-replace@1.1.0': {} + + '@pnpm/network.ca-file@1.0.2': + dependencies: + graceful-fs: 4.2.10 + + '@pnpm/npm-conf@2.3.1': + dependencies: + '@pnpm/config.env-replace': 1.1.0 + '@pnpm/network.ca-file': 1.0.2 + config-chain: 1.1.13 + + '@semantic-release/changelog@6.0.3(semantic-release@22.0.12(typescript@5.9.3))': + dependencies: + '@semantic-release/error': 3.0.0 + aggregate-error: 3.1.0 + fs-extra: 11.3.2 + lodash: 4.17.21 + semantic-release: 22.0.12(typescript@5.9.3) + + '@semantic-release/commit-analyzer@11.1.0(semantic-release@22.0.12(typescript@5.9.3))': + dependencies: + conventional-changelog-angular: 7.0.0 + conventional-commits-filter: 4.0.0 + conventional-commits-parser: 5.0.0 + debug: 4.4.3 + import-from-esm: 1.3.4 + lodash-es: 4.17.21 + micromatch: 4.0.8 + semantic-release: 22.0.12(typescript@5.9.3) + transitivePeerDependencies: + - supports-color + + '@semantic-release/commit-analyzer@9.0.2(semantic-release@22.0.12(typescript@5.9.3))': + dependencies: + conventional-changelog-angular: 5.0.13 + conventional-commits-filter: 2.0.7 + conventional-commits-parser: 3.2.4 + debug: 4.4.3 + import-from: 4.0.0 + lodash: 4.17.21 + micromatch: 4.0.8 + semantic-release: 22.0.12(typescript@5.9.3) + transitivePeerDependencies: + - supports-color + + '@semantic-release/error@3.0.0': {} + + '@semantic-release/error@4.0.0': {} + + '@semantic-release/git@10.0.1(semantic-release@22.0.12(typescript@5.9.3))': + dependencies: + '@semantic-release/error': 3.0.0 + aggregate-error: 3.1.0 + debug: 4.4.3 + dir-glob: 3.0.1 + execa: 5.1.1 + lodash: 4.17.21 + micromatch: 4.0.8 + p-reduce: 2.1.0 + semantic-release: 22.0.12(typescript@5.9.3) + transitivePeerDependencies: + - supports-color + + '@semantic-release/github@9.2.6(semantic-release@22.0.12(typescript@5.9.3))': + dependencies: + '@octokit/core': 5.2.2 + '@octokit/plugin-paginate-rest': 9.2.2(@octokit/core@5.2.2) + '@octokit/plugin-retry': 6.1.0(@octokit/core@5.2.2) + '@octokit/plugin-throttling': 8.2.0(@octokit/core@5.2.2) + '@semantic-release/error': 4.0.0 + aggregate-error: 5.0.0 + debug: 4.4.3 + dir-glob: 3.0.1 + globby: 14.1.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + issue-parser: 6.0.0 + lodash-es: 4.17.21 + mime: 4.1.0 + p-filter: 4.1.0 + semantic-release: 22.0.12(typescript@5.9.3) + url-join: 5.0.0 + transitivePeerDependencies: + - supports-color + + '@semantic-release/npm@11.0.3(semantic-release@22.0.12(typescript@5.9.3))': + dependencies: + '@semantic-release/error': 4.0.0 + aggregate-error: 5.0.0 + execa: 8.0.1 + fs-extra: 11.3.2 + lodash-es: 4.17.21 + nerf-dart: 1.0.0 + normalize-url: 8.1.0 + npm: 10.9.4 + rc: 1.2.8 + read-pkg: 9.0.1 + registry-auth-token: 5.1.0 + semantic-release: 22.0.12(typescript@5.9.3) + semver: 7.7.3 + tempy: 3.1.0 + + '@semantic-release/release-notes-generator@10.0.3(semantic-release@22.0.12(typescript@5.9.3))': + dependencies: + conventional-changelog-angular: 5.0.13 + conventional-changelog-writer: 5.0.1 + conventional-commits-filter: 2.0.7 + conventional-commits-parser: 3.2.4 + debug: 4.4.3 + get-stream: 6.0.1 + import-from: 4.0.0 + into-stream: 6.0.0 + lodash: 4.17.21 + read-pkg-up: 7.0.1 + semantic-release: 22.0.12(typescript@5.9.3) + transitivePeerDependencies: + - supports-color + + '@semantic-release/release-notes-generator@12.1.0(semantic-release@22.0.12(typescript@5.9.3))': + dependencies: + conventional-changelog-angular: 7.0.0 + conventional-changelog-writer: 7.0.1 + conventional-commits-filter: 4.0.0 + conventional-commits-parser: 5.0.0 + debug: 4.4.3 + get-stream: 7.0.1 + import-from-esm: 1.3.4 + into-stream: 7.0.0 + lodash-es: 4.17.21 + read-pkg-up: 11.0.0 + semantic-release: 22.0.12(typescript@5.9.3) + transitivePeerDependencies: + - supports-color + + '@sindresorhus/is@4.6.0': {} + + '@sindresorhus/merge-streams@2.3.0': {} + + '@types/conventional-commits-parser@5.0.1': + dependencies: + '@types/node': 24.8.1 + + '@types/minimist@1.2.5': {} + + '@types/node@24.8.1': + dependencies: + undici-types: 7.14.0 + + '@types/normalize-package-data@2.4.4': {} + + JSONStream@1.3.5: + dependencies: + jsonparse: 1.3.1 + through: 2.3.8 + + agent-base@7.1.4: {} + + aggregate-error@3.1.0: + dependencies: + clean-stack: 2.2.0 + indent-string: 4.0.0 + + aggregate-error@5.0.0: + dependencies: + clean-stack: 5.3.0 + indent-string: 5.0.0 + + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ansi-escapes@6.2.1: {} + + ansi-regex@5.0.1: {} + + ansi-styles@3.2.1: + dependencies: + color-convert: 1.9.3 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansicolors@0.3.2: {} + + argparse@2.0.1: {} + + argv-formatter@1.0.0: {} + + array-ify@1.0.0: {} + + arrify@1.0.1: {} + + before-after-hook@2.2.3: {} + + bottleneck@2.19.5: {} + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + callsites@3.1.0: {} + + camelcase-keys@6.2.2: + dependencies: + camelcase: 5.3.1 + map-obj: 4.3.0 + quick-lru: 4.0.1 + + camelcase@5.3.1: {} + + cardinal@2.1.1: + dependencies: + ansicolors: 0.3.2 + redeyed: 2.1.1 + + chalk@2.4.2: + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + + chalk@5.6.2: {} + + char-regex@1.0.2: {} + + clean-stack@2.2.0: {} + + clean-stack@5.3.0: + dependencies: + escape-string-regexp: 5.0.0 + + cli-table3@0.6.5: + dependencies: + string-width: 4.2.3 + optionalDependencies: + '@colors/colors': 1.5.0 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + color-convert@1.9.3: + dependencies: + color-name: 1.1.3 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.3: {} + + color-name@1.1.4: {} + + compare-func@2.0.0: + dependencies: + array-ify: 1.0.0 + dot-prop: 5.3.0 + + config-chain@1.1.13: + dependencies: + ini: 1.3.8 + proto-list: 1.2.4 + + conventional-changelog-angular@5.0.13: + dependencies: + compare-func: 2.0.0 + q: 1.5.1 + + conventional-changelog-angular@7.0.0: + dependencies: + compare-func: 2.0.0 + + conventional-changelog-conventionalcommits@7.0.2: + dependencies: + compare-func: 2.0.0 + + conventional-changelog-writer@5.0.1: + dependencies: + conventional-commits-filter: 2.0.7 + dateformat: 3.0.3 + handlebars: 4.7.8 + json-stringify-safe: 5.0.1 + lodash: 4.17.21 + meow: 8.1.2 + semver: 6.3.1 + split: 1.0.1 + through2: 4.0.2 + + conventional-changelog-writer@7.0.1: + dependencies: + conventional-commits-filter: 4.0.0 + handlebars: 4.7.8 + json-stringify-safe: 5.0.1 + meow: 12.1.1 + semver: 7.7.3 + split2: 4.2.0 + + conventional-commits-filter@2.0.7: + dependencies: + lodash.ismatch: 4.4.0 + modify-values: 1.0.1 + + conventional-commits-filter@4.0.0: {} + + conventional-commits-parser@3.2.4: + dependencies: + JSONStream: 1.3.5 + is-text-path: 1.0.1 + lodash: 4.17.21 + meow: 8.1.2 + split2: 3.2.2 + through2: 4.0.2 + + conventional-commits-parser@5.0.0: + dependencies: + JSONStream: 1.3.5 + is-text-path: 2.0.0 + meow: 12.1.1 + split2: 4.2.0 + + core-util-is@1.0.3: {} + + cosmiconfig-typescript-loader@6.2.0(@types/node@24.8.1)(cosmiconfig@9.0.0(typescript@5.9.3))(typescript@5.9.3): + dependencies: + '@types/node': 24.8.1 + cosmiconfig: 9.0.0(typescript@5.9.3) + jiti: 2.6.1 + typescript: 5.9.3 + + cosmiconfig@8.3.6(typescript@5.9.3): + dependencies: + import-fresh: 3.3.1 + js-yaml: 4.1.0 + parse-json: 5.2.0 + path-type: 4.0.0 + optionalDependencies: + typescript: 5.9.3 + + cosmiconfig@9.0.0(typescript@5.9.3): + dependencies: + env-paths: 2.2.1 + import-fresh: 3.3.1 + js-yaml: 4.1.0 + parse-json: 5.2.0 + optionalDependencies: + typescript: 5.9.3 + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + crypto-random-string@4.0.0: + dependencies: + type-fest: 1.4.0 + + dargs@8.1.0: {} + + dateformat@3.0.3: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decamelize-keys@1.1.1: + dependencies: + decamelize: 1.2.0 + map-obj: 1.0.1 + + decamelize@1.2.0: {} + + deep-extend@0.6.0: {} + + deprecation@2.3.1: {} + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + dot-prop@5.3.0: + dependencies: + is-obj: 2.0.0 + + duplexer2@0.1.4: + dependencies: + readable-stream: 2.3.8 + + emoji-regex@8.0.0: {} + + emojilib@2.4.0: {} + + env-ci@10.0.0: + dependencies: + execa: 8.0.1 + java-properties: 1.0.2 + + env-paths@2.2.1: {} + + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + + escalade@3.2.0: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@5.0.0: {} + + esprima@4.0.1: {} + + execa@5.1.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + + execa@8.0.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 8.0.1 + human-signals: 5.0.0 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.3.0 + onetime: 6.0.0 + signal-exit: 4.1.0 + strip-final-newline: 3.0.0 + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-uri@3.1.0: {} + + fastq@1.19.1: + dependencies: + reusify: 1.1.0 + + figures@2.0.0: + dependencies: + escape-string-regexp: 1.0.5 + + figures@6.1.0: + dependencies: + is-unicode-supported: 2.1.0 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up-simple@1.0.1: {} + + find-up@2.1.0: + dependencies: + locate-path: 2.0.0 + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + + find-up@7.0.0: + dependencies: + locate-path: 7.2.0 + path-exists: 5.0.0 + unicorn-magic: 0.1.0 + + find-versions@5.1.0: + dependencies: + semver-regex: 4.0.5 + + from2@2.3.0: + dependencies: + inherits: 2.0.4 + readable-stream: 2.3.8 + + fs-extra@11.3.2: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.0 + universalify: 2.0.1 + + function-bind@1.1.2: {} + + get-caller-file@2.0.5: {} + + get-stream@6.0.1: {} + + get-stream@7.0.1: {} + + get-stream@8.0.1: {} + + git-log-parser@1.2.1: + dependencies: + argv-formatter: 1.0.0 + spawn-error-forwarder: 1.0.0 + split2: 1.0.0 + stream-combiner2: 1.1.1 + through2: 2.0.5 + traverse: 0.6.8 + + git-raw-commits@4.0.0: + dependencies: + dargs: 8.1.0 + meow: 12.1.1 + split2: 4.2.0 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + global-directory@4.0.1: + dependencies: + ini: 4.1.1 + + globby@14.1.0: + dependencies: + '@sindresorhus/merge-streams': 2.3.0 + fast-glob: 3.3.3 + ignore: 7.0.5 + path-type: 6.0.0 + slash: 5.1.0 + unicorn-magic: 0.3.0 + + graceful-fs@4.2.10: {} + + graceful-fs@4.2.11: {} + + handlebars@4.7.8: + dependencies: + minimist: 1.2.8 + neo-async: 2.6.2 + source-map: 0.6.1 + wordwrap: 1.0.0 + optionalDependencies: + uglify-js: 3.19.3 + + hard-rejection@2.1.0: {} + + has-flag@3.0.0: {} + + has-flag@4.0.0: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + hook-std@3.0.0: {} + + hosted-git-info@2.8.9: {} + + hosted-git-info@4.1.0: + dependencies: + lru-cache: 6.0.0 + + hosted-git-info@7.0.2: + dependencies: + lru-cache: 10.4.3 + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + human-signals@2.1.0: {} + + human-signals@5.0.0: {} + + husky@9.1.7: {} + + ignore@7.0.5: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + import-from-esm@1.3.4: + dependencies: + debug: 4.4.3 + import-meta-resolve: 4.2.0 + transitivePeerDependencies: + - supports-color + + import-from@4.0.0: {} + + import-meta-resolve@4.2.0: {} + + indent-string@4.0.0: {} + + indent-string@5.0.0: {} + + index-to-position@1.2.0: {} + + inherits@2.0.4: {} + + ini@1.3.8: {} + + ini@4.1.1: {} + + into-stream@6.0.0: + dependencies: + from2: 2.3.0 + p-is-promise: 3.0.0 + + into-stream@7.0.0: + dependencies: + from2: 2.3.0 + p-is-promise: 3.0.0 + + is-arrayish@0.2.1: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + + is-obj@2.0.0: {} + + is-plain-obj@1.1.0: {} + + is-stream@2.0.1: {} + + is-stream@3.0.0: {} + + is-text-path@1.0.1: + dependencies: + text-extensions: 1.9.0 + + is-text-path@2.0.0: + dependencies: + text-extensions: 2.4.0 + + is-unicode-supported@2.1.0: {} + + isarray@1.0.0: {} + + isexe@2.0.0: {} + + issue-parser@6.0.0: + dependencies: + lodash.capitalize: 4.2.1 + lodash.escaperegexp: 4.1.2 + lodash.isplainobject: 4.0.6 + lodash.isstring: 4.0.1 + lodash.uniqby: 4.7.0 + + java-properties@1.0.2: {} + + jiti@2.6.1: {} + + js-tokens@4.0.0: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + json-parse-better-errors@1.0.2: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@1.0.0: {} + + json-stringify-safe@5.0.1: {} + + jsonfile@6.2.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + jsonparse@1.3.1: {} + + kind-of@6.0.3: {} + + lines-and-columns@1.2.4: {} + + load-json-file@4.0.0: + dependencies: + graceful-fs: 4.2.11 + parse-json: 4.0.0 + pify: 3.0.0 + strip-bom: 3.0.0 + + locate-path@2.0.0: + dependencies: + p-locate: 2.0.0 + path-exists: 3.0.0 + + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + + locate-path@7.2.0: + dependencies: + p-locate: 6.0.0 + + lodash-es@4.17.21: {} + + lodash.camelcase@4.3.0: {} + + lodash.capitalize@4.2.1: {} + + lodash.escaperegexp@4.1.2: {} + + lodash.ismatch@4.4.0: {} + + lodash.isplainobject@4.0.6: {} + + lodash.isstring@4.0.1: {} + + lodash.kebabcase@4.1.1: {} + + lodash.merge@4.6.2: {} + + lodash.mergewith@4.6.2: {} + + lodash.snakecase@4.1.1: {} + + lodash.startcase@4.4.0: {} + + lodash.uniq@4.5.0: {} + + lodash.uniqby@4.7.0: {} + + lodash.upperfirst@4.3.1: {} + + lodash@4.17.21: {} + + lru-cache@10.4.3: {} + + lru-cache@6.0.0: + dependencies: + yallist: 4.0.0 + + map-obj@1.0.1: {} + + map-obj@4.3.0: {} + + marked-terminal@6.2.0(marked@9.1.6): + dependencies: + ansi-escapes: 6.2.1 + cardinal: 2.1.1 + chalk: 5.6.2 + cli-table3: 0.6.5 + marked: 9.1.6 + node-emoji: 2.2.0 + supports-hyperlinks: 3.2.0 + + marked@9.1.6: {} + + meow@12.1.1: {} + + meow@8.1.2: + dependencies: + '@types/minimist': 1.2.5 + camelcase-keys: 6.2.2 + decamelize-keys: 1.1.1 + hard-rejection: 2.1.0 + minimist-options: 4.1.0 + normalize-package-data: 3.0.3 + read-pkg-up: 7.0.1 + redent: 3.0.0 + trim-newlines: 3.0.1 + type-fest: 0.18.1 + yargs-parser: 20.2.9 + + merge-stream@2.0.0: {} + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime@4.1.0: {} + + mimic-fn@2.1.0: {} + + mimic-fn@4.0.0: {} + + min-indent@1.0.1: {} + + minimist-options@4.1.0: + dependencies: + arrify: 1.0.1 + is-plain-obj: 1.1.0 + kind-of: 6.0.3 + + minimist@1.2.8: {} + + modify-values@1.0.1: {} + + ms@2.1.3: {} + + neo-async@2.6.2: {} + + nerf-dart@1.0.0: {} + + node-emoji@2.2.0: + dependencies: + '@sindresorhus/is': 4.6.0 + char-regex: 1.0.2 + emojilib: 2.4.0 + skin-tone: 2.0.0 + + normalize-package-data@2.5.0: + dependencies: + hosted-git-info: 2.8.9 + resolve: 1.22.10 + semver: 5.7.2 + validate-npm-package-license: 3.0.4 + + normalize-package-data@3.0.3: + dependencies: + hosted-git-info: 4.1.0 + is-core-module: 2.16.1 + semver: 7.7.3 + validate-npm-package-license: 3.0.4 + + normalize-package-data@6.0.2: + dependencies: + hosted-git-info: 7.0.2 + semver: 7.7.3 + validate-npm-package-license: 3.0.4 + + normalize-url@8.1.0: {} + + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + + npm-run-path@5.3.0: + dependencies: + path-key: 4.0.0 + + npm@10.9.4: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + onetime@6.0.0: + dependencies: + mimic-fn: 4.0.0 + + p-each-series@3.0.0: {} + + p-filter@4.1.0: + dependencies: + p-map: 7.0.3 + + p-is-promise@3.0.0: {} + + p-limit@1.3.0: + dependencies: + p-try: 1.0.0 + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-limit@4.0.0: + dependencies: + yocto-queue: 1.2.1 + + p-locate@2.0.0: + dependencies: + p-limit: 1.3.0 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-locate@6.0.0: + dependencies: + p-limit: 4.0.0 + + p-map@7.0.3: {} + + p-reduce@2.1.0: {} + + p-reduce@3.0.0: {} + + p-try@1.0.0: {} + + p-try@2.2.0: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-json@4.0.0: + dependencies: + error-ex: 1.3.4 + json-parse-better-errors: 1.0.2 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.27.1 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + parse-json@8.3.0: + dependencies: + '@babel/code-frame': 7.27.1 + index-to-position: 1.2.0 + type-fest: 4.41.0 + + path-exists@3.0.0: {} + + path-exists@4.0.0: {} + + path-exists@5.0.0: {} + + path-key@3.1.1: {} + + path-key@4.0.0: {} + + path-parse@1.0.7: {} + + path-type@4.0.0: {} + + path-type@6.0.0: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + pify@3.0.0: {} + + pkg-conf@2.1.0: + dependencies: + find-up: 2.1.0 + load-json-file: 4.0.0 + + process-nextick-args@2.0.1: {} + + proto-list@1.2.4: {} + + q@1.5.1: {} + + queue-microtask@1.2.3: {} + + quick-lru@4.0.1: {} + + rc@1.2.8: + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + + read-pkg-up@11.0.0: + dependencies: + find-up-simple: 1.0.1 + read-pkg: 9.0.1 + type-fest: 4.41.0 + + read-pkg-up@7.0.1: + dependencies: + find-up: 4.1.0 + read-pkg: 5.2.0 + type-fest: 0.8.1 + + read-pkg@5.2.0: + dependencies: + '@types/normalize-package-data': 2.4.4 + normalize-package-data: 2.5.0 + parse-json: 5.2.0 + type-fest: 0.6.0 + + read-pkg@9.0.1: + dependencies: + '@types/normalize-package-data': 2.4.4 + normalize-package-data: 6.0.2 + parse-json: 8.3.0 + type-fest: 4.41.0 + unicorn-magic: 0.1.0 + + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + redent@3.0.0: + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + + redeyed@2.1.1: + dependencies: + esprima: 4.0.1 + + registry-auth-token@5.1.0: + dependencies: + '@pnpm/npm-conf': 2.3.1 + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + resolve-from@4.0.0: {} + + resolve-from@5.0.0: {} + + resolve@1.22.10: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + reusify@1.1.0: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + safe-buffer@5.1.2: {} + + safe-buffer@5.2.1: {} + + semantic-release@22.0.12(typescript@5.9.3): + dependencies: + '@semantic-release/commit-analyzer': 11.1.0(semantic-release@22.0.12(typescript@5.9.3)) + '@semantic-release/error': 4.0.0 + '@semantic-release/github': 9.2.6(semantic-release@22.0.12(typescript@5.9.3)) + '@semantic-release/npm': 11.0.3(semantic-release@22.0.12(typescript@5.9.3)) + '@semantic-release/release-notes-generator': 12.1.0(semantic-release@22.0.12(typescript@5.9.3)) + aggregate-error: 5.0.0 + cosmiconfig: 8.3.6(typescript@5.9.3) + debug: 4.4.3 + env-ci: 10.0.0 + execa: 8.0.1 + figures: 6.1.0 + find-versions: 5.1.0 + get-stream: 6.0.1 + git-log-parser: 1.2.1 + hook-std: 3.0.0 + hosted-git-info: 7.0.2 + import-from-esm: 1.3.4 + lodash-es: 4.17.21 + marked: 9.1.6 + marked-terminal: 6.2.0(marked@9.1.6) + micromatch: 4.0.8 + p-each-series: 3.0.0 + p-reduce: 3.0.0 + read-pkg-up: 11.0.0 + resolve-from: 5.0.0 + semver: 7.7.3 + semver-diff: 4.0.0 + signale: 1.4.0 + yargs: 17.7.2 + transitivePeerDependencies: + - supports-color + - typescript + + semver-diff@4.0.0: + dependencies: + semver: 7.7.3 + + semver-regex@4.0.5: {} + + semver@5.7.2: {} + + semver@6.3.1: {} + + semver@7.7.3: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + signal-exit@3.0.7: {} + + signal-exit@4.1.0: {} + + signale@1.4.0: + dependencies: + chalk: 2.4.2 + figures: 2.0.0 + pkg-conf: 2.1.0 + + skin-tone@2.0.0: + dependencies: + unicode-emoji-modifier-base: 1.0.0 + + slash@5.1.0: {} + + source-map@0.6.1: {} + + spawn-error-forwarder@1.0.0: {} + + spdx-correct@3.2.0: + dependencies: + spdx-expression-parse: 3.0.1 + spdx-license-ids: 3.0.22 + + spdx-exceptions@2.5.0: {} + + spdx-expression-parse@3.0.1: + dependencies: + spdx-exceptions: 2.5.0 + spdx-license-ids: 3.0.22 + + spdx-license-ids@3.0.22: {} + + split2@1.0.0: + dependencies: + through2: 2.0.5 + + split2@3.2.2: + dependencies: + readable-stream: 3.6.2 + + split2@4.2.0: {} + + split@1.0.1: + dependencies: + through: 2.3.8 + + stream-combiner2@1.1.1: + dependencies: + duplexer2: 0.1.4 + readable-stream: 2.3.8 + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-bom@3.0.0: {} + + strip-final-newline@2.0.0: {} + + strip-final-newline@3.0.0: {} + + strip-indent@3.0.0: + dependencies: + min-indent: 1.0.1 + + strip-json-comments@2.0.1: {} + + supports-color@5.5.0: + dependencies: + has-flag: 3.0.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-hyperlinks@3.2.0: + dependencies: + has-flag: 4.0.0 + supports-color: 7.2.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + temp-dir@3.0.0: {} + + tempy@3.1.0: + dependencies: + is-stream: 3.0.0 + temp-dir: 3.0.0 + type-fest: 2.19.0 + unique-string: 3.0.0 + + text-extensions@1.9.0: {} + + text-extensions@2.4.0: {} + + through2@2.0.5: + dependencies: + readable-stream: 2.3.8 + xtend: 4.0.2 + + through2@4.0.2: + dependencies: + readable-stream: 3.6.2 + + through@2.3.8: {} + + tinyexec@1.0.1: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + traverse@0.6.8: {} + + trim-newlines@3.0.1: {} + + type-fest@0.18.1: {} + + type-fest@0.6.0: {} + + type-fest@0.8.1: {} + + type-fest@1.4.0: {} + + type-fest@2.19.0: {} + + type-fest@4.41.0: {} + + typescript@5.9.3: {} + + uglify-js@3.19.3: + optional: true + + undici-types@7.14.0: {} + + unicode-emoji-modifier-base@1.0.0: {} + + unicorn-magic@0.1.0: {} + + unicorn-magic@0.3.0: {} + + unique-string@3.0.0: + dependencies: + crypto-random-string: 4.0.0 + + universal-user-agent@6.0.1: {} + + universalify@2.0.1: {} + + url-join@5.0.0: {} + + util-deprecate@1.0.2: {} + + validate-npm-package-license@3.0.4: + dependencies: + spdx-correct: 3.2.0 + spdx-expression-parse: 3.0.1 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + wordwrap@1.0.0: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrappy@1.0.2: {} + + xtend@4.0.2: {} + + y18n@5.0.8: {} + + yallist@4.0.0: {} + + yargs-parser@20.2.9: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yocto-queue@1.2.1: {} From 3d8899f146d9eaae563b4ee1f39644f0593d5400 Mon Sep 17 00:00:00 2001 From: Husamettin ARABACI Date: Mon, 20 Oct 2025 13:44:21 +0300 Subject: [PATCH 07/16] feat(freq): added basic freq handler (#27) Added simple at freq handler not check and control --- firmware/Makefile | 20 ++-- firmware/src/at/at_task.rs | 4 +- firmware/src/at/command.rs | 23 +--- firmware/src/at/dispatcher.rs | 5 + firmware/src/at/handler.rs | 7 ++ firmware/src/at/handlers/freq_handler.rs | 54 +++++++++ firmware/src/at/handlers/mod.rs | 2 + firmware/src/b64/mod.rs | 38 ------ firmware/src/channel/message_type.rs | 3 + firmware/src/dds/ad985x.rs | 142 +++++++++++++++++++++++ firmware/src/dds/dds_task.rs | 57 +++++++++ firmware/src/dds/mod.rs | 7 ++ firmware/src/error/mod.rs | 4 +- firmware/src/hexa_config/mod.rs | 10 ++ firmware/src/main.rs | 34 ++++-- 15 files changed, 332 insertions(+), 78 deletions(-) create mode 100644 firmware/src/at/handlers/freq_handler.rs delete mode 100644 firmware/src/b64/mod.rs create mode 100644 firmware/src/dds/ad985x.rs create mode 100644 firmware/src/dds/dds_task.rs create mode 100644 firmware/src/dds/mod.rs diff --git a/firmware/Makefile b/firmware/Makefile index 4f1d84c..4c04b01 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -49,17 +49,15 @@ clean: @echo "==> Cleaning $(CARGO_TARGET_DIR)" @$(CARGO) clean -monitor: - @PORT=$$(ls /dev/ttyACM* 2>/dev/null | head -n1); \ - if [ -z "$$PORT" ]; then echo "No /dev/ttyACM* found. Is the device in application mode?"; exit 1; fi; \ - echo "Opening $$PORT at 115200 baud..."; \ - picocom -b 115200 $$PORT +monitor: + probe-rs run --chip RP2040 target/thumbv6m-none-eabi/debug/hexagenmini help: @echo "Targets:" - @echo " make build - Build firmware ($(PROFILE) mode)" - @echo " make elf - Generate .elf file" - @echo " make load - Load firmware to device (requires BOOTSEL mode)" - @echo " make clippy - Run Clippy" - @echo " make clean - Clean build artifacts" - @echo " make monitor - Open serial monitor (115200 baud)" + @echo " make build - Build firmware ($(PROFILE) mode)" + @echo " make elf - Generate .elf file" + @echo " make check-device - Check for RP2040 in BOOTSEL mode" + @echo " make load - Load firmware to device (requires BOOTSEL mode)" + @echo " make clippy - Run Clippy" + @echo " make clean - Clean build artifacts" + @echo " make monitor - Program and monitor via probe-rs" diff --git a/firmware/src/at/at_task.rs b/firmware/src/at/at_task.rs index f560c63..57994cf 100644 --- a/firmware/src/at/at_task.rs +++ b/firmware/src/at/at_task.rs @@ -18,18 +18,20 @@ pub async fn at_task( at_tx: Sender<'static, Cs, Msg, CAP>, usb_tx: Sender<'static, Cs, Msg, CAP>, rgb_tx: Sender<'static, Cs, Msg, CAP>, + dds_tx: Sender<'static, Cs, Msg, CAP>, ) { info!("Starting AT task"); loop { match at_rx.receive().await { Msg::AtCmd(MsgDirection::Input, line) => { - if let Some(e) = dispatcher.dispatch(spawner, at_tx, rgb_tx, &line) { + if let Some(e) = dispatcher.dispatch(spawner, at_tx, rgb_tx, dds_tx, &line) { error!("Dispatch error: {:?}", &e.code()); let compiled = compile_at_error(e); usb_tx.send(Msg::UsbTxLine(compiled)).await; } } Msg::AtCmd(MsgDirection::Output, line) => { + info!("Sending output: {}", line.as_str()); usb_tx.send(Msg::UsbTxLine(line)).await; } Msg::Ok => { diff --git a/firmware/src/at/command.rs b/firmware/src/at/command.rs index b76c211..dd3cd52 100644 --- a/firmware/src/at/command.rs +++ b/firmware/src/at/command.rs @@ -4,10 +4,8 @@ use defmt::info; use heapless::{String, Vec}; -use crate::b64::{b64_decode, b64_encode}; use crate::error::Error; -/// Parsed AT command pub struct AtCommand { pub name: String<16>, pub params: Vec, 8>, @@ -15,7 +13,6 @@ pub struct AtCommand { } impl AtCommand { - /// Compile AT command back into string pub fn compile(&self) -> String<64> { let mut s = String::<64>::new(); s.push_str("AT+").unwrap(); @@ -28,16 +25,15 @@ impl AtCommand { if i > 0 { s.push('#').unwrap(); } - let encoded = b64_encode(p.as_str()); - s.push_str(&encoded).unwrap(); + s.push_str(p.as_str()).unwrap(); } } s } } -/// Parse AT command string into name, params, and query flag pub fn parse(input: &str) -> Result { + info!("Received input: {}", input); let input = input.trim(); if !input.starts_with("AT+") { return Err(Error::InvalidCommand); @@ -53,18 +49,9 @@ pub fn parse(input: &str) -> Result { .filter(|p| !p.is_empty()) { info!("Parsing param: {}", p); - match b64_decode(p) { - Ok(decoded) => { - info!("Decoded param: {}", decoded.as_str()); - let mut s_hl = String::<16>::new(); - s_hl.push_str(&decoded).map_err(|_| Error::InvalidUtf8)?; - params.push(s_hl).map_err(|_| Error::ParamCount)?; - } - Err(_) => { - info!("Base64 decode failed for param: {}", p); - return Err(Error::InvalidBase64); - } - } + let mut s_hl = String::<16>::new(); + s_hl.push_str(p).map_err(|_| Error::InvalidUtf8)?; + params.push(s_hl).map_err(|_| Error::ParamCount)?; } Ok(AtCommand { name: { diff --git a/firmware/src/at/dispatcher.rs b/firmware/src/at/dispatcher.rs index 46431db..a4b53ae 100644 --- a/firmware/src/at/dispatcher.rs +++ b/firmware/src/at/dispatcher.rs @@ -28,6 +28,7 @@ impl AtDispatcher { spawner: Spawner, at_tx: Sender<'static, Cs, Msg, CAP>, rgb_tx: Sender<'static, Cs, Msg, CAP>, + dds_tx: Sender<'static, Cs, Msg, CAP>, input: &str, ) -> Option { match parse(input) { @@ -49,6 +50,10 @@ impl AtDispatcher { info!("Dispatching FWUPDATE command"); h.handle(spawner, at_tx, &cmd.params, cmd.is_query) } + Handler::Freq(h) => { + info!("Dispatching FREQ command"); + h.handle(spawner, dds_tx, &cmd.params, cmd.is_query) + } }, None => Some(Error::UnknownCommand), }, diff --git a/firmware/src/at/handler.rs b/firmware/src/at/handler.rs index b77ce11..ac89aab 100644 --- a/firmware/src/at/handler.rs +++ b/firmware/src/at/handler.rs @@ -25,6 +25,7 @@ pub enum Handler { SetRgb(SetRgbHandler), Reset(ResetHandler), FwUpdate(FwUpdateHandler), + Freq(FreqHandler), } pub fn register_all() -> LinearMap, Handler, 8> { @@ -54,5 +55,11 @@ pub fn register_all() -> LinearMap, Handler, 8> { ) .ok(); + map.insert( + String::<16>::try_from("FREQ").unwrap(), + Handler::Freq(FreqHandler), + ) + .ok(); + map } diff --git a/firmware/src/at/handlers/freq_handler.rs b/firmware/src/at/handlers/freq_handler.rs new file mode 100644 index 0000000..4a5518b --- /dev/null +++ b/firmware/src/at/handlers/freq_handler.rs @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: 2025 hexaTune LLC +// SPDX-License-Identifier: MIT + +use defmt::{error, info}; +use embassy_executor::Spawner; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; +use embassy_sync::channel::Sender; +use heapless::String; + +use crate::at::AtHandler; +use crate::channel::{CAP, Msg}; +use crate::error::Error; +use crate::hexa_config::is_dds_available; + +pub struct FreqHandler; + +impl AtHandler for FreqHandler { + fn handle( + &self, + spawner: Spawner, + dds_tx: Sender<'static, Cs, Msg, CAP>, + params: &[String<16>], + _is_query: bool, + ) -> Option { + if params.len() != 3 { + error!("FREQ command requires 3 parameters, got {}", params.len()); + return Some(Error::ParamCount); + } + let id = match params[0].parse::>() { + Ok(v) => v, + Err(_) => return Some(Error::ParamValue), + }; + let freq = match params[1].parse::() { + Ok(v) => v, + Err(_) => return Some(Error::ParamValue), + }; + let time_ms = match params[2].parse::() { + Ok(v) => v, + Err(_) => return Some(Error::ParamValue), + }; + if is_dds_available() { + let _ = spawner.spawn(freq_task(dds_tx, id, freq, time_ms)); + None + } else { + info!("DDS busy, cannot set FREQ ({})", id.as_str()); + Some(Error::DdsBusy) + } + } +} + +#[embassy_executor::task] +async fn freq_task(dds_tx: Sender<'static, Cs, Msg, CAP>, id: String<16>, freq: u32, time_ms: u32) { + dds_tx.send(Msg::FreqWithValue(id, freq, time_ms)).await; +} diff --git a/firmware/src/at/handlers/mod.rs b/firmware/src/at/handlers/mod.rs index c03c46e..b3d56c3 100644 --- a/firmware/src/at/handlers/mod.rs +++ b/firmware/src/at/handlers/mod.rs @@ -9,3 +9,5 @@ mod fwupdate_handler; pub use fwupdate_handler::*; mod reset_handler; pub use reset_handler::*; +mod freq_handler; +pub use freq_handler::*; diff --git a/firmware/src/b64/mod.rs b/firmware/src/b64/mod.rs deleted file mode 100644 index 9cbe0a2..0000000 --- a/firmware/src/b64/mod.rs +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-FileCopyrightText: 2025 hexaTune LLC -// SPDX-License-Identifier: MIT - -//use base64ct::{Base64, Encoding}; -use heapless::String; - -use crate::error::Error; - -pub fn b64_decode(input: &str) -> Result, Error> { - // let mut buf = [0u8; 64]; - // match Base64::decode(input, &mut buf) { - // Ok(decoded) => match core::str::from_utf8(decoded) { - // Ok(s) => { - // let mut out = String::<64>::new(); - // out.push_str(s).map_err(|_| Error::InvalidUtf8)?; - // Ok(out) - // } - // Err(_) => Err(Error::InvalidUtf8), - // }, - // Err(_) => Err(Error::InvalidBase64), - // } - - let mut out = String::<64>::new(); - out.push_str(input).map_err(|_| Error::InvalidUtf8)?; - Ok(out) -} - -pub fn b64_encode(input: &str) -> String<64> { - // let mut buf = [0u8; 64]; - // let encoded = Base64::encode(input.as_bytes(), &mut buf).unwrap(); - // let mut out = String::<64>::new(); - // out.push_str(encoded).unwrap(); - // out - - let mut out = String::<64>::new(); - out.push_str(input).unwrap(); - out -} diff --git a/firmware/src/channel/message_type.rs b/firmware/src/channel/message_type.rs index 0d46b9e..feea7ff 100644 --- a/firmware/src/channel/message_type.rs +++ b/firmware/src/channel/message_type.rs @@ -15,5 +15,8 @@ pub enum Msg { Ok, Err(Error), UsbTxLine(String<64>), + ///RGB value message with R, G, B u8 values RgbWithValue(u8, u8, u8), + ///Frequency message with ID, frequency in Hz, and time in ms + FreqWithValue(String<16>, u32, u32), } diff --git a/firmware/src/dds/ad985x.rs b/firmware/src/dds/ad985x.rs new file mode 100644 index 0000000..18f0c2f --- /dev/null +++ b/firmware/src/dds/ad985x.rs @@ -0,0 +1,142 @@ +// SPDX-FileCopyrightText: 2025 hexaTune LLC +// SPDX-License-Identifier: MIT + +use embassy_rp::gpio::Output; +use embassy_time::{Duration, Timer}; + +use crate::error::Error; + +const CTRL_PWRDOWN: u8 = 1 << 7; +const CTRL_SLEEP: u8 = 1 << 6; +//const CTRL_X6: u8 = 1 << 5; // AD9851 x6 PLL +const CTRL_PHASE0: u8 = 0; +const PULSE_US: u64 = 1; + +pub struct Ad985x { + wclk: Output<'static>, + fq_ud: Output<'static>, + data: Output<'static>, + rst: Output<'static>, + ref_clk_hz: u32, + ctrl_base: u8, +} + +impl Ad985x { + pub fn new( + wclk: Output<'static>, + fq_ud: Output<'static>, + data: Output<'static>, + rst: Output<'static>, + ref_clk_hz: u32, + ctrl_base: u8, + ) -> Self { + Self { + wclk, + fq_ud, + data, + rst, + ref_clk_hz, + ctrl_base, + } + } + + #[inline(always)] + async fn pulse_high_low(pin: &mut Output<'static>) { + pin.set_high(); + Timer::after(Duration::from_micros(PULSE_US)).await; + pin.set_low(); + Timer::after(Duration::from_micros(PULSE_US)).await; + } + + async fn shift_lsb_first(&mut self, mut value: u64, bits: usize) { + for _ in 0..bits { + if (value & 1) != 0 { + self.data.set_high(); + } else { + self.data.set_low(); + } + self.wclk.set_high(); + Timer::after(Duration::from_micros(PULSE_US)).await; + self.wclk.set_low(); + Timer::after(Duration::from_micros(PULSE_US)).await; + value >>= 1; + } + } + + async fn write_ftw_ctrl(&mut self, ftw: u32, ctrl: u8) { + self.shift_lsb_first(ftw as u64, 32).await; + self.shift_lsb_first(ctrl as u64, 8).await; + Self::pulse_high_low(&mut self.fq_ud).await; + } + + fn hz_to_ftw(&self, freq_hz: u32) -> u32 { + let num = (freq_hz as u64) << 32; + let den = self.ref_clk_hz as u64; + ((num + den / 2) / den) as u32 + } + + pub async fn reset(&mut self) -> Option { + self.rst.set_high(); + Timer::after(Duration::from_micros(5)).await; + self.rst.set_low(); + Timer::after(Duration::from_micros(5)).await; + + for _ in 0..5 { + Self::pulse_high_low(&mut self.wclk).await; + } + Self::pulse_high_low(&mut self.fq_ud).await; + + self.write_ftw_ctrl(0, self.ctrl_base & !(CTRL_SLEEP | CTRL_PWRDOWN)) + .await; + + None + } + + /*pub async fn sleep(&mut self) -> Option { + self.write_ftw_ctrl(0, self.ctrl_base | CTRL_SLEEP).await; + None + }*/ + + pub async fn down(&mut self) -> Option { + self.write_ftw_ctrl(0, self.ctrl_base | CTRL_PWRDOWN).await; + None + } + + pub async fn up(&mut self) -> Option { + self.write_ftw_ctrl(0, self.ctrl_base & !(CTRL_SLEEP | CTRL_PWRDOWN)) + .await; + None + } + + pub async fn set_freq(&mut self, freq_hz: u32, dwell_ms: u32) -> Option { + if let Some(e) = self.down().await { + return Some(e); + } + + if let Some(e) = self.up().await { + return Some(e); + } + + if let Some(e) = self.reset().await { + return Some(e); + } + + if let Some(e) = self.set_freq_immediate(freq_hz).await { + return Some(e); + } + + Timer::after(Duration::from_millis(dwell_ms as u64)).await; + + if let Some(e) = self.down().await { + return Some(e); + } + + None + } + + async fn set_freq_immediate(&mut self, freq_hz: u32) -> Option { + let ftw = self.hz_to_ftw(freq_hz); + self.write_ftw_ctrl(ftw, self.ctrl_base | CTRL_PHASE0).await; + None + } +} diff --git a/firmware/src/dds/dds_task.rs b/firmware/src/dds/dds_task.rs new file mode 100644 index 0000000..0406ba0 --- /dev/null +++ b/firmware/src/dds/dds_task.rs @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: 2025 hexaTune LLC +// SPDX-License-Identifier: MIT + +use defmt::info; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; +use embassy_sync::channel::{Receiver, Sender}; +use heapless::{String, Vec}; +use {defmt_rtt as _, panic_probe as _}; + +use crate::at::AtCommand; +use crate::channel::MsgDirection; +use crate::channel::{CAP, Msg}; +use crate::dds::Ad985x; +use crate::error::Error; +use crate::hexa_config::*; + +#[embassy_executor::task] +pub async fn dds_task( + mut ad985x: Ad985x, + dds_rx: Receiver<'static, Cs, Msg, CAP>, + at_tx: Sender<'static, Cs, Msg, CAP>, +) { + info!("Starting DDS task"); + loop { + if let Msg::FreqWithValue(id, freq, time_ms) = dds_rx.receive().await { + if is_dds_available() { + set_dds_status(true); + info!("Setting FREQ to ({}) over {} ms", id.as_str(), time_ms); + if let Some(err) = ad985x.set_freq(freq, time_ms).await { + info!("Error setting FREQ: {:?}", err.code()); + at_tx.send(Msg::Err(err)).await; + } else { + let mut name = String::<16>::new(); + name.push_str("FREQ").unwrap(); + + let mut params = Vec::, 8>::new(); + let ver = String::<16>::try_from(id).unwrap(); + params.push(ver).ok(); + let done = String::<16>::try_from("DONE").unwrap(); + params.push(done).ok(); + + let compiled = AtCommand { + name, + params, + is_query: false, + } + .compile(); + at_tx.send(Msg::AtCmd(MsgDirection::Output, compiled)).await; + } + set_dds_status(false); + } else { + info!("DDS busy, cannot set FREQ ({})", id.as_str()); + at_tx.send(Msg::Err(Error::DdsBusy)).await; + } + } + } +} diff --git a/firmware/src/dds/mod.rs b/firmware/src/dds/mod.rs new file mode 100644 index 0000000..f751686 --- /dev/null +++ b/firmware/src/dds/mod.rs @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2025 hexaTune LLC +// SPDX-License-Identifier: MIT + +mod dds_task; +pub use dds_task::*; +mod ad985x; +pub use ad985x::*; diff --git a/firmware/src/error/mod.rs b/firmware/src/error/mod.rs index a4a3239..0d8f4f0 100644 --- a/firmware/src/error/mod.rs +++ b/firmware/src/error/mod.rs @@ -4,7 +4,7 @@ #[derive(Debug, Clone)] pub enum Error { InvalidCommand, // "Not an AT command" - InvalidBase64, // "Invalid base64 param" + DdsBusy, // "DDS is busy" InvalidUtf8, // "Invalid UTF-8 in param" InvalidSysEx, // "Invalid SysEx" InvalidDataLenght, // "Invalid data length" @@ -19,7 +19,7 @@ impl Error { match self { // Command errors Error::InvalidCommand => "E001001", - Error::InvalidBase64 => "E001002", + Error::DdsBusy => "E001002", Error::InvalidUtf8 => "E001003", Error::InvalidSysEx => "E001004", Error::InvalidDataLenght => "E001005", diff --git a/firmware/src/hexa_config/mod.rs b/firmware/src/hexa_config/mod.rs index b5677df..e46284c 100644 --- a/firmware/src/hexa_config/mod.rs +++ b/firmware/src/hexa_config/mod.rs @@ -1,4 +1,14 @@ // SPDX-FileCopyrightText: 2025 hexaTune LLC // SPDX-License-Identifier: MIT +use core::sync::atomic::{AtomicBool, Ordering}; + pub const CONF_VERSION: &str = "v1.0.0"; + +pub static DDS_GENERATING: AtomicBool = AtomicBool::new(false); +pub fn set_dds_status(status: bool) { + DDS_GENERATING.store(status, Ordering::SeqCst); +} +pub fn is_dds_available() -> bool { + !DDS_GENERATING.load(Ordering::SeqCst) +} diff --git a/firmware/src/main.rs b/firmware/src/main.rs index 827c5d4..ce3bdde 100644 --- a/firmware/src/main.rs +++ b/firmware/src/main.rs @@ -10,8 +10,8 @@ use embassy_sync::mutex::Mutex as AsyncMutex; use {defmt_rtt as _, panic_probe as _}; mod at; -mod b64; mod channel; +mod dds; mod error; mod hexa_config; mod rgb; @@ -40,12 +40,12 @@ async fn main(spawner: embassy_executor::Spawner) { let usb_rx = mgr.register(channel::ModuleId::Usb, &channel::USB_CH); let at_rx = mgr.register(channel::ModuleId::At, &channel::AT_CH); let rgb_rx = mgr.register(channel::ModuleId::Rgb, &channel::RGB_CH); - let _dds_rx = mgr.register(channel::ModuleId::Dds, &channel::DDS_CH); + let dds_rx = mgr.register(channel::ModuleId::Dds, &channel::DDS_CH); let mgr_ref: &'static channel::ChannelManager<{ channel::CAP }, 8> = CHANNEL_MANAGER.init(mgr); let at_tx = mgr_ref.tx(channel::ModuleId::At).unwrap(); let usb_tx = mgr_ref.tx(channel::ModuleId::Usb).unwrap(); let rgb_tx = mgr_ref.tx(channel::ModuleId::Rgb).unwrap(); - //let dds_tx = mgr_ref.tx(channel::ModuleId::Dds).unwrap(); + let dds_tx = mgr_ref.tx(channel::ModuleId::Dds).unwrap(); //USB module info!("Initializing USB"); @@ -77,11 +77,21 @@ async fn main(spawner: embassy_executor::Spawner) { ); let rgb_led = rgb::RgbLed::new(ws2812); + //DDS module + let ad9850 = dds::Ad985x::new( + embassy_rp::gpio::Output::new(p.PIN_2, embassy_rp::gpio::Level::Low), + embassy_rp::gpio::Output::new(p.PIN_3, embassy_rp::gpio::Level::Low), + embassy_rp::gpio::Output::new(p.PIN_4, embassy_rp::gpio::Level::Low), + embassy_rp::gpio::Output::new(p.PIN_5, embassy_rp::gpio::Level::Low), + 125_000_000, + 0, + ); + //Spawn tasks info!("Spawning tasks"); spawner .spawn(at::at_task( - dispatcher, spawner, at_rx, at_tx, usb_tx, rgb_tx, + dispatcher, spawner, at_rx, at_tx, usb_tx, rgb_tx, dds_tx, )) .unwrap(); spawner.spawn(usb::dev_task(device)).unwrap(); @@ -92,14 +102,22 @@ async fn main(spawner: embassy_executor::Spawner) { spawner .spawn(rgb::rgb_task(rgb_led, rgb_rx, at_tx)) .unwrap(); + spawner.spawn(dds::dds_task(ad9850, dds_rx, at_tx)).unwrap(); //Dummy task to blink the LED let mut led = embassy_rp::gpio::Output::new(p.PIN_25, embassy_rp::gpio::Level::Low); loop { info!("Blink!"); - led.set_high(); - embassy_time::Timer::after_secs(5).await; - led.set_low(); - embassy_time::Timer::after_secs(5).await; + if hexa_config::is_dds_available() { + led.set_high(); + embassy_time::Timer::after_secs(5).await; + led.set_low(); + embassy_time::Timer::after_secs(5).await; + } else { + led.set_high(); + embassy_time::Timer::after_secs(1).await; + led.set_low(); + embassy_time::Timer::after_secs(1).await; + } } } From e023c785d9592aee6e93debbf1637106eddaac9f Mon Sep 17 00:00:00 2001 From: Husamettin ARABACI Date: Tue, 21 Oct 2025 19:33:09 +0300 Subject: [PATCH 08/16] feat(core): added multi core (#28) System is configured for working multi-core. DDS task is working on the Core-1 and others on the Core-0 --- firmware/Cargo.toml | 1 - firmware/src/at/handlers/version_handler.rs | 4 +- firmware/src/dds/dds_task.rs | 8 +- firmware/src/error/mod.rs | 4 +- firmware/src/main.rs | 91 ++++++++++++++++----- firmware/src/usb/usb_task.rs | 9 +- 6 files changed, 83 insertions(+), 34 deletions(-) diff --git a/firmware/Cargo.toml b/firmware/Cargo.toml index b934231..c192c6f 100644 --- a/firmware/Cargo.toml +++ b/firmware/Cargo.toml @@ -8,7 +8,6 @@ version = "0.1.0" authors = ["Husamettin ARABACI "] edition = "2024" license = "MIT" -license-file = "LICENSE" repository = "https://github.com/hTuneSys/hexaGenMini" homepage = "https://hexatune.com" keywords = ["rust", "firmware", "event-driven", "embedded", "hexaGenMini"] diff --git a/firmware/src/at/handlers/version_handler.rs b/firmware/src/at/handlers/version_handler.rs index a9a7ac6..f64def1 100644 --- a/firmware/src/at/handlers/version_handler.rs +++ b/firmware/src/at/handlers/version_handler.rs @@ -38,8 +38,8 @@ async fn version_task(at_tx: Sender<'static, Cs, Msg, CAP>) { name.push_str("VERSION").unwrap(); let mut params = Vec::, 8>::new(); - let ver = String::<16>::try_from(CONF_VERSION).unwrap(); - params.push(ver).ok(); + let version_param = String::<16>::try_from(CONF_VERSION).unwrap(); + params.push(version_param).ok(); let compiled = AtCommand { name, diff --git a/firmware/src/dds/dds_task.rs b/firmware/src/dds/dds_task.rs index 0406ba0..861c5ac 100644 --- a/firmware/src/dds/dds_task.rs +++ b/firmware/src/dds/dds_task.rs @@ -34,10 +34,10 @@ pub async fn dds_task( name.push_str("FREQ").unwrap(); let mut params = Vec::, 8>::new(); - let ver = String::<16>::try_from(id).unwrap(); - params.push(ver).ok(); - let done = String::<16>::try_from("DONE").unwrap(); - params.push(done).ok(); + let id_param = String::<16>::try_from(id).unwrap(); + params.push(id_param).ok(); + let done_param = String::<16>::try_from("DONE").unwrap(); + params.push(done_param).ok(); let compiled = AtCommand { name, diff --git a/firmware/src/error/mod.rs b/firmware/src/error/mod.rs index 0d8f4f0..ee59309 100644 --- a/firmware/src/error/mod.rs +++ b/firmware/src/error/mod.rs @@ -7,7 +7,7 @@ pub enum Error { DdsBusy, // "DDS is busy" InvalidUtf8, // "Invalid UTF-8 in param" InvalidSysEx, // "Invalid SysEx" - InvalidDataLenght, // "Invalid data length" + InvalidDataLength, // "Invalid data length" ParamCount, // "Param count" ParamValue, // "Param value" NotAQuery, // "Not a query" @@ -22,7 +22,7 @@ impl Error { Error::DdsBusy => "E001002", Error::InvalidUtf8 => "E001003", Error::InvalidSysEx => "E001004", - Error::InvalidDataLenght => "E001005", + Error::InvalidDataLength => "E001005", Error::ParamCount => "E001006", Error::ParamValue => "E001007", Error::NotAQuery => "E001008", diff --git a/firmware/src/main.rs b/firmware/src/main.rs index ce3bdde..8f1b355 100644 --- a/firmware/src/main.rs +++ b/firmware/src/main.rs @@ -4,9 +4,10 @@ #![no_std] #![no_main] -use defmt::info; +use defmt::*; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; use embassy_sync::mutex::Mutex as AsyncMutex; +use heapless::{String, Vec}; use {defmt_rtt as _, panic_probe as _}; mod at; @@ -18,6 +19,13 @@ mod rgb; mod sysex; mod usb; +use crate::channel::CAP; + +static mut CORE1_STACK: embassy_rp::multicore::Stack<4096> = embassy_rp::multicore::Stack::new(); +static EXECUTOR0: static_cell::StaticCell = + static_cell::StaticCell::new(); +static EXECUTOR1: static_cell::StaticCell = + static_cell::StaticCell::new(); static CHANNEL_MANAGER: static_cell::StaticCell> = static_cell::StaticCell::new(); @@ -29,7 +37,7 @@ embassy_rp::bind_interrupts!(struct IrqPio { }); #[embassy_executor::main] -async fn main(spawner: embassy_executor::Spawner) { +async fn main(_spawner: embassy_executor::Spawner) { //Initialize the RP2040 let p = embassy_rp::init(Default::default()); info!("Starting hexaGenMini firmware"); @@ -87,37 +95,76 @@ async fn main(spawner: embassy_executor::Spawner) { 0, ); - //Spawn tasks - info!("Spawning tasks"); - spawner - .spawn(at::at_task( + //Dummy Led + let led = embassy_rp::gpio::Output::new(p.PIN_25, embassy_rp::gpio::Level::Low); + + embassy_rp::multicore::spawn_core1( + p.CORE1, + unsafe { &mut *core::ptr::addr_of_mut!(CORE1_STACK) }, + move || { + let executor1 = EXECUTOR1.init(embassy_executor::Executor::new()); + executor1.run(|spawner| { + unwrap!(spawner.spawn(dds::dds_task(ad9850, dds_rx, at_tx))); + }); + }, + ); + + //Core0 Task Spawner + let executor0 = EXECUTOR0.init(embassy_executor::Executor::new()); + executor0.run(|spawner| { + //AT tasks + unwrap!(spawner.spawn(at::at_task( dispatcher, spawner, at_rx, at_tx, usb_tx, rgb_tx, dds_tx, - )) - .unwrap(); - spawner.spawn(usb::dev_task(device)).unwrap(); - // midi_mutex now matches the expected type for usb_io_task (AsyncMutex) - spawner - .spawn(usb::usb_io_task(midi_mutex, usb_rx, at_tx)) - .unwrap(); - spawner - .spawn(rgb::rgb_task(rgb_led, rgb_rx, at_tx)) - .unwrap(); - spawner.spawn(dds::dds_task(ad9850, dds_rx, at_tx)).unwrap(); + ))); + //USB Device task + unwrap!(spawner.spawn(usb::dev_task(device))); + //USB IO task + unwrap!(spawner.spawn(usb::usb_io_task(midi_mutex, usb_rx, at_tx))); + //RGB task + unwrap!(spawner.spawn(rgb::rgb_task(rgb_led, rgb_rx, at_tx))); + //Main loop task + unwrap!(spawner.spawn(main_loop_task(at_tx, led))); + }); +} +#[embassy_executor::task] +pub async fn main_loop_task( + at_tx: embassy_sync::channel::Sender<'static, Cs, channel::Msg, CAP>, + mut led: embassy_rp::gpio::Output<'static>, +) { //Dummy task to blink the LED - let mut led = embassy_rp::gpio::Output::new(p.PIN_25, embassy_rp::gpio::Level::Low); + loop { info!("Blink!"); + let mut name = String::<16>::new(); + name.push_str("STATUS").unwrap(); + + let mut params = Vec::, 8>::new(); + let mut status_param = String::<16>::try_from("AVAILABLE").unwrap(); + if hexa_config::is_dds_available() { led.set_high(); embassy_time::Timer::after_secs(5).await; led.set_low(); embassy_time::Timer::after_secs(5).await; } else { - led.set_high(); - embassy_time::Timer::after_secs(1).await; - led.set_low(); - embassy_time::Timer::after_secs(1).await; + status_param = String::<16>::try_from("GENERATING").unwrap(); + for _ in 0..5 { + led.set_high(); + embassy_time::Timer::after_secs(1).await; + led.set_low(); + embassy_time::Timer::after_secs(1).await; + } + } + params.push(status_param).ok(); + let compiled = at::AtCommand { + name, + params, + is_query: false, } + .compile(); + at_tx + .send(channel::Msg::AtCmd(channel::MsgDirection::Output, compiled)) + .await; } } diff --git a/firmware/src/usb/usb_task.rs b/firmware/src/usb/usb_task.rs index 0222ed5..0375321 100644 --- a/firmware/src/usb/usb_task.rs +++ b/firmware/src/usb/usb_task.rs @@ -40,6 +40,9 @@ pub async fn usb_io_task( match select(read_fut, tx_fut).await { Either::First((n, buf)) => { + if n == 0 { + continue; + } let data = &buf[..n]; info!("Received MIDI packet: {:?}", data); @@ -50,8 +53,8 @@ pub async fn usb_io_task( at_tx.send(Msg::AtCmd(MsgDirection::Input, line)).await; } Err(_) => { - error!("Error Code: {}", Error::InvalidDataLenght.code()); - at_tx.send(Msg::Err(Error::InvalidDataLenght)).await; + error!("Error Code: {}", Error::InvalidDataLength.code()); + at_tx.send(Msg::Err(Error::InvalidDataLength)).await; } } } else { @@ -70,9 +73,9 @@ pub async fn usb_io_task( let packets = sysex_to_usb_midi_packets::<64>(&sysex); info!("Sending {} MIDI packets", packets.len()); + let mut m = midi.lock().await; for pkt in packets.iter() { info!("Sending MIDI packet: {:?}", pkt); - let mut m = midi.lock().await; if let Err(e) = m.write_packet(pkt).await { error!("USB write error: {:?}", e); } From 4128984ed11ea8cd6f393653d6de39185c511af9 Mon Sep 17 00:00:00 2001 From: Husamettin ARABACI Date: Tue, 21 Oct 2025 23:10:52 +0300 Subject: [PATCH 09/16] feat(core): at command is made by id based (#29) Changed all message types to at command base and id forced for all at command --- firmware/src/at/at_task.rs | 38 +++++++---- firmware/src/at/command.rs | 49 +++++++++++--- firmware/src/at/dispatcher.rs | 10 +-- firmware/src/at/handler.rs | 3 +- firmware/src/at/handlers/freq_handler.rs | 33 +++------ firmware/src/at/handlers/fwupdate_handler.rs | 6 +- firmware/src/at/handlers/reset_handler.rs | 6 +- firmware/src/at/handlers/setrgb_handler.rs | 30 ++------- firmware/src/at/handlers/version_handler.rs | 17 +++-- firmware/src/channel/message_type.rs | 25 +++---- firmware/src/dds/dds_task.rs | 70 +++++++++++--------- firmware/src/hexa_config/mod.rs | 10 +-- firmware/src/main.rs | 15 ++--- firmware/src/rgb/rgb_task.rs | 43 ++++++++++-- firmware/src/usb/usb_task.rs | 13 ++-- 15 files changed, 204 insertions(+), 164 deletions(-) diff --git a/firmware/src/at/at_task.rs b/firmware/src/at/at_task.rs index 57994cf..d39eacc 100644 --- a/firmware/src/at/at_task.rs +++ b/firmware/src/at/at_task.rs @@ -7,8 +7,9 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; use embassy_sync::channel::{Receiver, Sender}; use {defmt_rtt as _, panic_probe as _}; -use crate::at::{AtDispatcher, compile_at_error, compile_at_ok}; -use crate::channel::{CAP, Msg, MsgDirection}; +use crate::at::*; +use crate::channel::{CAP, Msg}; +use crate::hexa_config::*; #[embassy_executor::task] pub async fn at_task( @@ -23,27 +24,36 @@ pub async fn at_task( info!("Starting AT task"); loop { match at_rx.receive().await { - Msg::AtCmd(MsgDirection::Input, line) => { + Msg::AtRxLine(line) => { if let Some(e) = dispatcher.dispatch(spawner, at_tx, rgb_tx, dds_tx, &line) { - error!("Dispatch error: {:?}", &e.code()); - let compiled = compile_at_error(e); + let compiled = compile_at_error(get_empty_id(), e); + error!("Dispatch error: {:?}", compiled.as_str()); usb_tx.send(Msg::UsbTxLine(compiled)).await; } } - Msg::AtCmd(MsgDirection::Output, line) => { - info!("Sending output: {}", line.as_str()); - usb_tx.send(Msg::UsbTxLine(line)).await; + Msg::AtCmdOutput(at_command) => { + let compiled = at_command.compile(); + info!("Sending output: {}", compiled.as_str()); + usb_tx.send(Msg::UsbTxLine(compiled)).await; + } + Msg::Done(msg_id) => { + let compiled = compile_at_done(msg_id); + info!("Sending done: {}", compiled.as_str()); + usb_tx.send(Msg::UsbTxLine(compiled)).await; } - Msg::Ok => { - let compiled = compile_at_ok(); - info!("Sending OK from AT task"); + Msg::Err(msg_id, e) => { + let compiled = compile_at_error(msg_id, e); + error!("Sending error: {}", compiled.as_str()); usb_tx.send(Msg::UsbTxLine(compiled)).await; } - Msg::Err(e) => { - error!("Error Code: {}", &e.code()); - let compiled = compile_at_error(e); + Msg::ErrWOCommand(e) => { + let compiled = compile_at_error(get_empty_id(), e); + error!("Sending error: {}", compiled.as_str()); usb_tx.send(Msg::UsbTxLine(compiled)).await; } + Msg::SetDdsAvailable(status) => { + set_dds_available(status); + } _ => {} } } diff --git a/firmware/src/at/command.rs b/firmware/src/at/command.rs index dd3cd52..f8b9475 100644 --- a/firmware/src/at/command.rs +++ b/firmware/src/at/command.rs @@ -7,6 +7,7 @@ use heapless::{String, Vec}; use crate::error::Error; pub struct AtCommand { + pub id: String<16>, pub name: String<16>, pub params: Vec, 8>, pub is_query: bool, @@ -19,13 +20,14 @@ impl AtCommand { s.push_str(self.name.as_str()).unwrap(); if self.is_query { s.push('?').unwrap(); - } else if !self.params.is_empty() { + } else { s.push('=').unwrap(); - for (i, p) in self.params.iter().enumerate() { - if i > 0 { + s.push_str(self.id.as_str()).unwrap(); + if !self.params.is_empty() { + for p in self.params.iter() { s.push('#').unwrap(); + s.push_str(p.as_str()).unwrap(); } - s.push_str(p.as_str()).unwrap(); } } s @@ -41,19 +43,30 @@ pub fn parse(input: &str) -> Result { let cmd = &input[3..]; if let Some(eq_pos) = cmd.find('=') { let (name, param_str) = cmd.split_at(eq_pos); + let mut id: String<16> = { + let mut id = String::<16>::new(); + id.push_str("0").map_err(|_| Error::InvalidUtf8)?; + id + }; let param_str = ¶m_str[1..]; let mut params = Vec::, 8>::new(); - for p in param_str + for (i, p) in param_str .split('#') .map(|p| p.trim()) .filter(|p| !p.is_empty()) + .enumerate() { info!("Parsing param: {}", p); let mut s_hl = String::<16>::new(); s_hl.push_str(p).map_err(|_| Error::InvalidUtf8)?; - params.push(s_hl).map_err(|_| Error::ParamCount)?; + if i == 0 { + id = s_hl; + } else { + params.push(s_hl).map_err(|_| Error::ParamCount)?; + } } Ok(AtCommand { + id, name: { let mut n = String::<16>::new(); n.push_str(name).map_err(|_| Error::InvalidUtf8)?; @@ -64,6 +77,11 @@ pub fn parse(input: &str) -> Result { }) } else if let Some(name) = cmd.strip_suffix('?') { Ok(AtCommand { + id: { + let mut id = String::<16>::new(); + id.push_str("0").map_err(|_| Error::InvalidUtf8)?; + id + }, name: { let mut n = String::<16>::new(); n.push_str(name).map_err(|_| Error::InvalidUtf8)?; @@ -74,6 +92,11 @@ pub fn parse(input: &str) -> Result { }) } else { Ok(AtCommand { + id: { + let mut id = String::<16>::new(); + id.push_str("0").map_err(|_| Error::InvalidUtf8)?; + id + }, name: { let mut n = String::<16>::new(); n.push_str(cmd).map_err(|_| Error::InvalidUtf8)?; @@ -85,11 +108,18 @@ pub fn parse(input: &str) -> Result { } } -pub fn compile_at_ok() -> String<64> { +pub fn get_empty_id() -> String<16> { + let mut id = String::<16>::new(); + id.push_str("0").unwrap(); + id +} + +pub fn compile_at_done(id: String<16>) -> String<64> { let at_cmd = AtCommand { + id, name: { let mut n = String::<16>::new(); - n.push_str("OK").unwrap(); + n.push_str("DONE").unwrap(); n }, params: Vec::, 8>::new(), @@ -98,8 +128,9 @@ pub fn compile_at_ok() -> String<64> { at_cmd.compile() } -pub fn compile_at_error(e: Error) -> String<64> { +pub fn compile_at_error(id: String<16>, e: Error) -> String<64> { let at_cmd = AtCommand { + id, name: { let mut n = String::<16>::new(); n.push_str("ERROR").unwrap(); diff --git a/firmware/src/at/dispatcher.rs b/firmware/src/at/dispatcher.rs index a4b53ae..dc6d9fb 100644 --- a/firmware/src/at/dispatcher.rs +++ b/firmware/src/at/dispatcher.rs @@ -36,23 +36,23 @@ impl AtDispatcher { Some(handler) => match handler { Handler::Version(h) => { info!("Dispatching VERSION command"); - h.handle(spawner, at_tx, &cmd.params, cmd.is_query) + h.handle(spawner, at_tx, cmd) } Handler::SetRgb(h) => { info!("Dispatching SETRGB command"); - h.handle(spawner, rgb_tx, &cmd.params, cmd.is_query) + h.handle(spawner, rgb_tx, cmd) } Handler::Reset(h) => { info!("Dispatching RESET command"); - h.handle(spawner, at_tx, &cmd.params, cmd.is_query) + h.handle(spawner, at_tx, cmd) } Handler::FwUpdate(h) => { info!("Dispatching FWUPDATE command"); - h.handle(spawner, at_tx, &cmd.params, cmd.is_query) + h.handle(spawner, at_tx, cmd) } Handler::Freq(h) => { info!("Dispatching FREQ command"); - h.handle(spawner, dds_tx, &cmd.params, cmd.is_query) + h.handle(spawner, dds_tx, cmd) } }, None => Some(Error::UnknownCommand), diff --git a/firmware/src/at/handler.rs b/firmware/src/at/handler.rs index ac89aab..175d101 100644 --- a/firmware/src/at/handler.rs +++ b/firmware/src/at/handler.rs @@ -15,8 +15,7 @@ pub trait AtHandler { &self, spawner: Spawner, tx: Sender<'static, Cs, Msg, CAP>, - params: &[String<16>], - is_query: bool, + at_command: AtCommand, ) -> Option; } diff --git a/firmware/src/at/handlers/freq_handler.rs b/firmware/src/at/handlers/freq_handler.rs index 4a5518b..ee7e4fb 100644 --- a/firmware/src/at/handlers/freq_handler.rs +++ b/firmware/src/at/handlers/freq_handler.rs @@ -5,12 +5,11 @@ use defmt::{error, info}; use embassy_executor::Spawner; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; use embassy_sync::channel::Sender; -use heapless::String; -use crate::at::AtHandler; +use crate::at::{AtCommand, AtHandler}; use crate::channel::{CAP, Msg}; use crate::error::Error; -use crate::hexa_config::is_dds_available; +use crate::hexa_config::*; pub struct FreqHandler; @@ -19,36 +18,20 @@ impl AtHandler for FreqHandler { &self, spawner: Spawner, dds_tx: Sender<'static, Cs, Msg, CAP>, - params: &[String<16>], - _is_query: bool, + at_command: AtCommand, ) -> Option { - if params.len() != 3 { - error!("FREQ command requires 3 parameters, got {}", params.len()); - return Some(Error::ParamCount); - } - let id = match params[0].parse::>() { - Ok(v) => v, - Err(_) => return Some(Error::ParamValue), - }; - let freq = match params[1].parse::() { - Ok(v) => v, - Err(_) => return Some(Error::ParamValue), - }; - let time_ms = match params[2].parse::() { - Ok(v) => v, - Err(_) => return Some(Error::ParamValue), - }; if is_dds_available() { - let _ = spawner.spawn(freq_task(dds_tx, id, freq, time_ms)); + info!("Setting FREQ: {:?}", at_command.id.as_str()); + let _ = spawner.spawn(freq_task(dds_tx, at_command)); None } else { - info!("DDS busy, cannot set FREQ ({})", id.as_str()); + error!("DDS busy, cannot set FREQ ({})", at_command.id.as_str()); Some(Error::DdsBusy) } } } #[embassy_executor::task] -async fn freq_task(dds_tx: Sender<'static, Cs, Msg, CAP>, id: String<16>, freq: u32, time_ms: u32) { - dds_tx.send(Msg::FreqWithValue(id, freq, time_ms)).await; +async fn freq_task(dds_tx: Sender<'static, Cs, Msg, CAP>, at_command: AtCommand) { + dds_tx.send(Msg::FreqWithValue(at_command)).await; } diff --git a/firmware/src/at/handlers/fwupdate_handler.rs b/firmware/src/at/handlers/fwupdate_handler.rs index aaf4b40..b0cb160 100644 --- a/firmware/src/at/handlers/fwupdate_handler.rs +++ b/firmware/src/at/handlers/fwupdate_handler.rs @@ -5,9 +5,8 @@ use defmt::info; use embassy_executor::Spawner; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; use embassy_sync::channel::Sender; -use heapless::String; -use crate::at::AtHandler; +use crate::at::*; use crate::channel::{CAP, Msg}; use crate::error::Error; @@ -18,8 +17,7 @@ impl AtHandler for FwUpdateHandler { &self, spawner: Spawner, _at_tx: Sender<'static, Cs, Msg, CAP>, - _params: &[String<16>], - _is_query: bool, + _at_command: AtCommand, ) -> Option { info!("Firmware update requested - spawning BOOTSEL task"); let _ = spawner.spawn(fwupdate_task()); diff --git a/firmware/src/at/handlers/reset_handler.rs b/firmware/src/at/handlers/reset_handler.rs index 3b17833..6802db1 100644 --- a/firmware/src/at/handlers/reset_handler.rs +++ b/firmware/src/at/handlers/reset_handler.rs @@ -5,9 +5,8 @@ use cortex_m::peripheral::SCB; use embassy_executor::Spawner; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; use embassy_sync::channel::Sender; -use heapless::String; -use crate::at::AtHandler; +use crate::at::*; use crate::channel::{CAP, Msg}; use crate::error::Error; @@ -18,8 +17,7 @@ impl AtHandler for ResetHandler { &self, _spawner: Spawner, _at_tx: Sender<'static, Cs, Msg, CAP>, - _params: &[String<16>], - _is_query: bool, + _at_command: AtCommand, ) -> Option { SCB::sys_reset(); } diff --git a/firmware/src/at/handlers/setrgb_handler.rs b/firmware/src/at/handlers/setrgb_handler.rs index c9cfd62..ed3c8a4 100644 --- a/firmware/src/at/handlers/setrgb_handler.rs +++ b/firmware/src/at/handlers/setrgb_handler.rs @@ -1,13 +1,11 @@ // SPDX-FileCopyrightText: 2025 hexaTune LLC // SPDX-License-Identifier: MIT -use defmt::error; use embassy_executor::Spawner; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; use embassy_sync::channel::Sender; -use heapless::String; -use crate::at::AtHandler; +use crate::at::{AtCommand, AtHandler}; use crate::channel::{CAP, Msg}; use crate::error::Error; @@ -18,32 +16,14 @@ impl AtHandler for SetRgbHandler { &self, spawner: Spawner, rgb_tx: Sender<'static, Cs, Msg, CAP>, - params: &[String<16>], - _is_query: bool, + at_command: AtCommand, ) -> Option { - if params.len() != 3 { - error!("SETRGB command requires 3 parameters, got {}", params.len()); - return Some(Error::ParamCount); - } - // check params are u8 - let r = match params[0].parse::() { - Ok(v) => v, - Err(_) => return Some(Error::ParamValue), - }; - let g = match params[1].parse::() { - Ok(v) => v, - Err(_) => return Some(Error::ParamValue), - }; - let b = match params[2].parse::() { - Ok(v) => v, - Err(_) => return Some(Error::ParamValue), - }; - let _ = spawner.spawn(setrgb_task(rgb_tx, r, g, b)); + let _ = spawner.spawn(setrgb_task(rgb_tx, at_command)); None } } #[embassy_executor::task] -async fn setrgb_task(rgb_tx: Sender<'static, Cs, Msg, CAP>, r: u8, g: u8, b: u8) { - rgb_tx.send(Msg::RgbWithValue(r, g, b)).await; +async fn setrgb_task(rgb_tx: Sender<'static, Cs, Msg, CAP>, at_command: AtCommand) { + rgb_tx.send(Msg::RgbWithValue(at_command)).await; } diff --git a/firmware/src/at/handlers/version_handler.rs b/firmware/src/at/handlers/version_handler.rs index f64def1..08c4b66 100644 --- a/firmware/src/at/handlers/version_handler.rs +++ b/firmware/src/at/handlers/version_handler.rs @@ -7,8 +7,8 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; use embassy_sync::channel::Sender; use heapless::{String, Vec}; -use crate::at::{AtCommand, AtHandler}; -use crate::channel::{CAP, Msg, MsgDirection}; +use crate::at::*; +use crate::channel::{CAP, Msg}; use crate::error::Error; use crate::hexa_config::CONF_VERSION; @@ -19,10 +19,9 @@ impl AtHandler for VersionHandler { &self, spawner: Spawner, at_tx: Sender<'static, Cs, Msg, CAP>, - _params: &[String<16>], - is_query: bool, + at_command: AtCommand, ) -> Option { - if is_query { + if at_command.is_query { let _ = spawner.spawn(version_task(at_tx)); None } else { @@ -41,11 +40,11 @@ async fn version_task(at_tx: Sender<'static, Cs, Msg, CAP>) { let version_param = String::<16>::try_from(CONF_VERSION).unwrap(); params.push(version_param).ok(); - let compiled = AtCommand { + let at_command = AtCommand { + id: get_empty_id(), name, params, is_query: false, - } - .compile(); - at_tx.send(Msg::AtCmd(MsgDirection::Output, compiled)).await; + }; + at_tx.send(Msg::AtCmdOutput(at_command)).await; } diff --git a/firmware/src/channel/message_type.rs b/firmware/src/channel/message_type.rs index feea7ff..dea2cdb 100644 --- a/firmware/src/channel/message_type.rs +++ b/firmware/src/channel/message_type.rs @@ -3,20 +3,21 @@ use heapless::String; +use crate::at::AtCommand; use crate::error::Error; -pub enum MsgDirection { - Input, - Output, -} +pub type MsgId = String<16>; +pub type MsgString = String<64>; +pub type IsDdsAvailable = bool; pub enum Msg { - AtCmd(MsgDirection, String<64>), - Ok, - Err(Error), - UsbTxLine(String<64>), - ///RGB value message with R, G, B u8 values - RgbWithValue(u8, u8, u8), - ///Frequency message with ID, frequency in Hz, and time in ms - FreqWithValue(String<16>, u32, u32), + AtRxLine(MsgString), + AtCmdOutput(AtCommand), + Done(MsgId), + Err(MsgId, Error), + ErrWOCommand(Error), + UsbTxLine(MsgString), + RgbWithValue(AtCommand), + FreqWithValue(AtCommand), + SetDdsAvailable(IsDdsAvailable), } diff --git a/firmware/src/dds/dds_task.rs b/firmware/src/dds/dds_task.rs index 861c5ac..c8baffa 100644 --- a/firmware/src/dds/dds_task.rs +++ b/firmware/src/dds/dds_task.rs @@ -1,18 +1,14 @@ // SPDX-FileCopyrightText: 2025 hexaTune LLC // SPDX-License-Identifier: MIT -use defmt::info; +use defmt::*; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; use embassy_sync::channel::{Receiver, Sender}; -use heapless::{String, Vec}; use {defmt_rtt as _, panic_probe as _}; -use crate::at::AtCommand; -use crate::channel::MsgDirection; use crate::channel::{CAP, Msg}; use crate::dds::Ad985x; use crate::error::Error; -use crate::hexa_config::*; #[embassy_executor::task] pub async fn dds_task( @@ -22,35 +18,45 @@ pub async fn dds_task( ) { info!("Starting DDS task"); loop { - if let Msg::FreqWithValue(id, freq, time_ms) = dds_rx.receive().await { - if is_dds_available() { - set_dds_status(true); - info!("Setting FREQ to ({}) over {} ms", id.as_str(), time_ms); - if let Some(err) = ad985x.set_freq(freq, time_ms).await { - info!("Error setting FREQ: {:?}", err.code()); - at_tx.send(Msg::Err(err)).await; - } else { - let mut name = String::<16>::new(); - name.push_str("FREQ").unwrap(); - - let mut params = Vec::, 8>::new(); - let id_param = String::<16>::try_from(id).unwrap(); - params.push(id_param).ok(); - let done_param = String::<16>::try_from("DONE").unwrap(); - params.push(done_param).ok(); - - let compiled = AtCommand { - name, - params, - is_query: false, - } - .compile(); - at_tx.send(Msg::AtCmd(MsgDirection::Output, compiled)).await; + if let Msg::FreqWithValue(at_command) = dds_rx.receive().await { + if at_command.params.len() != 2 { + error!( + "FREQ command requires 3 parameters, got {}", + at_command.params.len() + ); + at_tx.send(Msg::Err(at_command.id, Error::DdsBusy)).await; + return; + } + let freq = match at_command.params[0].parse::() { + Ok(v) => v, + Err(_) => { + error!("Invalid frequency value: {}", at_command.id.as_str()); + at_tx.send(Msg::Err(at_command.id, Error::ParamValue)).await; + return; } - set_dds_status(false); + }; + let time_ms = match at_command.params[1].parse::() { + Ok(v) => v, + Err(_) => { + error!("Invalid time_ms value: {}", at_command.id.as_str()); + at_tx.send(Msg::Err(at_command.id, Error::ParamValue)).await; + return; + } + }; + + at_tx.send(Msg::SetDdsAvailable(false)).await; + info!( + "Setting FREQ to ({}) over {} ms", + at_command.id.as_str(), + time_ms + ); + let err = ad985x.set_freq(freq, time_ms).await; + at_tx.send(Msg::SetDdsAvailable(true)).await; + if let Some(err) = err { + error!("Error setting FREQ: {:?}", err.code()); + at_tx.send(Msg::Err(at_command.id, err)).await; } else { - info!("DDS busy, cannot set FREQ ({})", id.as_str()); - at_tx.send(Msg::Err(Error::DdsBusy)).await; + at_tx.send(Msg::Done(at_command.id)).await; } } } diff --git a/firmware/src/hexa_config/mod.rs b/firmware/src/hexa_config/mod.rs index e46284c..d4cdeff 100644 --- a/firmware/src/hexa_config/mod.rs +++ b/firmware/src/hexa_config/mod.rs @@ -3,12 +3,14 @@ use core::sync::atomic::{AtomicBool, Ordering}; +//General configuration constants pub const CONF_VERSION: &str = "v1.0.0"; -pub static DDS_GENERATING: AtomicBool = AtomicBool::new(false); -pub fn set_dds_status(status: bool) { - DDS_GENERATING.store(status, Ordering::SeqCst); +//DDS status tracking +pub static DDS_AVAILABLE: AtomicBool = AtomicBool::new(true); +pub fn set_dds_available(status: bool) { + DDS_AVAILABLE.store(status, Ordering::SeqCst); } pub fn is_dds_available() -> bool { - !DDS_GENERATING.load(Ordering::SeqCst) + DDS_AVAILABLE.load(Ordering::SeqCst) } diff --git a/firmware/src/main.rs b/firmware/src/main.rs index 8f1b355..1bfb09e 100644 --- a/firmware/src/main.rs +++ b/firmware/src/main.rs @@ -112,6 +112,8 @@ async fn main(_spawner: embassy_executor::Spawner) { //Core0 Task Spawner let executor0 = EXECUTOR0.init(embassy_executor::Executor::new()); executor0.run(|spawner| { + //RGB task + unwrap!(spawner.spawn(rgb::rgb_task(rgb_led, rgb_rx, at_tx))); //AT tasks unwrap!(spawner.spawn(at::at_task( dispatcher, spawner, at_rx, at_tx, usb_tx, rgb_tx, dds_tx, @@ -120,8 +122,6 @@ async fn main(_spawner: embassy_executor::Spawner) { unwrap!(spawner.spawn(usb::dev_task(device))); //USB IO task unwrap!(spawner.spawn(usb::usb_io_task(midi_mutex, usb_rx, at_tx))); - //RGB task - unwrap!(spawner.spawn(rgb::rgb_task(rgb_led, rgb_rx, at_tx))); //Main loop task unwrap!(spawner.spawn(main_loop_task(at_tx, led))); }); @@ -133,7 +133,6 @@ pub async fn main_loop_task( mut led: embassy_rp::gpio::Output<'static>, ) { //Dummy task to blink the LED - loop { info!("Blink!"); let mut name = String::<16>::new(); @@ -157,14 +156,12 @@ pub async fn main_loop_task( } } params.push(status_param).ok(); - let compiled = at::AtCommand { + let at_command = at::AtCommand { + id: at::get_empty_id(), name, params, is_query: false, - } - .compile(); - at_tx - .send(channel::Msg::AtCmd(channel::MsgDirection::Output, compiled)) - .await; + }; + at_tx.send(channel::Msg::AtCmdOutput(at_command)).await; } } diff --git a/firmware/src/rgb/rgb_task.rs b/firmware/src/rgb/rgb_task.rs index 5a70624..fb73804 100644 --- a/firmware/src/rgb/rgb_task.rs +++ b/firmware/src/rgb/rgb_task.rs @@ -1,12 +1,13 @@ // SPDX-FileCopyrightText: 2025 hexaTune LLC // SPDX-License-Identifier: MIT -use defmt::info; +use defmt::*; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; use embassy_sync::channel::{Receiver, Sender}; use {defmt_rtt as _, panic_probe as _}; use crate::channel::{CAP, Msg}; +use crate::error::Error; use crate::rgb::RgbLed; #[embassy_executor::task] @@ -17,12 +18,46 @@ pub async fn rgb_task( ) { info!("Starting RGB task"); loop { - if let Msg::RgbWithValue(r, g, b) = rgb_rx.receive().await { + if let Msg::RgbWithValue(at_command) = rgb_rx.receive().await { + if at_command.params.len() != 3 { + error!( + "SETRGB command {} requires 3 parameters, got {}", + at_command.id.as_str(), + at_command.params.len() + ); + at_tx.send(Msg::Err(at_command.id, Error::ParamCount)).await; + return; + } + // check params are u8 + let r = match at_command.params[0].parse::() { + Ok(v) => v, + Err(_) => { + error!("Invalid R value: {}", at_command.id.as_str()); + at_tx.send(Msg::Err(at_command.id, Error::ParamValue)).await; + return; + } + }; + let g = match at_command.params[1].parse::() { + Ok(v) => v, + Err(_) => { + error!("Invalid G value: {}", at_command.id.as_str()); + at_tx.send(Msg::Err(at_command.id, Error::ParamValue)).await; + return; + } + }; + let b = match at_command.params[2].parse::() { + Ok(v) => v, + Err(_) => { + error!("Invalid B value: {}", at_command.id.as_str()); + at_tx.send(Msg::Err(at_command.id, Error::ParamValue)).await; + return; + } + }; info!("Setting RGB to ({}, {}, {})", r, g, b); rgb_led.set_rgb(r, g, b).await; info!("RGB set"); - info!("Sending OK from RGB task"); - at_tx.send(Msg::Ok).await; + info!("Sending DONE from RGB task"); + at_tx.send(Msg::Done(at_command.id)).await; } } } diff --git a/firmware/src/usb/usb_task.rs b/firmware/src/usb/usb_task.rs index 0375321..b955611 100644 --- a/firmware/src/usb/usb_task.rs +++ b/firmware/src/usb/usb_task.rs @@ -7,7 +7,7 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; use embassy_sync::channel::{Receiver, Sender}; use embassy_sync::mutex::Mutex; -use crate::channel::{CAP, Msg, MsgDirection}; +use crate::channel::{CAP, Msg}; use crate::error::Error; use crate::sysex::{build_sysex, extract_sysex_payload, sysex_to_usb_midi_packets}; use crate::usb::{MyMidiClass, MyUsbDevice}; @@ -25,7 +25,6 @@ pub async fn usb_io_task( at_tx: Sender<'static, Cs, Msg, CAP>, ) { info!("Starting unified USB IO task"); - loop { let read_fut = async { let mut buf = [0u8; 64]; @@ -50,20 +49,22 @@ pub async fn usb_io_task( if let Ok(input) = core::str::from_utf8(&payload) { match heapless::String::<64>::try_from(input) { Ok(line) => { - at_tx.send(Msg::AtCmd(MsgDirection::Input, line)).await; + at_tx.send(Msg::AtRxLine(line)).await; } Err(_) => { error!("Error Code: {}", Error::InvalidDataLength.code()); - at_tx.send(Msg::Err(Error::InvalidDataLength)).await; + at_tx + .send(Msg::ErrWOCommand(Error::InvalidDataLength)) + .await; } } } else { error!("Error Code: {}", Error::InvalidUtf8.code()); - at_tx.send(Msg::Err(Error::InvalidUtf8)).await; + at_tx.send(Msg::ErrWOCommand(Error::InvalidUtf8)).await; } } else { error!("Error Code: {}", Error::InvalidSysEx.code()); - at_tx.send(Msg::Err(Error::InvalidSysEx)).await; + at_tx.send(Msg::ErrWOCommand(Error::InvalidSysEx)).await; } } From 15c515e8458f3b84065186b39c23839b60b8f0f4 Mon Sep 17 00:00:00 2001 From: Husamettin ARABACI Date: Wed, 22 Oct 2025 18:43:04 +0300 Subject: [PATCH 10/16] docs(basic): added basic docs Added simple documantation for version v1.0.0 (#30) --- README.md | 190 +++++++++++++++++++++++++++++++++++++- docs/ARCHITECTURE.md | 127 +++++++++++++++++++++++++ docs/GETTING_STARTED.md | 108 +++++++++++++++++++++- docs/PROJECT_STRUCTURE.md | 83 +++++++++++++++++ 4 files changed, 505 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cca41ed..720e715 100644 --- a/README.md +++ b/README.md @@ -3,5 +3,193 @@ SPDX-FileCopyrightText: 2025 hexaTune LLC SPDX-License-Identifier: MIT --> -# hexaGenMini +# 🎛️ hexaGenMini + +[![GitHub](https://img.shields.io/badge/GitHub-hTuneSys/hexagenmini-blue)](https://github.com/hTuneSys/hexaGenMini) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +[![Rust](https://img.shields.io/badge/Rust-1.86.0-orange)](https://www.rust-lang.org/) +[![Embassy](https://img.shields.io/badge/Embassy-0.9.0-red)](https://embassy.dev/) + +**hexaGenMini** is a compact, USB-powered signal generator designed for music synthesis and electronic experimentation. Built on the Raspberry Pi Pico (RP2040), it features Direct Digital Synthesis (DDS) for precise frequency generation, RGB status indication, and USB MIDI control via AT commands. + +Developed by **hexaTune LLC** and the **hexaTeam**, led by **Husamettin ARABACI**. + +## ✨ Features + +- **Precise Frequency Generation**: AD985x DDS chip supporting frequencies up to 125MHz +- **USB MIDI Control**: Send AT commands via MIDI SysEx for remote control +- **RGB Status LED**: Visual feedback for device state and DDS availability +- **Firmware Updates**: Built-in BOOTSEL mode for easy firmware flashing +- **Cross-Platform**: Works with any USB MIDI-compatible host +- **Mobile App**: Control via dedicated iOS/Android app ([hexaGenApp](https://github.com/hTuneSys/hexaGenApp)) +- **Open Source**: Fully open hardware and software under MIT license + +## 📱 Mobile App + +Control your hexaGenMini wirelessly with our companion mobile app: + +- **iOS & Android Support** +- **Real-time Frequency Control** +- **Preset Management** +- **Visual Waveform Display** + +[![Get it on Google Play](https://img.shields.io/badge/Get%20it%20on-Google%20Play-green)](https://play.google.com/store/apps/developer?id=hexaTune+LLC) +[![Download on the App Store](https://img.shields.io/badge/Download%20on-the%20App%20Store-black)](https://apps.apple.com/us/developer/hexatune-llc/id1234567890) + +**Source Code**: [hTuneSys/hexaGenApp](https://github.com/hTuneSys/hexaGenApp) + +## 🚀 Quick Start + +1. **Connect** your hexaGenMini via USB +2. **Flash Firmware**: + ```bash + cd firmware + cargo run + ``` +3. **Send AT Commands** via MIDI SysEx: + ``` + AT+FREQ=1#1000000#1000 # Set 1MHz for 1 second + AT+SETRGB=2#255#0#128 # Set LED to purple + ``` + +## 📦 Installation + +### Prerequisites + +- **Rust** (1.86.0+): [Install rustup](https://rustup.rs/) +- **picotool**: For flashing RP2040 +- **Node.js & pnpm**: For development tools +- **KiCad** (optional): For hardware modifications +- **FreeCAD** (optional): For enclosure modifications + +### Setup + +```bash +# Clone repository +git clone https://github.com/hTuneSys/hexaGenMini.git +cd hexaGenMini + +# Install development dependencies +pnpm install +pnpm prepare # Setup husky pre-commit hooks + +# Install Rust targets +rustup target add thumbv6m-none-eabi +``` + +### Flashing Firmware + +```bash +cd firmware +cargo run +``` + +Put your device in BOOTSEL mode (hold BOOTSEL while plugging in) when prompted. + +## 🎯 Usage + +### AT Command Protocol + +hexaGenMini uses AT commands sent via MIDI SysEx messages: + +#### Supported Commands + +- `AT+VERSION?` - Get firmware version +- `AT+SETRGB=###` - Set RGB LED color +- `AT+FREQ=##` - Generate frequency with dwell time +- `AT+RESET=` - System reset +- `AT+FWUPDATE=` - Enter firmware update mode + +#### Example Usage + +```bash +# Query version +AT+VERSION? + +# Set frequency to 440Hz for 5 seconds +AT+FREQ=1#440#5000 + +# Set LED to red +AT+SETRGB=2#255#0#0 +``` + +### Hardware Connections + +- **USB**: Power and MIDI communication +- **SMA Output**: DDS signal output +- **RGB LED**: Status indication +- **BOOTSEL**: Firmware update mode + +## 🏗️ Architecture + +The firmware is built with Rust and Embassy, running concurrent async tasks: + +- **USB Task**: MIDI communication handling +- **AT Dispatcher**: Command parsing and routing +- **DDS Task**: Frequency generation control +- **RGB Task**: LED management +- **Main Loop**: Status monitoring + +For detailed architecture information, see [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md). + +## 📁 Project Structure + +``` +hexaGenMini/ +├── firmware/ # Rust firmware (Embassy framework) +├── hardware/ # KiCad PCB designs +├── mechanic/ # FreeCAD enclosure designs +├── docs/ # Documentation +├── .github/ # CI/CD workflows +└── README.md +``` + +## 🤝 Contributing + +We welcome contributions! Please see our [Contributing Guide](docs/CONTRIBUTING.md) for details. + +### Development Workflow + +1. Fork the repository +2. Create a feature branch from `develop` +3. Make your changes +4. Submit a pull request +5. Follow conventional commit format + +### Areas for Contribution + +- Firmware enhancements +- Hardware improvements +- Documentation +- Mobile app features +- Testing and CI/CD + +## 📄 Documentation + +- [Getting Started](docs/GETTING_STARTED.md) - Setup guide +- [Architecture](docs/ARCHITECTURE.md) - System design +- [Project Structure](docs/PROJECT_STRUCTURE.md) - Repository overview +- [Contributing](docs/CONTRIBUTING.md) - How to contribute +- [FAQ](docs/FAQ.md) - Common questions + +## 📜 License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +## 📞 Contact + +- **Website**: [hexatune.com](https://hexatune.com) +- **Email**: [info@hexatune.com](mailto:info@hexatune.com) +- **GitHub**: [hTuneSys](https://github.com/hTuneSys) +- **Issues**: [GitHub Issues](https://github.com/hTuneSys/hexaGenMini/issues) + +## 🙏 Acknowledgments + +Built with ❤️ by the hexaTeam at hexaTune LLC. + +Special thanks to the Rust embedded community and Embassy framework developers. + +--- + +**hexaGenMini** - Precision meets portability in signal generation. diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 1c09c3c..50fea9c 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -14,3 +14,130 @@ Contact the team at **[info@hexatune.com](mailto:info@hexatune.com)** or open an --- Built by [hexaTune LLC](https://hexatune.com) · GitHub: [hTuneSys/hexaGenMini](https://github.com/hTuneSys/hexaGenMini) · License: [MIT](https://opensource.org/license/mit/) + +--- + +## Overview + +hexaGenMini is a compact signal generator device based on the Raspberry Pi Pico (RP2040) microcontroller. It features a Direct Digital Synthesis (DDS) module for generating precise waveforms, an RGB LED for status indication, and USB MIDI communication for control. The device is designed for integration into music synthesis setups, providing frequency generation capabilities via AT command protocol over MIDI SysEx messages. + +## System Architecture + +The firmware is written in Rust using the Embassy framework for embedded async programming. It consists of several modules running as concurrent tasks on the RP2040's dual cores: + +- **USB Module**: Handles USB MIDI communication +- **AT Command Module**: Parses and dispatches AT commands +- **DDS Module**: Controls the AD985x DDS chip for frequency generation +- **RGB Module**: Manages the WS2812 RGB LED +- **Channel Manager**: Facilitates inter-task communication via async channels + +### Core 0 Tasks +- USB Device Task +- USB IO Task +- AT Task +- RGB Task +- Main Loop Task + +### Core 1 Tasks +- DDS Task + +## System Flow + +1. **Initialization**: The main function initializes peripherals, sets up channels, and spawns tasks on both cores. + +2. **USB Communication**: USB IO task listens for incoming MIDI packets containing SysEx messages. + +3. **AT Command Parsing**: Received SysEx payloads are parsed into AT commands. + +4. **Command Dispatch**: The AT dispatcher routes commands to appropriate handlers based on the command name. + +5. **Handler Execution**: Handlers perform actions like setting frequency, changing LED color, or triggering firmware updates. + +6. **Response Generation**: Results are compiled back into AT response format and sent via USB MIDI. + +## AT Command Structure + +AT commands follow a specific format for communication with the device: + +### Command Format +``` +AT+=###... +``` + +### Query Format +``` +AT+? +``` + +### Response Format +``` +AT+=###... +``` + +### Supported Commands + +#### VERSION +- **Query**: `AT+VERSION?` +- **Response**: `AT+VERSION=0#v1.0.0` +- **Description**: Returns the firmware version + +#### SETRGB +- **Command**: `AT+SETRGB=###` +- **Response**: `AT+DONE=` +- **Description**: Sets the RGB LED color (R, G, B values 0-255) +- **Example**: `AT+SETRGB=123#255#0#128` + +#### RESET +- **Command**: `AT+RESET=` +- **Description**: Performs a system reset +- **Note**: No response as device resets + +#### FWUPDATE +- **Command**: `AT+FWUPDATE=` +- **Description**: Enters BOOTSEL mode for firmware update +- **Note**: No response as device enters bootloader + +#### FREQ +- **Command**: `AT+FREQ=##` +- **Response**: `AT+DONE=` or `AT+ERROR=#` +- **Description**: Sets DDS frequency with dwell time +- **Parameters**: + - FREQUENCY: Frequency in Hz (u32) + - TIME_MS: Dwell time in milliseconds (u32) +- **Example**: `AT+FREQ=456#1000000#5000` + +### Error Codes +- E001001: Invalid command +- E001002: DDS busy +- E001003: Invalid UTF-8 +- E001004: Invalid SysEx +- E001005: Invalid data length +- E001006: Parameter count error +- E001007: Parameter value error +- E001008: Not a query +- E001009: Unknown command + +## Communication Protocol + +Commands are sent as MIDI SysEx messages over USB MIDI: + +- **SysEx Start**: 0xF0 +- **Payload**: UTF-8 encoded AT command string +- **SysEx End**: 0xF7 + +The USB MIDI implementation uses standard MIDI packet formats for SysEx transmission. + +## Hardware Interfaces + +- **USB**: Full-speed USB 2.0 for MIDI communication +- **DDS**: AD985x controlled via GPIO bit-banging +- **RGB LED**: WS2812 controlled via PIO +- **Status LED**: Onboard LED for system status + +## Task Communication + +Inter-task communication uses Embassy async channels with a capacity of 16 messages per channel. The ChannelManager provides typed access to senders and receivers for each module. + +## Configuration + +System configuration is managed through constants in `hexa_config` module, including version information and DDS availability status. diff --git a/docs/GETTING_STARTED.md b/docs/GETTING_STARTED.md index 0f08f40..6aaa309 100644 --- a/docs/GETTING_STARTED.md +++ b/docs/GETTING_STARTED.md @@ -3,9 +3,113 @@ SPDX-FileCopyrightText: 2025 hexaTune LLC SPDX-License-Identifier: MIT --> -# GETTING_STARTED.md +# 🚀 Getting Started with hexaGenMini -Welcome to the hexaGenMini project! This guide will help you set up your environment, build the project, and understand the basics of how to start using or contributing to hexaGenMini. +Welcome to the hexaGenMini project! This guide will help you set up your development environment, build the project components, and get started with contributing or using hexaGenMini. + +## Prerequisites + +Before you begin, ensure you have the following installed: + +- **Git**: For version control +- **Node.js** and **pnpm**: For managing development tools +- **Rust**: For firmware development (install via rustup) +- **KiCad**: For PCB design (optional, for hardware development) +- **FreeCAD**: For mechanical design (optional, for enclosure design) +- **picotool**: For flashing firmware to Raspberry Pi Pico + +## Project Setup + +1. **Clone the repository**: + ```bash + git clone https://github.com/hTuneSys/hexaGenMini.git + cd hexaGenMini + ``` + +2. **Install dependencies**: + ```bash + pnpm install + ``` + +3. **Set up pre-commit hooks**: + ```bash + pnpm prepare + ``` + +## Development Areas + +### Firmware Development + +The firmware is written in Rust for the RP2040 microcontroller. + +1. **Navigate to firmware directory**: + ```bash + cd firmware + ``` + +2. **Install Rust targets** (if not already installed): + ```bash + rustup target add thumbv6m-none-eabi + ``` + +3. **Connect your hexaGenMini device** via USB and put it in BOOTSEL mode (hold BOOTSEL button while plugging in). + +4. **Flash and test the firmware**: + ```bash + cargo run + ``` + + This will build the firmware and flash it to the connected device. + +5. **Monitor output** (optional): + Use a tool like `minicom` or the embedded debugger to view serial output. + +### Hardware Development + +PCB designs are created with KiCad. + +1. **Open KiCad** and load the project: + - Navigate to `hardware/hexaGenMini-v1/` + - Open `hexaGenMini-v1.kicad_pro` + +2. **View schematics**: + - Open `hexaGenMini-v1.kicad_sch` + +3. **View PCB layout**: + - Open `hexaGenMini-v1.kicad_pcb` + +4. **Generate fabrication files**: + - Use KiCad's Plot and Drill tools to export Gerber files + - Fabrication outputs are pre-generated in `fabrication_output/` + +### Mechanical Development + +Enclosure designs are created with FreeCAD. + +1. **Open FreeCAD** and load the design files: + - `mechanic/Bottom.FCStd` - Bottom case + - `mechanic/PcbBoard.FCStd` - PCB mounting + +2. **Export for manufacturing**: + - Use FreeCAD's export tools for STL (3D printing) or DXF (laser cutting) + +## Testing Your Setup + +1. **Firmware**: After flashing, the device should blink its status LED and be detectable as a USB MIDI device. + +2. **AT Commands**: Use a MIDI tool to send SysEx messages with AT commands (see [ARCHITECTURE](ARCHITECTURE.md) for details). + +3. **Hardware**: Verify PCB connections and component placement. + +## Documentation + +For more detailed information, explore the documentation files: + +- [ARCHITECTURE](ARCHITECTURE.md) - System design and AT command protocol +- [DEVELOPMENT_GUIDE](DEVELOPMENT_GUIDE.md) - Detailed development setup +- [PROJECT_STRUCTURE](PROJECT_STRUCTURE.md) - Repository organization +- [CONTRIBUTING](CONTRIBUTING.md) - How to contribute +- [FAQ](FAQ.md) - Common questions --- diff --git a/docs/PROJECT_STRUCTURE.md b/docs/PROJECT_STRUCTURE.md index 2819b83..c5a763d 100644 --- a/docs/PROJECT_STRUCTURE.md +++ b/docs/PROJECT_STRUCTURE.md @@ -4,3 +4,86 @@ SPDX-License-Identifier: MIT --> # 📁 Project Structure: `hexaGenMini` + +This document outlines the organization of the hexaGenMini project repository. + +## Root Directory + +- `.github/`: GitHub workflows, issue templates, and configuration +- `.husky/`: Git hooks for commit linting +- `docs/`: Documentation files +- `firmware/`: Embedded firmware source code (Rust) +- `hardware/`: PCB design files (KiCad) +- `mechanic/`: Mechanical design files (FreeCAD) +- `LICENSE`: MIT license +- `README.md`: Project overview +- `package.json` & `pnpm-lock.yaml`: Node.js dependencies for tooling + +## Firmware Directory (`firmware/`) + +The firmware is written in Rust using the Embassy framework for the RP2040 microcontroller. + +- `src/`: Source code + - `main.rs`: Application entry point and task initialization + - `at/`: AT command parsing and handling + - `channel/`: Inter-task communication channels + - `dds/`: Direct Digital Synthesis (AD985x) control + - `error/`: Error definitions + - `hexa_config/`: Configuration constants + - `rgb/`: RGB LED control + - `sysex/`: MIDI SysEx message handling + - `usb/`: USB MIDI communication +- `build.rs`: Build script for memory layout +- `Cargo.toml`: Rust dependencies and build configuration +- `memory.x`: Linker memory layout + +## Hardware Directory (`hardware/`) + +PCB designs created with KiCad. + +- `hexaGenMini-v1/`: Main PCB design + - `fabrication_output/`: Manufacturing files + - `bom/`: Bill of Materials + - `gerber/`: Gerber files for PCB fabrication + - `pnp/`: Pick and place files + - `*.kicad_*`: KiCad project files + - `PCB_Library.pretty/`: Custom KiCad footprints + - `SCH_Library.kicad_sym`: Custom KiCad symbols + +## Mechanic Directory (`mechanic/`) + +Mechanical designs for the product enclosure, created with FreeCAD. + +- `Bottom.FCStd`: Bottom case design +- `PcbBoard.FCStd`: PCB mounting design +- `PcbBoard-Body.dxf`: 2D export for manufacturing + +## Documentation (`docs/`) + +- `ARCHITECTURE.md`: System architecture and AT command protocol +- `BRANCH_STRATEGY.md`: Git branching guidelines +- `BRANDING.md`: Branding guidelines +- `COMMIT_STRATEGY.md`: Commit message conventions +- `COMMUNITY.md`: Community guidelines +- `CONFIGURATION.md`: Configuration instructions +- `CONTACT.md`: Contact information +- `CONTRIBUTING.md`: Contribution guidelines +- `DEVELOPMENT_GUIDE.md`: Development setup +- `FAQ.md`: Frequently asked questions +- `GETTING_STARTED.md`: Quick start guide +- `LABELLING_STRATEGY.md`: Issue labeling +- `PR_STRATEGY.md`: Pull request guidelines +- `PROJECT_BOARD.md`: Project management +- `PROJECT_STRUCTURE.md`: This file +- `SECURITY.md`: Security policy +- `STYLE_GUIDE.md`: Code style guidelines +- `SUMMARY.md`: Project summary +- `SUPPORT.md`: Support information + +## Build and Development + +- Firmware: Built with Cargo (Rust toolchain) +- Hardware: Fabricated using standard PCB processes +- Mechanics: Manufactured using 3D printing or CNC + +For detailed setup instructions, see `docs/DEVELOPMENT_GUIDE.md`. From e34eddab8e0cb99966eb607e5b72e36b155d70de Mon Sep 17 00:00:00 2001 From: Husamettin ARABACI Date: Sun, 26 Oct 2025 15:42:22 +0300 Subject: [PATCH 11/16] feat(multicore): fixed multicore Fixed channel for multicore tasks --- firmware/src/at/at_task.rs | 33 ++-- firmware/src/at/dispatcher.rs | 22 +-- firmware/src/at/handler.rs | 10 +- firmware/src/at/handlers/freq_handler.rs | 23 ++- firmware/src/at/handlers/fwupdate_handler.rs | 10 +- firmware/src/at/handlers/reset_handler.rs | 10 +- firmware/src/at/handlers/setrgb_handler.rs | 18 +-- firmware/src/at/handlers/version_handler.rs | 18 +-- firmware/src/channel/channel_manager.rs | 36 ----- firmware/src/channel/channel_type.rs | 14 -- firmware/src/channel/message_type.rs | 1 - firmware/src/channel/mod.rs | 6 - firmware/src/channel/module_type.rs | 10 -- firmware/src/dds/ad985x.rs | 5 +- firmware/src/dds/dds_task.rs | 46 +++--- firmware/src/main.rs | 88 +++-------- firmware/src/main_multicore.rs | 153 +++++++++++++++++++ firmware/src/rgb/rgb_task.rs | 24 ++- firmware/src/usb/usb_task.rs | 30 ++-- 19 files changed, 277 insertions(+), 280 deletions(-) delete mode 100644 firmware/src/channel/channel_manager.rs delete mode 100644 firmware/src/channel/channel_type.rs delete mode 100644 firmware/src/channel/module_type.rs create mode 100644 firmware/src/main_multicore.rs diff --git a/firmware/src/at/at_task.rs b/firmware/src/at/at_task.rs index d39eacc..ee3cada 100644 --- a/firmware/src/at/at_task.rs +++ b/firmware/src/at/at_task.rs @@ -3,53 +3,40 @@ use defmt::{error, info}; use embassy_executor::Spawner; -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; -use embassy_sync::channel::{Receiver, Sender}; use {defmt_rtt as _, panic_probe as _}; +use crate::AT_CH; +use crate::USB_CH; use crate::at::*; -use crate::channel::{CAP, Msg}; +use crate::channel::*; use crate::hexa_config::*; #[embassy_executor::task] -pub async fn at_task( - dispatcher: &'static AtDispatcher, - spawner: Spawner, - at_rx: Receiver<'static, Cs, Msg, CAP>, - at_tx: Sender<'static, Cs, Msg, CAP>, - usb_tx: Sender<'static, Cs, Msg, CAP>, - rgb_tx: Sender<'static, Cs, Msg, CAP>, - dds_tx: Sender<'static, Cs, Msg, CAP>, -) { +pub async fn at_task(dispatcher: &'static AtDispatcher, spawner: Spawner) { info!("Starting AT task"); loop { - match at_rx.receive().await { + match AT_CH.receive().await { Msg::AtRxLine(line) => { - if let Some(e) = dispatcher.dispatch(spawner, at_tx, rgb_tx, dds_tx, &line) { + if let Some(e) = dispatcher.dispatch(spawner, &line) { let compiled = compile_at_error(get_empty_id(), e); error!("Dispatch error: {:?}", compiled.as_str()); - usb_tx.send(Msg::UsbTxLine(compiled)).await; + USB_CH.send(Msg::UsbTxLine(compiled)).await; } } Msg::AtCmdOutput(at_command) => { let compiled = at_command.compile(); info!("Sending output: {}", compiled.as_str()); - usb_tx.send(Msg::UsbTxLine(compiled)).await; + USB_CH.send(Msg::UsbTxLine(compiled)).await; } Msg::Done(msg_id) => { let compiled = compile_at_done(msg_id); info!("Sending done: {}", compiled.as_str()); - usb_tx.send(Msg::UsbTxLine(compiled)).await; + USB_CH.send(Msg::UsbTxLine(compiled)).await; } Msg::Err(msg_id, e) => { let compiled = compile_at_error(msg_id, e); error!("Sending error: {}", compiled.as_str()); - usb_tx.send(Msg::UsbTxLine(compiled)).await; - } - Msg::ErrWOCommand(e) => { - let compiled = compile_at_error(get_empty_id(), e); - error!("Sending error: {}", compiled.as_str()); - usb_tx.send(Msg::UsbTxLine(compiled)).await; + USB_CH.send(Msg::UsbTxLine(compiled)).await; } Msg::SetDdsAvailable(status) => { set_dds_available(status); diff --git a/firmware/src/at/dispatcher.rs b/firmware/src/at/dispatcher.rs index dc6d9fb..6c7066e 100644 --- a/firmware/src/at/dispatcher.rs +++ b/firmware/src/at/dispatcher.rs @@ -3,13 +3,10 @@ use defmt::{error, info}; use embassy_executor::Spawner; -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; -use embassy_sync::channel::Sender; use heapless::{LinearMap, String}; use crate::at::handler::{AtHandler, Handler, register_all}; use crate::at::parse; -use crate::channel::{CAP, Msg}; use crate::error::Error; pub struct AtDispatcher { @@ -23,36 +20,29 @@ impl AtDispatcher { } } - pub fn dispatch( - &self, - spawner: Spawner, - at_tx: Sender<'static, Cs, Msg, CAP>, - rgb_tx: Sender<'static, Cs, Msg, CAP>, - dds_tx: Sender<'static, Cs, Msg, CAP>, - input: &str, - ) -> Option { + pub fn dispatch(&self, spawner: Spawner, input: &str) -> Option { match parse(input) { Ok(cmd) => match self.handlers.get(&cmd.name) { Some(handler) => match handler { Handler::Version(h) => { info!("Dispatching VERSION command"); - h.handle(spawner, at_tx, cmd) + h.handle(spawner, cmd) } Handler::SetRgb(h) => { info!("Dispatching SETRGB command"); - h.handle(spawner, rgb_tx, cmd) + h.handle(spawner, cmd) } Handler::Reset(h) => { info!("Dispatching RESET command"); - h.handle(spawner, at_tx, cmd) + h.handle(spawner, cmd) } Handler::FwUpdate(h) => { info!("Dispatching FWUPDATE command"); - h.handle(spawner, at_tx, cmd) + h.handle(spawner, cmd) } Handler::Freq(h) => { info!("Dispatching FREQ command"); - h.handle(spawner, dds_tx, cmd) + h.handle(spawner, cmd) } }, None => Some(Error::UnknownCommand), diff --git a/firmware/src/at/handler.rs b/firmware/src/at/handler.rs index 175d101..14dde22 100644 --- a/firmware/src/at/handler.rs +++ b/firmware/src/at/handler.rs @@ -2,21 +2,13 @@ // SPDX-License-Identifier: MIT use embassy_executor::Spawner; -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; -use embassy_sync::channel::Sender; use heapless::{LinearMap, String}; use crate::at::*; -use crate::channel::{CAP, Msg}; use crate::error::Error; pub trait AtHandler { - fn handle( - &self, - spawner: Spawner, - tx: Sender<'static, Cs, Msg, CAP>, - at_command: AtCommand, - ) -> Option; + fn handle(&self, spawner: Spawner, at_command: AtCommand) -> Option; } pub enum Handler { diff --git a/firmware/src/at/handlers/freq_handler.rs b/firmware/src/at/handlers/freq_handler.rs index ee7e4fb..7d5ea45 100644 --- a/firmware/src/at/handlers/freq_handler.rs +++ b/firmware/src/at/handlers/freq_handler.rs @@ -3,26 +3,20 @@ use defmt::{error, info}; use embassy_executor::Spawner; -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; -use embassy_sync::channel::Sender; +use crate::DDS_CH; use crate::at::{AtCommand, AtHandler}; -use crate::channel::{CAP, Msg}; +use crate::channel::*; use crate::error::Error; use crate::hexa_config::*; pub struct FreqHandler; impl AtHandler for FreqHandler { - fn handle( - &self, - spawner: Spawner, - dds_tx: Sender<'static, Cs, Msg, CAP>, - at_command: AtCommand, - ) -> Option { + fn handle(&self, spawner: Spawner, at_command: AtCommand) -> Option { if is_dds_available() { info!("Setting FREQ: {:?}", at_command.id.as_str()); - let _ = spawner.spawn(freq_task(dds_tx, at_command)); + let _ = spawner.spawn(freq_task(at_command)); None } else { error!("DDS busy, cannot set FREQ ({})", at_command.id.as_str()); @@ -32,6 +26,11 @@ impl AtHandler for FreqHandler { } #[embassy_executor::task] -async fn freq_task(dds_tx: Sender<'static, Cs, Msg, CAP>, at_command: AtCommand) { - dds_tx.send(Msg::FreqWithValue(at_command)).await; +async fn freq_task(at_command: AtCommand) { + info!( + "Sending FREQ command to DDS task: {}", + at_command.id.as_str() + ); + DDS_CH.send(Msg::FreqWithValue(at_command)).await; + info!("FREQ command sent to DDS task"); } diff --git a/firmware/src/at/handlers/fwupdate_handler.rs b/firmware/src/at/handlers/fwupdate_handler.rs index b0cb160..4effdae 100644 --- a/firmware/src/at/handlers/fwupdate_handler.rs +++ b/firmware/src/at/handlers/fwupdate_handler.rs @@ -3,22 +3,14 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; -use embassy_sync::channel::Sender; use crate::at::*; -use crate::channel::{CAP, Msg}; use crate::error::Error; pub struct FwUpdateHandler; impl AtHandler for FwUpdateHandler { - fn handle( - &self, - spawner: Spawner, - _at_tx: Sender<'static, Cs, Msg, CAP>, - _at_command: AtCommand, - ) -> Option { + fn handle(&self, spawner: Spawner, _at_command: AtCommand) -> Option { info!("Firmware update requested - spawning BOOTSEL task"); let _ = spawner.spawn(fwupdate_task()); None diff --git a/firmware/src/at/handlers/reset_handler.rs b/firmware/src/at/handlers/reset_handler.rs index 6802db1..f3785fc 100644 --- a/firmware/src/at/handlers/reset_handler.rs +++ b/firmware/src/at/handlers/reset_handler.rs @@ -3,22 +3,14 @@ use cortex_m::peripheral::SCB; use embassy_executor::Spawner; -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; -use embassy_sync::channel::Sender; use crate::at::*; -use crate::channel::{CAP, Msg}; use crate::error::Error; pub struct ResetHandler; impl AtHandler for ResetHandler { - fn handle( - &self, - _spawner: Spawner, - _at_tx: Sender<'static, Cs, Msg, CAP>, - _at_command: AtCommand, - ) -> Option { + fn handle(&self, _spawner: Spawner, _at_command: AtCommand) -> Option { SCB::sys_reset(); } } diff --git a/firmware/src/at/handlers/setrgb_handler.rs b/firmware/src/at/handlers/setrgb_handler.rs index ed3c8a4..f6413f8 100644 --- a/firmware/src/at/handlers/setrgb_handler.rs +++ b/firmware/src/at/handlers/setrgb_handler.rs @@ -2,28 +2,22 @@ // SPDX-License-Identifier: MIT use embassy_executor::Spawner; -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; -use embassy_sync::channel::Sender; +use crate::RGB_CH; use crate::at::{AtCommand, AtHandler}; -use crate::channel::{CAP, Msg}; +use crate::channel::*; use crate::error::Error; pub struct SetRgbHandler; impl AtHandler for SetRgbHandler { - fn handle( - &self, - spawner: Spawner, - rgb_tx: Sender<'static, Cs, Msg, CAP>, - at_command: AtCommand, - ) -> Option { - let _ = spawner.spawn(setrgb_task(rgb_tx, at_command)); + fn handle(&self, spawner: Spawner, at_command: AtCommand) -> Option { + let _ = spawner.spawn(setrgb_task(at_command)); None } } #[embassy_executor::task] -async fn setrgb_task(rgb_tx: Sender<'static, Cs, Msg, CAP>, at_command: AtCommand) { - rgb_tx.send(Msg::RgbWithValue(at_command)).await; +async fn setrgb_task(at_command: AtCommand) { + RGB_CH.send(Msg::RgbWithValue(at_command)).await; } diff --git a/firmware/src/at/handlers/version_handler.rs b/firmware/src/at/handlers/version_handler.rs index 08c4b66..4a2849b 100644 --- a/firmware/src/at/handlers/version_handler.rs +++ b/firmware/src/at/handlers/version_handler.rs @@ -3,26 +3,20 @@ use defmt::error; use embassy_executor::Spawner; -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; -use embassy_sync::channel::Sender; use heapless::{String, Vec}; +use crate::AT_CH; use crate::at::*; -use crate::channel::{CAP, Msg}; +use crate::channel::*; use crate::error::Error; use crate::hexa_config::CONF_VERSION; pub struct VersionHandler; impl AtHandler for VersionHandler { - fn handle( - &self, - spawner: Spawner, - at_tx: Sender<'static, Cs, Msg, CAP>, - at_command: AtCommand, - ) -> Option { + fn handle(&self, spawner: Spawner, at_command: AtCommand) -> Option { if at_command.is_query { - let _ = spawner.spawn(version_task(at_tx)); + let _ = spawner.spawn(version_task()); None } else { error!("VERSION command is a query only"); @@ -32,7 +26,7 @@ impl AtHandler for VersionHandler { } #[embassy_executor::task] -async fn version_task(at_tx: Sender<'static, Cs, Msg, CAP>) { +async fn version_task() { let mut name = String::<16>::new(); name.push_str("VERSION").unwrap(); @@ -46,5 +40,5 @@ async fn version_task(at_tx: Sender<'static, Cs, Msg, CAP>) { params, is_query: false, }; - at_tx.send(Msg::AtCmdOutput(at_command)).await; + AT_CH.send(Msg::AtCmdOutput(at_command)).await; } diff --git a/firmware/src/channel/channel_manager.rs b/firmware/src/channel/channel_manager.rs deleted file mode 100644 index aa195fe..0000000 --- a/firmware/src/channel/channel_manager.rs +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-FileCopyrightText: 2025 hexaTune LLC -// SPDX-License-Identifier: MIT - -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; -use embassy_sync::channel::{Channel, Receiver, Sender}; -use heapless::LinearMap; - -use crate::channel::ModuleId; -use crate::channel::Msg; - -pub struct ChannelManager { - senders: LinearMap, MAX_CH>, -} - -impl ChannelManager { - pub const fn new() -> Self { - Self { - senders: LinearMap::new(), - } - } - - pub fn register( - &mut self, - id: ModuleId, - ch: &'static Channel, - ) -> Receiver<'static, Cs, Msg, CAP> { - let tx = ch.sender(); - let rx = ch.receiver(); - let _ = self.senders.insert(id, tx); - rx - } - - pub fn tx(&self, id: ModuleId) -> Option> { - self.senders.get(&id).copied() - } -} diff --git a/firmware/src/channel/channel_type.rs b/firmware/src/channel/channel_type.rs deleted file mode 100644 index 85e15bb..0000000 --- a/firmware/src/channel/channel_type.rs +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-FileCopyrightText: 2025 hexaTune LLC -// SPDX-License-Identifier: MIT - -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; -use embassy_sync::channel::Channel; - -use crate::channel::Msg; - -pub const CAP: usize = 16; - -pub static USB_CH: Channel = Channel::new(); -pub static AT_CH: Channel = Channel::new(); -pub static RGB_CH: Channel = Channel::new(); -pub static DDS_CH: Channel = Channel::new(); diff --git a/firmware/src/channel/message_type.rs b/firmware/src/channel/message_type.rs index dea2cdb..0d7f26f 100644 --- a/firmware/src/channel/message_type.rs +++ b/firmware/src/channel/message_type.rs @@ -15,7 +15,6 @@ pub enum Msg { AtCmdOutput(AtCommand), Done(MsgId), Err(MsgId, Error), - ErrWOCommand(Error), UsbTxLine(MsgString), RgbWithValue(AtCommand), FreqWithValue(AtCommand), diff --git a/firmware/src/channel/mod.rs b/firmware/src/channel/mod.rs index a171e42..996d09f 100644 --- a/firmware/src/channel/mod.rs +++ b/firmware/src/channel/mod.rs @@ -1,11 +1,5 @@ // SPDX-FileCopyrightText: 2025 hexaTune LLC // SPDX-License-Identifier: MIT -mod channel_type; -pub use channel_type::*; mod message_type; pub use message_type::*; -mod module_type; -pub use module_type::*; -mod channel_manager; -pub use channel_manager::*; diff --git a/firmware/src/channel/module_type.rs b/firmware/src/channel/module_type.rs deleted file mode 100644 index d87fa1e..0000000 --- a/firmware/src/channel/module_type.rs +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-FileCopyrightText: 2025 hexaTune LLC -// SPDX-License-Identifier: MIT - -#[derive(Clone, Copy, PartialEq, Eq, Hash)] -pub enum ModuleId { - Usb, - At, - Rgb, - Dds, -} diff --git a/firmware/src/dds/ad985x.rs b/firmware/src/dds/ad985x.rs index 18f0c2f..e1c4b98 100644 --- a/firmware/src/dds/ad985x.rs +++ b/firmware/src/dds/ad985x.rs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2025 hexaTune LLC // SPDX-License-Identifier: MIT +use defmt::*; use embassy_rp::gpio::Output; use embassy_time::{Duration, Timer}; @@ -124,9 +125,9 @@ impl Ad985x { if let Some(e) = self.set_freq_immediate(freq_hz).await { return Some(e); } - + info!("Waiting time ms {}", dwell_ms); Timer::after(Duration::from_millis(dwell_ms as u64)).await; - + info!("Wait complete"); if let Some(e) = self.down().await { return Some(e); } diff --git a/firmware/src/dds/dds_task.rs b/firmware/src/dds/dds_task.rs index c8baffa..84dc2ce 100644 --- a/firmware/src/dds/dds_task.rs +++ b/firmware/src/dds/dds_task.rs @@ -2,61 +2,73 @@ // SPDX-License-Identifier: MIT use defmt::*; -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; -use embassy_sync::channel::{Receiver, Sender}; use {defmt_rtt as _, panic_probe as _}; -use crate::channel::{CAP, Msg}; +use crate::AT_CH; +use crate::DDS_CH; +use crate::channel::*; use crate::dds::Ad985x; use crate::error::Error; #[embassy_executor::task] -pub async fn dds_task( - mut ad985x: Ad985x, - dds_rx: Receiver<'static, Cs, Msg, CAP>, - at_tx: Sender<'static, Cs, Msg, CAP>, -) { +pub async fn dds_task(mut ad985x: Ad985x) { info!("Starting DDS task"); loop { - if let Msg::FreqWithValue(at_command) = dds_rx.receive().await { + if let Msg::FreqWithValue(at_command) = DDS_CH.receive().await { + info!( + "Received FREQ command in DDS task: {}", + at_command.id.as_str() + ); + + info!("Parsing FREQ command parameters"); if at_command.params.len() != 2 { error!( "FREQ command requires 3 parameters, got {}", at_command.params.len() ); - at_tx.send(Msg::Err(at_command.id, Error::DdsBusy)).await; + AT_CH.send(Msg::Err(at_command.id, Error::DdsBusy)).await; return; } + info!("Parameters parsed successfully"); + info!("Extracting frequency and time_ms values"); let freq = match at_command.params[0].parse::() { Ok(v) => v, Err(_) => { error!("Invalid frequency value: {}", at_command.id.as_str()); - at_tx.send(Msg::Err(at_command.id, Error::ParamValue)).await; + AT_CH.send(Msg::Err(at_command.id, Error::ParamValue)).await; return; } }; + info!("Frequency value extracted: {}", freq); let time_ms = match at_command.params[1].parse::() { Ok(v) => v, Err(_) => { error!("Invalid time_ms value: {}", at_command.id.as_str()); - at_tx.send(Msg::Err(at_command.id, Error::ParamValue)).await; + AT_CH.send(Msg::Err(at_command.id, Error::ParamValue)).await; return; } }; - - at_tx.send(Msg::SetDdsAvailable(false)).await; + info!("time_ms value extracted: {}", time_ms); + info!("Setting Device Available to false"); + AT_CH.send(Msg::SetDdsAvailable(false)).await; info!( "Setting FREQ to ({}) over {} ms", at_command.id.as_str(), time_ms ); + info!("Starting frequency set..."); let err = ad985x.set_freq(freq, time_ms).await; - at_tx.send(Msg::SetDdsAvailable(true)).await; + info!("Frequency set complete."); + AT_CH.send(Msg::SetDdsAvailable(true)).await; + info!("Setting Device Available to true"); if let Some(err) = err { error!("Error setting FREQ: {:?}", err.code()); - at_tx.send(Msg::Err(at_command.id, err)).await; + AT_CH.send(Msg::Err(at_command.id, err)).await; + info!("Error sent"); } else { - at_tx.send(Msg::Done(at_command.id)).await; + info!("FREQ set successfully"); + AT_CH.send(Msg::Done(at_command.id)).await; + info!("Done sent"); } } } diff --git a/firmware/src/main.rs b/firmware/src/main.rs index 1bfb09e..b2d7014 100644 --- a/firmware/src/main.rs +++ b/firmware/src/main.rs @@ -6,8 +6,8 @@ use defmt::*; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; +use embassy_sync::channel::Channel; use embassy_sync::mutex::Mutex as AsyncMutex; -use heapless::{String, Vec}; use {defmt_rtt as _, panic_probe as _}; mod at; @@ -19,15 +19,12 @@ mod rgb; mod sysex; mod usb; -use crate::channel::CAP; - -static mut CORE1_STACK: embassy_rp::multicore::Stack<4096> = embassy_rp::multicore::Stack::new(); -static EXECUTOR0: static_cell::StaticCell = - static_cell::StaticCell::new(); -static EXECUTOR1: static_cell::StaticCell = - static_cell::StaticCell::new(); -static CHANNEL_MANAGER: static_cell::StaticCell> = - static_cell::StaticCell::new(); +use crate::channel::*; +pub const CAP: usize = 16; +pub static USB_CH: Channel = Channel::new(); +pub static AT_CH: Channel = Channel::new(); +pub static RGB_CH: Channel = Channel::new(); +pub static DDS_CH: Channel = Channel::new(); embassy_rp::bind_interrupts!(struct IrqUsb { USBCTRL_IRQ => embassy_rp::usb::InterruptHandler; @@ -37,24 +34,11 @@ embassy_rp::bind_interrupts!(struct IrqPio { }); #[embassy_executor::main] -async fn main(_spawner: embassy_executor::Spawner) { +async fn main(spawner: embassy_executor::Spawner) { //Initialize the RP2040 let p = embassy_rp::init(Default::default()); info!("Starting hexaGenMini firmware"); - //Create the channel manager - info!("Initializing channel manager"); - let mut mgr = channel::ChannelManager::<{ channel::CAP }, 8>::new(); - let usb_rx = mgr.register(channel::ModuleId::Usb, &channel::USB_CH); - let at_rx = mgr.register(channel::ModuleId::At, &channel::AT_CH); - let rgb_rx = mgr.register(channel::ModuleId::Rgb, &channel::RGB_CH); - let dds_rx = mgr.register(channel::ModuleId::Dds, &channel::DDS_CH); - let mgr_ref: &'static channel::ChannelManager<{ channel::CAP }, 8> = CHANNEL_MANAGER.init(mgr); - let at_tx = mgr_ref.tx(channel::ModuleId::At).unwrap(); - let usb_tx = mgr_ref.tx(channel::ModuleId::Usb).unwrap(); - let rgb_tx = mgr_ref.tx(channel::ModuleId::Rgb).unwrap(); - let dds_tx = mgr_ref.tx(channel::ModuleId::Dds).unwrap(); - //USB module info!("Initializing USB"); let driver = embassy_rp::usb::Driver::new(p.USB, IrqUsb); @@ -98,48 +82,24 @@ async fn main(_spawner: embassy_executor::Spawner) { //Dummy Led let led = embassy_rp::gpio::Output::new(p.PIN_25, embassy_rp::gpio::Level::Low); - embassy_rp::multicore::spawn_core1( - p.CORE1, - unsafe { &mut *core::ptr::addr_of_mut!(CORE1_STACK) }, - move || { - let executor1 = EXECUTOR1.init(embassy_executor::Executor::new()); - executor1.run(|spawner| { - unwrap!(spawner.spawn(dds::dds_task(ad9850, dds_rx, at_tx))); - }); - }, - ); - - //Core0 Task Spawner - let executor0 = EXECUTOR0.init(embassy_executor::Executor::new()); - executor0.run(|spawner| { - //RGB task - unwrap!(spawner.spawn(rgb::rgb_task(rgb_led, rgb_rx, at_tx))); - //AT tasks - unwrap!(spawner.spawn(at::at_task( - dispatcher, spawner, at_rx, at_tx, usb_tx, rgb_tx, dds_tx, - ))); - //USB Device task - unwrap!(spawner.spawn(usb::dev_task(device))); - //USB IO task - unwrap!(spawner.spawn(usb::usb_io_task(midi_mutex, usb_rx, at_tx))); - //Main loop task - unwrap!(spawner.spawn(main_loop_task(at_tx, led))); - }); + spawner.spawn(at::at_task(dispatcher, spawner)).unwrap(); + spawner.spawn(rgb::rgb_task(rgb_led)).unwrap(); + spawner.spawn(usb::dev_task(device)).unwrap(); + spawner.spawn(usb::usb_io_task(midi_mutex)).unwrap(); + spawner.spawn(dds::dds_task(ad9850)).unwrap(); + spawner.spawn(main_loop_task(led)).unwrap(); } #[embassy_executor::task] -pub async fn main_loop_task( - at_tx: embassy_sync::channel::Sender<'static, Cs, channel::Msg, CAP>, - mut led: embassy_rp::gpio::Output<'static>, -) { +pub async fn main_loop_task(mut led: embassy_rp::gpio::Output<'static>) { //Dummy task to blink the LED loop { info!("Blink!"); - let mut name = String::<16>::new(); + /*let mut name = String::<16>::new(); name.push_str("STATUS").unwrap(); let mut params = Vec::, 8>::new(); - let mut status_param = String::<16>::try_from("AVAILABLE").unwrap(); + let mut status_param = String::<16>::try_from("AVAILABLE").unwrap();*/ if hexa_config::is_dds_available() { led.set_high(); @@ -147,21 +107,21 @@ pub async fn main_loop_task( led.set_low(); embassy_time::Timer::after_secs(5).await; } else { - status_param = String::<16>::try_from("GENERATING").unwrap(); - for _ in 0..5 { + //status_param = String::<16>::try_from("GENERATING").unwrap(); + for _ in 0..50 { led.set_high(); - embassy_time::Timer::after_secs(1).await; + embassy_time::Timer::after_millis(100).await; led.set_low(); - embassy_time::Timer::after_secs(1).await; + embassy_time::Timer::after_millis(100).await; } } - params.push(status_param).ok(); + /*params.push(status_param).ok(); let at_command = at::AtCommand { id: at::get_empty_id(), name, params, is_query: false, - }; - at_tx.send(channel::Msg::AtCmdOutput(at_command)).await; + };*/ + //AT_CH.send(channel::Msg::AtCmdOutput(at_command)).await; } } diff --git a/firmware/src/main_multicore.rs b/firmware/src/main_multicore.rs new file mode 100644 index 0000000..ff07515 --- /dev/null +++ b/firmware/src/main_multicore.rs @@ -0,0 +1,153 @@ +// SPDX-FileCopyrightText: 2025 hexaTune LLC +// SPDX-License-Identifier: MIT + +#![no_std] +#![no_main] + +use defmt::*; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; +use embassy_sync::channel::Channel; +use embassy_sync::mutex::Mutex as AsyncMutex; +use heapless::{String, Vec}; +use {defmt_rtt as _, panic_probe as _}; + +mod at; +mod channel; +mod dds; +mod error; +mod hexa_config; +mod rgb; +mod sysex; +mod usb; + +use crate::channel::*; +pub const CAP: usize = 16; +pub static USB_CH: Channel = Channel::new(); +pub static AT_CH: Channel = Channel::new(); +pub static RGB_CH: Channel = Channel::new(); +pub static DDS_CH: Channel = Channel::new(); + +static mut CORE1_STACK: embassy_rp::multicore::Stack<4096> = embassy_rp::multicore::Stack::new(); +static EXECUTOR0: static_cell::StaticCell = + static_cell::StaticCell::new(); +static EXECUTOR1: static_cell::StaticCell = + static_cell::StaticCell::new(); + +embassy_rp::bind_interrupts!(struct IrqUsb { + USBCTRL_IRQ => embassy_rp::usb::InterruptHandler; +}); +embassy_rp::bind_interrupts!(struct IrqPio { + PIO0_IRQ_0 => embassy_rp::pio::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: embassy_executor::Spawner) { + //Initialize the RP2040 + let p = embassy_rp::init(Default::default()); + info!("Starting hexaGenMini firmware"); + + //USB module + info!("Initializing USB"); + let driver = embassy_rp::usb::Driver::new(p.USB, IrqUsb); + let usb::UsbMidi { device, midi } = usb::init(driver); + + static MIDI_CELL: static_cell::StaticCell>> = + static_cell::StaticCell::new(); + let midi_mutex: &'static AsyncMutex> = + MIDI_CELL.init(AsyncMutex::new(midi)); + + //AT module + info!("Initializing AT command dispatcher"); + static DISPATCHER: static_cell::StaticCell = static_cell::StaticCell::new(); + let dispatcher = DISPATCHER.init(at::AtDispatcher::new()); + + //Led module + info!("Initializing RGB LED"); + let embassy_rp::pio::Pio { + mut common, sm0, .. + } = embassy_rp::pio::Pio::new(p.PIO0, IrqPio); + let program = embassy_rp::pio_programs::ws2812::PioWs2812Program::new(&mut common); + let ws2812 = embassy_rp::pio_programs::ws2812::PioWs2812::new( + &mut common, + sm0, + p.DMA_CH0, + p.PIN_23, + &program, + ); + let rgb_led = rgb::RgbLed::new(ws2812); + + //DDS module + let ad9850 = dds::Ad985x::new( + embassy_rp::gpio::Output::new(p.PIN_2, embassy_rp::gpio::Level::Low), + embassy_rp::gpio::Output::new(p.PIN_3, embassy_rp::gpio::Level::Low), + embassy_rp::gpio::Output::new(p.PIN_4, embassy_rp::gpio::Level::Low), + embassy_rp::gpio::Output::new(p.PIN_5, embassy_rp::gpio::Level::Low), + 125_000_000, + 0, + ); + + //Dummy Led + let led = embassy_rp::gpio::Output::new(p.PIN_25, embassy_rp::gpio::Level::Low); + + embassy_rp::multicore::spawn_core1( + p.CORE1, + unsafe { &mut *core::ptr::addr_of_mut!(CORE1_STACK) }, + move || { + let executor1 = EXECUTOR1.init(embassy_executor::Executor::new()); + executor1.run(|spawner| { + unwrap!(spawner.spawn(dds::dds_task(ad9850))); + }); + }, + ); + + //Core0 Task Spawner + let executor0 = EXECUTOR0.init(embassy_executor::Executor::new()); + executor0.run(|spawner| { + //RGB task + unwrap!(spawner.spawn(rgb::rgb_task(rgb_led))); + //AT tasks + unwrap!(spawner.spawn(at::at_task(dispatcher, spawner))); + //USB Device task + unwrap!(spawner.spawn(usb::dev_task(device))); + //USB IO task + unwrap!(spawner.spawn(usb::usb_io_task(midi_mutex))); + //Main loop task + unwrap!(spawner.spawn(main_loop_task(led))); + }); +} + +#[embassy_executor::task] +pub async fn main_loop_task(mut led: embassy_rp::gpio::Output<'static>) { + //Dummy task to blink the LED + loop { + info!("Blink!"); + let mut name = String::<16>::new(); + name.push_str("STATUS").unwrap(); + + let mut params = Vec::, 8>::new(); + let mut status_param = String::<16>::try_from("AVAILABLE").unwrap(); + + if hexa_config::is_dds_available() { + led.set_high(); + embassy_time::Timer::after_secs(5).await; + led.set_low(); + embassy_time::Timer::after_secs(5).await; + } else { + status_param = String::<16>::try_from("GENERATING").unwrap(); + for _ in 0..5 { + led.set_high(); + embassy_time::Timer::after_secs(1).await; + led.set_low(); + embassy_time::Timer::after_secs(1).await; + } + } + params.push(status_param).ok(); + let at_command = at::AtCommand { + id: at::get_empty_id(), + name, + params, + is_query: false, + }; + AT_CH.send(channel::Msg::AtCmdOutput(at_command)).await; + } +} diff --git a/firmware/src/rgb/rgb_task.rs b/firmware/src/rgb/rgb_task.rs index fb73804..b3bce79 100644 --- a/firmware/src/rgb/rgb_task.rs +++ b/firmware/src/rgb/rgb_task.rs @@ -2,30 +2,26 @@ // SPDX-License-Identifier: MIT use defmt::*; -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; -use embassy_sync::channel::{Receiver, Sender}; use {defmt_rtt as _, panic_probe as _}; -use crate::channel::{CAP, Msg}; +use crate::AT_CH; +use crate::RGB_CH; +use crate::channel::*; use crate::error::Error; use crate::rgb::RgbLed; #[embassy_executor::task] -pub async fn rgb_task( - mut rgb_led: RgbLed, - rgb_rx: Receiver<'static, Cs, Msg, CAP>, - at_tx: Sender<'static, Cs, Msg, CAP>, -) { +pub async fn rgb_task(mut rgb_led: RgbLed) { info!("Starting RGB task"); loop { - if let Msg::RgbWithValue(at_command) = rgb_rx.receive().await { + if let Msg::RgbWithValue(at_command) = RGB_CH.receive().await { if at_command.params.len() != 3 { error!( "SETRGB command {} requires 3 parameters, got {}", at_command.id.as_str(), at_command.params.len() ); - at_tx.send(Msg::Err(at_command.id, Error::ParamCount)).await; + AT_CH.send(Msg::Err(at_command.id, Error::ParamCount)).await; return; } // check params are u8 @@ -33,7 +29,7 @@ pub async fn rgb_task( Ok(v) => v, Err(_) => { error!("Invalid R value: {}", at_command.id.as_str()); - at_tx.send(Msg::Err(at_command.id, Error::ParamValue)).await; + AT_CH.send(Msg::Err(at_command.id, Error::ParamValue)).await; return; } }; @@ -41,7 +37,7 @@ pub async fn rgb_task( Ok(v) => v, Err(_) => { error!("Invalid G value: {}", at_command.id.as_str()); - at_tx.send(Msg::Err(at_command.id, Error::ParamValue)).await; + AT_CH.send(Msg::Err(at_command.id, Error::ParamValue)).await; return; } }; @@ -49,7 +45,7 @@ pub async fn rgb_task( Ok(v) => v, Err(_) => { error!("Invalid B value: {}", at_command.id.as_str()); - at_tx.send(Msg::Err(at_command.id, Error::ParamValue)).await; + AT_CH.send(Msg::Err(at_command.id, Error::ParamValue)).await; return; } }; @@ -57,7 +53,7 @@ pub async fn rgb_task( rgb_led.set_rgb(r, g, b).await; info!("RGB set"); info!("Sending DONE from RGB task"); - at_tx.send(Msg::Done(at_command.id)).await; + AT_CH.send(Msg::Done(at_command.id)).await; } } } diff --git a/firmware/src/usb/usb_task.rs b/firmware/src/usb/usb_task.rs index b955611..32bd300 100644 --- a/firmware/src/usb/usb_task.rs +++ b/firmware/src/usb/usb_task.rs @@ -4,10 +4,12 @@ use defmt::{error, info}; use embassy_futures::select::{Either, select}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; -use embassy_sync::channel::{Receiver, Sender}; use embassy_sync::mutex::Mutex; -use crate::channel::{CAP, Msg}; +use crate::AT_CH; +use crate::USB_CH; +use crate::at::get_empty_id; +use crate::channel::*; use crate::error::Error; use crate::sysex::{build_sysex, extract_sysex_payload, sysex_to_usb_midi_packets}; use crate::usb::{MyMidiClass, MyUsbDevice}; @@ -19,11 +21,7 @@ pub async fn dev_task(mut dev: MyUsbDevice<'static>) { } #[embassy_executor::task] -pub async fn usb_io_task( - midi: &'static Mutex>, - usb_rx: Receiver<'static, Cs, Msg, CAP>, - at_tx: Sender<'static, Cs, Msg, CAP>, -) { +pub async fn usb_io_task(midi: &'static Mutex>) { info!("Starting unified USB IO task"); loop { let read_fut = async { @@ -35,7 +33,7 @@ pub async fn usb_io_task( (n, buf) }; - let tx_fut = async { usb_rx.receive().await }; + let tx_fut = async { USB_CH.receive().await }; match select(read_fut, tx_fut).await { Either::First((n, buf)) => { @@ -49,22 +47,26 @@ pub async fn usb_io_task( if let Ok(input) = core::str::from_utf8(&payload) { match heapless::String::<64>::try_from(input) { Ok(line) => { - at_tx.send(Msg::AtRxLine(line)).await; + AT_CH.send(Msg::AtRxLine(line)).await; } Err(_) => { error!("Error Code: {}", Error::InvalidDataLength.code()); - at_tx - .send(Msg::ErrWOCommand(Error::InvalidDataLength)) + AT_CH + .send(Msg::Err(get_empty_id(), Error::InvalidDataLength)) .await; } } } else { error!("Error Code: {}", Error::InvalidUtf8.code()); - at_tx.send(Msg::ErrWOCommand(Error::InvalidUtf8)).await; + AT_CH + .send(Msg::Err(get_empty_id(), Error::InvalidUtf8)) + .await; } } else { error!("Error Code: {}", Error::InvalidSysEx.code()); - at_tx.send(Msg::ErrWOCommand(Error::InvalidSysEx)).await; + /*AT_CH + .send(Msg::Err(get_empty_id(), Error::InvalidSysEx)) + .await;*/ } } @@ -76,7 +78,7 @@ pub async fn usb_io_task( let mut m = midi.lock().await; for pkt in packets.iter() { - info!("Sending MIDI packet: {:?}", pkt); + //info!("Sending MIDI packet: {:?}", pkt); if let Err(e) = m.write_packet(pkt).await { error!("USB write error: {:?}", e); } From fb40bd0977d25a4d57ac0e14b1ec06011ef65e2f Mon Sep 17 00:00:00 2001 From: Husamettin ARABACI Date: Mon, 27 Oct 2025 03:49:00 +0300 Subject: [PATCH 12/16] feat(multicore): fixed multicore Fixed channel for multicore tasks --- firmware/src/at/at_task.rs | 15 ++ firmware/src/at/command.rs | 13 + firmware/src/at/dispatcher.rs | 4 + firmware/src/at/handler.rs | 7 + firmware/src/at/handlers/mod.rs | 2 + firmware/src/at/handlers/operation_handler.rs | 52 ++++ firmware/src/channel/message_type.rs | 4 + firmware/src/dds/dds_task.rs | 248 ++++++++++++++---- firmware/src/dds/dds_type.rs | 40 +++ firmware/src/dds/mod.rs | 2 + firmware/src/error/mod.rs | 20 +- firmware/src/main.rs | 14 - 12 files changed, 343 insertions(+), 78 deletions(-) create mode 100644 firmware/src/at/handlers/operation_handler.rs create mode 100644 firmware/src/dds/dds_type.rs diff --git a/firmware/src/at/at_task.rs b/firmware/src/at/at_task.rs index ee3cada..5580a73 100644 --- a/firmware/src/at/at_task.rs +++ b/firmware/src/at/at_task.rs @@ -3,6 +3,7 @@ use defmt::{error, info}; use embassy_executor::Spawner; +use heapless::String; use {defmt_rtt as _, panic_probe as _}; use crate::AT_CH; @@ -14,6 +15,7 @@ use crate::hexa_config::*; #[embassy_executor::task] pub async fn at_task(dispatcher: &'static AtDispatcher, spawner: Spawner) { info!("Starting AT task"); + let mut last_operation_status: String<64> = String::new(); loop { match AT_CH.receive().await { Msg::AtRxLine(line) => { @@ -41,6 +43,19 @@ pub async fn at_task(dispatcher: &'static AtDispatcher, spawner: Spawner) { Msg::SetDdsAvailable(status) => { set_dds_available(status); } + Msg::Completed(at_command) => { + let compiled = compile_at_completed(at_command); + info!("Sending done: {}", compiled.as_str()); + USB_CH.send(Msg::UsbTxLine(compiled)).await; + } + Msg::SetOperationStatus(status) => { + last_operation_status = status; + } + Msg::GetOperationStatus => { + USB_CH + .send(Msg::UsbTxLine(last_operation_status.clone())) + .await; + } _ => {} } } diff --git a/firmware/src/at/command.rs b/firmware/src/at/command.rs index f8b9475..6318713 100644 --- a/firmware/src/at/command.rs +++ b/firmware/src/at/command.rs @@ -6,6 +6,7 @@ use heapless::{String, Vec}; use crate::error::Error; +#[derive(Debug, Clone)] pub struct AtCommand { pub id: String<16>, pub name: String<16>, @@ -128,6 +129,18 @@ pub fn compile_at_done(id: String<16>) -> String<64> { at_cmd.compile() } +pub fn compile_at_completed(at_command: AtCommand) -> String<64> { + let mut at_cmd = AtCommand { + id: at_command.id, + name: at_command.name, + params: at_command.params, + is_query: false, + }; + let completede_param = String::<16>::try_from("COMPLETED").unwrap(); + at_cmd.params.push(completede_param).ok(); + at_cmd.compile() +} + pub fn compile_at_error(id: String<16>, e: Error) -> String<64> { let at_cmd = AtCommand { id, diff --git a/firmware/src/at/dispatcher.rs b/firmware/src/at/dispatcher.rs index 6c7066e..935341e 100644 --- a/firmware/src/at/dispatcher.rs +++ b/firmware/src/at/dispatcher.rs @@ -44,6 +44,10 @@ impl AtDispatcher { info!("Dispatching FREQ command"); h.handle(spawner, cmd) } + Handler::Operation(h) => { + info!("Dispatching OPERATION command"); + h.handle(spawner, cmd) + } }, None => Some(Error::UnknownCommand), }, diff --git a/firmware/src/at/handler.rs b/firmware/src/at/handler.rs index 14dde22..8567778 100644 --- a/firmware/src/at/handler.rs +++ b/firmware/src/at/handler.rs @@ -17,6 +17,7 @@ pub enum Handler { Reset(ResetHandler), FwUpdate(FwUpdateHandler), Freq(FreqHandler), + Operation(OperationHandler), } pub fn register_all() -> LinearMap, Handler, 8> { @@ -52,5 +53,11 @@ pub fn register_all() -> LinearMap, Handler, 8> { ) .ok(); + map.insert( + String::<16>::try_from("OPERATION").unwrap(), + Handler::Operation(OperationHandler), + ) + .ok(); + map } diff --git a/firmware/src/at/handlers/mod.rs b/firmware/src/at/handlers/mod.rs index b3d56c3..7c4329a 100644 --- a/firmware/src/at/handlers/mod.rs +++ b/firmware/src/at/handlers/mod.rs @@ -11,3 +11,5 @@ mod reset_handler; pub use reset_handler::*; mod freq_handler; pub use freq_handler::*; +mod operation_handler; +pub use operation_handler::*; diff --git a/firmware/src/at/handlers/operation_handler.rs b/firmware/src/at/handlers/operation_handler.rs new file mode 100644 index 0000000..564dcc4 --- /dev/null +++ b/firmware/src/at/handlers/operation_handler.rs @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: 2025 hexaTune LLC +// SPDX-License-Identifier: MIT + +use defmt::{error, info}; +use embassy_executor::Spawner; + +use crate::AT_CH; +use crate::DDS_CH; +use crate::at::{AtCommand, AtHandler}; +use crate::channel::*; +use crate::error::Error; +use crate::hexa_config::*; + +pub struct OperationHandler; + +impl AtHandler for OperationHandler { + fn handle(&self, spawner: Spawner, at_command: AtCommand) -> Option { + if at_command.is_query { + let _ = spawner.spawn(operation_status_task()); + None + } else { + info!("Handling OPERATION command"); + if is_dds_available() { + info!("Setting OPERATION: {:?}", at_command.id.as_str()); + let _ = spawner.spawn(operation_task(at_command)); + None + } else { + error!( + "DDS busy, cannot set OPERATION ({})", + at_command.id.as_str() + ); + Some(Error::DdsBusy) + } + } + } +} + +#[embassy_executor::task] +async fn operation_task(at_command: AtCommand) { + info!( + "Sending OPERATION command to DDS task: {}", + at_command.id.as_str() + ); + DDS_CH.send(Msg::Operation(at_command)).await; + info!("OPERATION command sent to DDS task"); +} + +#[embassy_executor::task] +async fn operation_status_task() { + info!("Requesting OPERATION status"); + AT_CH.send(Msg::GetOperationStatus).await; +} diff --git a/firmware/src/channel/message_type.rs b/firmware/src/channel/message_type.rs index 0d7f26f..b4b42ff 100644 --- a/firmware/src/channel/message_type.rs +++ b/firmware/src/channel/message_type.rs @@ -14,9 +14,13 @@ pub enum Msg { AtRxLine(MsgString), AtCmdOutput(AtCommand), Done(MsgId), + Completed(AtCommand), Err(MsgId, Error), UsbTxLine(MsgString), RgbWithValue(AtCommand), FreqWithValue(AtCommand), SetDdsAvailable(IsDdsAvailable), + SetOperationStatus(MsgString), + GetOperationStatus, + Operation(AtCommand), } diff --git a/firmware/src/dds/dds_task.rs b/firmware/src/dds/dds_task.rs index 84dc2ce..a2886d2 100644 --- a/firmware/src/dds/dds_task.rs +++ b/firmware/src/dds/dds_task.rs @@ -1,75 +1,213 @@ // SPDX-FileCopyrightText: 2025 hexaTune LLC // SPDX-License-Identifier: MIT +use core::cell::RefCell; use defmt::*; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; +use embassy_sync::mutex::Mutex; + +use heapless::{String, Vec}; use {defmt_rtt as _, panic_probe as _}; -use crate::AT_CH; -use crate::DDS_CH; +use crate::at::*; use crate::channel::*; -use crate::dds::Ad985x; +use crate::dds::*; use crate::error::Error; +use crate::{AT_CH, DDS_CH}; + +static OPERATION: Mutex> = Mutex::new(RefCell::new(Operation::new())); #[embassy_executor::task] pub async fn dds_task(mut ad985x: Ad985x) { info!("Starting DDS task"); loop { - if let Msg::FreqWithValue(at_command) = DDS_CH.receive().await { - info!( - "Received FREQ command in DDS task: {}", - at_command.id.as_str() - ); - - info!("Parsing FREQ command parameters"); - if at_command.params.len() != 2 { - error!( - "FREQ command requires 3 parameters, got {}", - at_command.params.len() + match DDS_CH.receive().await { + Msg::Operation(at_command) => { + info!( + "Received OPERATION command in DDS task: {}", + at_command.id.as_str() ); - AT_CH.send(Msg::Err(at_command.id, Error::DdsBusy)).await; - return; - } - info!("Parameters parsed successfully"); - info!("Extracting frequency and time_ms values"); - let freq = match at_command.params[0].parse::() { - Ok(v) => v, - Err(_) => { - error!("Invalid frequency value: {}", at_command.id.as_str()); - AT_CH.send(Msg::Err(at_command.id, Error::ParamValue)).await; - return; + info!("Parsing OPERATION command parameters"); + if at_command.params.len() != 1 { + error!( + "OPERATION command requires 1 parameters, got {}", + at_command.params.len() + ); + AT_CH.send(Msg::Err(at_command.id, Error::ParamCount)).await; + continue; } - }; - info!("Frequency value extracted: {}", freq); - let time_ms = match at_command.params[1].parse::() { - Ok(v) => v, - Err(_) => { - error!("Invalid time_ms value: {}", at_command.id.as_str()); - AT_CH.send(Msg::Err(at_command.id, Error::ParamValue)).await; - return; + info!("Parameters parsed successfully"); + info!("Extracting command_type values"); + let command_type = match at_command.params[0].parse::>() { + Ok(v) => v, + Err(_) => { + error!("Invalid command_type value: {}", at_command.id.as_str()); + AT_CH.send(Msg::Err(at_command.id, Error::ParamValue)).await; + continue; + } + }; + + match command_type.as_str() { + "PREPARE" => { + info!("Preparing DDS operation"); + + // Reset the operation by replacing the inner value + let operation = OPERATION.lock().await; + *operation.borrow_mut() = Operation::new(); + + { + let mut guard = operation.borrow_mut(); + guard.set_id(at_command.id.clone()); + } + drop(operation); + + info!("DDS operation prepared"); + AT_CH.send(Msg::Completed(at_command.clone())).await; + info!("Completed sent for PREPARE command"); + + let compiled_status = compile_at_completed(at_command.clone()); + AT_CH.send(Msg::SetOperationStatus(compiled_status)).await; + } + "GENERATE" => { + info!("Starting DDS operation"); + + let mut result: Option = None; + + info!("Setting Device Available to false"); + AT_CH.send(Msg::SetDdsAvailable(false)).await; + info!("Set Device Available to false"); + + let compiled_status = compile_at_completed(at_command.clone()); + AT_CH.send(Msg::SetOperationStatus(compiled_status)).await; + + let steps = { + let operation = OPERATION.lock().await; + let guard = operation.borrow(); + guard.get_steps().clone() + }; + + let mut at_command_status = at_command.clone(); + at_command_status.params.clear(); + + for at_command_step in steps.iter() { + let mut params = Vec::, 8>::new(); + let generating_param = String::<16>::try_from("GENERATING").unwrap(); + params.push(generating_param).ok(); + + at_command_status.params = params; + + info!("Parsing FREQ command parameters"); + if at_command_step.params.len() != 2 { + error!( + "FREQ command requires 2 parameters, got {}", + at_command_step.params.len() + ); + result = Some(Error::ParamCount); + break; + } + info!("Parameters parsed successfully"); + + info!("Extracting frequency and time_ms values"); + let freq = match at_command_step.params[0].parse::() { + Ok(v) => v, + Err(_) => { + error!( + "Invalid frequency value: {}", + at_command_step.id.as_str() + ); + result = Some(Error::ParamValue); + break; + } + }; + info!("Frequency value extracted: {}", freq); + let time_ms = match at_command_step.params[1].parse::() { + Ok(v) => v, + Err(_) => { + error!( + "Invalid time_ms value: {}", + at_command_step.id.as_str() + ); + result = Some(Error::ParamValue); + break; + } + }; + info!("time_ms value extracted: {}", time_ms); + + info!( + "Setting FREQ to ({}) over {} ms", + at_command_step.id.as_str(), + time_ms + ); + + let step_id_param = + String::<16>::try_from(at_command_step.id.as_str()).unwrap(); + at_command_status.params.push(step_id_param).ok(); + let compiled_status = compile_at_completed(at_command_status.clone()); + AT_CH.send(Msg::SetOperationStatus(compiled_status)).await; + + info!("Starting frequency set..."); + let err = ad985x.set_freq(freq, time_ms).await; + info!("Frequency set complete."); + + if let Some(err) = err { + error!("Error setting FREQ: {:?}", err.code()); + result = Some(err); + break; + } else { + info!("FREQ set successfully"); + } + } + + info!("Setting Device Available to true"); + AT_CH.send(Msg::SetDdsAvailable(true)).await; + info!("Set Device Available to true"); + + if let Some(err) = result { + error!("DDS operation failed: {:?}", err.code()); + AT_CH + .send(Msg::Err(at_command.id.clone(), err.clone())) + .await; + + let compiled_status = compile_at_error(at_command.id.clone(), err); + AT_CH.send(Msg::SetOperationStatus(compiled_status)).await; + } else { + let compiled_status = compile_at_completed(at_command.clone()); + AT_CH.send(Msg::SetOperationStatus(compiled_status)).await; + } + } + _ => { + error!("Unknown command_type value: {}", at_command.id.as_str()); + AT_CH.send(Msg::Err(at_command.id, Error::ParamValue)).await; + continue; + } } - }; - info!("time_ms value extracted: {}", time_ms); - info!("Setting Device Available to false"); - AT_CH.send(Msg::SetDdsAvailable(false)).await; - info!( - "Setting FREQ to ({}) over {} ms", - at_command.id.as_str(), - time_ms - ); - info!("Starting frequency set..."); - let err = ad985x.set_freq(freq, time_ms).await; - info!("Frequency set complete."); - AT_CH.send(Msg::SetDdsAvailable(true)).await; - info!("Setting Device Available to true"); - if let Some(err) = err { - error!("Error setting FREQ: {:?}", err.code()); - AT_CH.send(Msg::Err(at_command.id, err)).await; - info!("Error sent"); - } else { - info!("FREQ set successfully"); - AT_CH.send(Msg::Done(at_command.id)).await; - info!("Done sent"); } + Msg::FreqWithValue(at_command) => { + info!( + "Received FREQ command in DDS task: {}", + at_command.id.as_str() + ); + + info!("Adding FREQ command to operation"); + let operation = OPERATION.lock().await; + + let add_result = { + let mut guard = operation.borrow_mut(); + guard.add_step(at_command.clone()) + }; + drop(operation); + + if let Err(e) = add_result { + error!("Failed to add step: operation is full"); + AT_CH.send(Msg::Err(at_command.id, e)).await; + } else { + info!("FREQ command added to operation"); + AT_CH.send(Msg::Completed(at_command)).await; + info!("Completed sent for FREQ command"); + } + } + + _ => break, } } } diff --git a/firmware/src/dds/dds_type.rs b/firmware/src/dds/dds_type.rs new file mode 100644 index 0000000..b68532a --- /dev/null +++ b/firmware/src/dds/dds_type.rs @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: 2025 hexaTune LLC +// SPDX-License-Identifier: MIT + +use heapless::{String, Vec}; + +use crate::at::AtCommand; +use crate::error::Error; + +pub type OperationId = String<16>; + +pub struct Operation { + id: OperationId, + steps: Vec, +} + +impl Operation { + pub const fn new() -> Self { + Self { + id: String::new(), + steps: Vec::new(), + } + } + + #[allow(dead_code)] + pub fn get_id(&self) -> &OperationId { + &self.id + } + + pub fn set_id(&mut self, id: OperationId) { + self.id = id; + } + + pub fn get_steps(&self) -> &heapless::Vec { + &self.steps + } + + pub fn add_step(&mut self, step: AtCommand) -> Result<(), Error> { + self.steps.push(step).map_err(|_| Error::OperationStepsFull) + } +} diff --git a/firmware/src/dds/mod.rs b/firmware/src/dds/mod.rs index f751686..b619aba 100644 --- a/firmware/src/dds/mod.rs +++ b/firmware/src/dds/mod.rs @@ -5,3 +5,5 @@ mod dds_task; pub use dds_task::*; mod ad985x; pub use ad985x::*; +mod dds_type; +pub use dds_type::*; diff --git a/firmware/src/error/mod.rs b/firmware/src/error/mod.rs index ee59309..40c79a3 100644 --- a/firmware/src/error/mod.rs +++ b/firmware/src/error/mod.rs @@ -3,15 +3,16 @@ #[derive(Debug, Clone)] pub enum Error { - InvalidCommand, // "Not an AT command" - DdsBusy, // "DDS is busy" - InvalidUtf8, // "Invalid UTF-8 in param" - InvalidSysEx, // "Invalid SysEx" - InvalidDataLength, // "Invalid data length" - ParamCount, // "Param count" - ParamValue, // "Param value" - NotAQuery, // "Not a query" - UnknownCommand, // "Unknown command" + InvalidCommand, // "Not an AT command" + DdsBusy, // "DDS is busy" + InvalidUtf8, // "Invalid UTF-8 in param" + InvalidSysEx, // "Invalid SysEx" + InvalidDataLength, // "Invalid data length" + ParamCount, // "Param count" + ParamValue, // "Param value" + NotAQuery, // "Not a query" + UnknownCommand, // "Unknown command" + OperationStepsFull, // "Operation steps full" } impl Error { @@ -27,6 +28,7 @@ impl Error { Error::ParamValue => "E001007", Error::NotAQuery => "E001008", Error::UnknownCommand => "E001009", + Error::OperationStepsFull => "E001010", } } } diff --git a/firmware/src/main.rs b/firmware/src/main.rs index b2d7014..6f425bb 100644 --- a/firmware/src/main.rs +++ b/firmware/src/main.rs @@ -95,11 +95,6 @@ pub async fn main_loop_task(mut led: embassy_rp::gpio::Output<'static>) { //Dummy task to blink the LED loop { info!("Blink!"); - /*let mut name = String::<16>::new(); - name.push_str("STATUS").unwrap(); - - let mut params = Vec::, 8>::new(); - let mut status_param = String::<16>::try_from("AVAILABLE").unwrap();*/ if hexa_config::is_dds_available() { led.set_high(); @@ -107,7 +102,6 @@ pub async fn main_loop_task(mut led: embassy_rp::gpio::Output<'static>) { led.set_low(); embassy_time::Timer::after_secs(5).await; } else { - //status_param = String::<16>::try_from("GENERATING").unwrap(); for _ in 0..50 { led.set_high(); embassy_time::Timer::after_millis(100).await; @@ -115,13 +109,5 @@ pub async fn main_loop_task(mut led: embassy_rp::gpio::Output<'static>) { embassy_time::Timer::after_millis(100).await; } } - /*params.push(status_param).ok(); - let at_command = at::AtCommand { - id: at::get_empty_id(), - name, - params, - is_query: false, - };*/ - //AT_CH.send(channel::Msg::AtCmdOutput(at_command)).await; } } From 46fa31024c26d38745be9639d73f01bd1d443747 Mon Sep 17 00:00:00 2001 From: Husamettin ARABACI Date: Sun, 2 Nov 2025 21:30:52 +0300 Subject: [PATCH 13/16] refactor(commitlint): add commitlint config Removed ipad config and configured commintlint --- .github/workflows/commitlint.yml | 54 ++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 .github/workflows/commitlint.yml diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml new file mode 100644 index 0000000..a3706b9 --- /dev/null +++ b/.github/workflows/commitlint.yml @@ -0,0 +1,54 @@ +# SPDX-FileCopyrightText: 2025 hexaTune LLC +# SPDX-License-Identifier: MIT + +name: 🧐 Commit Lint + +on: + pull_request: + branches: + - develop + types: [opened, synchronize, edited, reopened, ready_for_review] + +permissions: + contents: read + pull-requests: read + +jobs: + commitlint: + name: Commit Lint (hexaGenMini) + runs-on: ubuntu-latest + steps: + - name: Checkout (full history) + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: "20" + + - name: Install commitlint deps + run: | + npm i -D @commitlint/cli @commitlint/config-conventional + + - name: Show PR commit range (debug) + shell: bash + run: | + git fetch origin ${{ github.base_ref }} --depth=0 + BASE=$(git merge-base HEAD origin/${{ github.base_ref }}) + echo "Base ref: ${{ github.base_ref }}" + echo "Merge-base: $BASE" + echo "Commits to lint:" + git log --oneline "$BASE..HEAD" + + - name: Lint only commits introduced by this PR + shell: bash + run: | + git fetch origin ${{ github.base_ref }} --depth=0 + BASE=$(git merge-base HEAD origin/${{ github.base_ref }}) + npx commitlint \ + --from "$BASE" \ + --to "HEAD" \ + --config .github/.commitlintrc.yml \ + --verbose From f4174ad954aedbe357fa316ab46188308ef0e54b Mon Sep 17 00:00:00 2001 From: husamettinarabaci Date: Sat, 29 Nov 2025 18:45:43 +0300 Subject: [PATCH 14/16] init(project): auto project adder -m --- .github/workflows/auto-add-project.yml | 20 ++++++++++++++++++++ .github/workflows/commitlint.yml | 4 ++++ 2 files changed, 24 insertions(+) create mode 100644 .github/workflows/auto-add-project.yml diff --git a/.github/workflows/auto-add-project.yml b/.github/workflows/auto-add-project.yml new file mode 100644 index 0000000..af93d5b --- /dev/null +++ b/.github/workflows/auto-add-project.yml @@ -0,0 +1,20 @@ +# SPDX-FileCopyrightText: 2025 hexaTune LLC +# SPDX-License-Identifier: MIT + +name: Auto add issues and PRs to hTuneSys Project 16 + +on: + issues: + types: [opened] + pull_request: + types: [opened] + +jobs: + add-to-project: + runs-on: ubuntu-latest + + steps: + - uses: actions/add-to-project@v0.5.0 + with: + project-url: https://github.com/orgs/hTuneSys/projects/16 + github-token: ${{ secrets.ORG_PROJECT_TOKEN }} diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml index a3706b9..d513ad6 100644 --- a/.github/workflows/commitlint.yml +++ b/.github/workflows/commitlint.yml @@ -15,7 +15,11 @@ permissions: jobs: commitlint: +<<<<<<< Updated upstream name: Commit Lint (hexaGenMini) +======= + name: Commit Lint (hexaTuneWeb) +>>>>>>> Stashed changes runs-on: ubuntu-latest steps: - name: Checkout (full history) From d166f21500ec3ce448a37a3ec1a51f5ce67081ff Mon Sep 17 00:00:00 2001 From: Husamettin ARABACI Date: Wed, 4 Mar 2026 09:43:35 +0300 Subject: [PATCH 15/16] feat(firmware): migrate AT/SysEx/USB-MIDI to hexaTuneProto shared library (#39) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace hand-written AT command parser, SysEx framing, and USB MIDI packetization with hexa-tune-proto and hexa-tune-proto-embedded crates to establish a unified protocol layer shared between embedded firmware and Flutter mobile app. Changes: - Add hexa-tune-proto and hexa-tune-proto-embedded path dependencies - Remove unused sha2 and base64 dependencies - Replace Error enum with FirmwareError wrapper (Proto/Hexa/firmware) - Replace AtCommand-based Msg enum with typed variants (RgbSet, FreqSet, etc.) - Delete sysex/mod.rs — replaced by hexa_tune_proto::sysex - Delete at/command.rs — replaced by hexa_tune_proto::at::parse/encode - Delete at/handler.rs — replaced by HexaCommand match dispatch - Delete handlers/reset_handler.rs — reset is now inline in at_task - Rewrite dispatcher.rs with library-based dispatch and encoding helpers - Rewrite at_task.rs with HexaCommand dispatch, no more static dispatcher - Rewrite usb_task.rs with library depacketize/unframe (RX) and frame/packetize (TX) - Rewrite dds_task.rs with typed messages and FreqStep instead of AtCommand - Simplify all handlers to accept typed parameters instead of string parsing - Simplify rgb_task.rs — no more parameter parsing from AtCommand - Update ad985x.rs error type from Error to FirmwareError - Update main.rs — remove mod sysex, remove AtDispatcher static cell Stats: 23 files changed, +423 -810 lines (net -387) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- firmware/Cargo.lock | 37 ++-- firmware/Cargo.toml | 4 +- firmware/src/at/at_task.rs | 86 ++++++-- firmware/src/at/command.rs | 162 -------------- firmware/src/at/dispatcher.rs | 112 +++++----- firmware/src/at/handler.rs | 63 ------ firmware/src/at/handlers/freq_handler.rs | 30 +-- firmware/src/at/handlers/fwupdate_handler.rs | 16 +- firmware/src/at/handlers/mod.rs | 2 - firmware/src/at/handlers/operation_handler.rs | 43 +--- firmware/src/at/handlers/reset_handler.rs | 16 -- firmware/src/at/handlers/setrgb_handler.rs | 17 +- firmware/src/at/handlers/version_handler.rs | 39 +--- firmware/src/at/mod.rs | 4 - firmware/src/channel/message_type.rs | 20 +- firmware/src/dds/ad985x.rs | 14 +- firmware/src/dds/dds_task.rs | 204 +++++++----------- firmware/src/dds/dds_type.rs | 31 +-- firmware/src/error/mod.rs | 64 +++--- firmware/src/main.rs | 5 +- firmware/src/rgb/rgb_task.rs | 39 +--- firmware/src/sysex/mod.rs | 96 --------- firmware/src/usb/usb_task.rs | 129 ++++++++--- 23 files changed, 423 insertions(+), 810 deletions(-) delete mode 100644 firmware/src/at/command.rs delete mode 100644 firmware/src/at/handler.rs delete mode 100644 firmware/src/at/handlers/reset_handler.rs delete mode 100644 firmware/src/sysex/mod.rs diff --git a/firmware/Cargo.lock b/firmware/Cargo.lock index 34559ee..317232d 100644 --- a/firmware/Cargo.lock +++ b/firmware/Cargo.lock @@ -68,12 +68,6 @@ dependencies = [ "rustc_version", ] -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - [[package]] name = "bit-set" version = "0.8.0" @@ -750,11 +744,26 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "hexa-tune-proto" +version = "0.1.0" +dependencies = [ + "defmt", +] + +[[package]] +name = "hexa-tune-proto-embedded" +version = "0.1.0" +dependencies = [ + "defmt", + "heapless 0.9.1", + "hexa-tune-proto", +] + [[package]] name = "hexagenmini" version = "0.1.0" dependencies = [ - "base64", "cortex-m", "cortex-m-rt", "critical-section", @@ -770,9 +779,10 @@ dependencies = [ "embassy-usb", "embedded-storage", "heapless 0.9.1", + "hexa-tune-proto", + "hexa-tune-proto-embedded", "panic-probe", "portable-atomic", - "sha2", "smart-leds", "static_cell", ] @@ -1253,17 +1263,6 @@ dependencies = [ "syn 2.0.106", ] -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - [[package]] name = "sha2-const-stable" version = "0.1.0" diff --git a/firmware/Cargo.toml b/firmware/Cargo.toml index c192c6f..e2878ad 100644 --- a/firmware/Cargo.toml +++ b/firmware/Cargo.toml @@ -61,8 +61,8 @@ heapless = "0.9.1" static_cell = "2.1" portable-atomic = { version = "1.5", features = ["critical-section"] } -sha2 = { version = "0.10", default-features = false } -base64 = { version = "0.22.1", default-features = false } +hexa-tune-proto = { path = "../../hexaTuneProto/crates/hexa-tune-proto", default-features = false } +hexa-tune-proto-embedded = { path = "../../hexaTuneProto/crates/hexa-tune-proto-embedded", default-features = false, features = ["defmt"] } [profile.release] # Enable generation of debug symbols even on release builds diff --git a/firmware/src/at/at_task.rs b/firmware/src/at/at_task.rs index 5580a73..5da51af 100644 --- a/firmware/src/at/at_task.rs +++ b/firmware/src/at/at_task.rs @@ -1,53 +1,52 @@ // SPDX-FileCopyrightText: 2025 hexaTune LLC // SPDX-License-Identifier: MIT +use cortex_m::peripheral::SCB; use defmt::{error, info}; use embassy_executor::Spawner; use heapless::String; use {defmt_rtt as _, panic_probe as _}; +use hexa_tune_proto_embedded::command::HexaCommand; +use hexa_tune_proto_embedded::HexaError; + use crate::AT_CH; use crate::USB_CH; use crate::at::*; use crate::channel::*; +use crate::error::FirmwareError; use crate::hexa_config::*; #[embassy_executor::task] -pub async fn at_task(dispatcher: &'static AtDispatcher, spawner: Spawner) { +pub async fn at_task(spawner: Spawner) { info!("Starting AT task"); let mut last_operation_status: String<64> = String::new(); loop { match AT_CH.receive().await { Msg::AtRxLine(line) => { - if let Some(e) = dispatcher.dispatch(spawner, &line) { - let compiled = compile_at_error(get_empty_id(), e); + if let Err((id, e)) = dispatch_and_spawn(spawner, line.as_bytes()) { + let compiled = encode_error_response(id, &e); error!("Dispatch error: {:?}", compiled.as_str()); USB_CH.send(Msg::UsbTxLine(compiled)).await; } } - Msg::AtCmdOutput(at_command) => { - let compiled = at_command.compile(); - info!("Sending output: {}", compiled.as_str()); - USB_CH.send(Msg::UsbTxLine(compiled)).await; + Msg::AtCmdResponse(line) => { + info!("Sending response: {}", line.as_str()); + USB_CH.send(Msg::UsbTxLine(line)).await; } Msg::Done(msg_id) => { - let compiled = compile_at_done(msg_id); + let compiled = encode_done(msg_id); info!("Sending done: {}", compiled.as_str()); USB_CH.send(Msg::UsbTxLine(compiled)).await; } Msg::Err(msg_id, e) => { - let compiled = compile_at_error(msg_id, e); + let compiled = encode_error_response(msg_id, &e); error!("Sending error: {}", compiled.as_str()); USB_CH.send(Msg::UsbTxLine(compiled)).await; } Msg::SetDdsAvailable(status) => { set_dds_available(status); } - Msg::Completed(at_command) => { - let compiled = compile_at_completed(at_command); - info!("Sending done: {}", compiled.as_str()); - USB_CH.send(Msg::UsbTxLine(compiled)).await; - } Msg::SetOperationStatus(status) => { last_operation_status = status; } @@ -60,3 +59,62 @@ pub async fn at_task(dispatcher: &'static AtDispatcher, spawner: Spawner) { } } } + +fn dispatch_and_spawn(spawner: Spawner, payload: &[u8]) -> Result<(), (u32, FirmwareError)> { + let cmd = dispatch_at_payload(payload).map_err(|e| (0u32, e))?; + let id = command_id(&cmd); + + match cmd { + HexaCommand::VersionQuery => { + info!("Dispatching VERSION query"); + spawner.spawn(version_task()).ok(); + } + HexaCommand::SetRgb { id, r, g, b } => { + info!("Dispatching SETRGB command"); + spawner.spawn(setrgb_task(id, r, g, b)).ok(); + } + HexaCommand::Reset { .. } => { + info!("Dispatching RESET command"); + SCB::sys_reset(); + } + HexaCommand::FwUpdate { .. } => { + info!("Dispatching FWUPDATE command"); + spawner.spawn(fwupdate_task()).ok(); + } + HexaCommand::Freq { id, freq, time_ms } => { + if !is_dds_available() { + error!("DDS busy, cannot set FREQ"); + return Err((id, FirmwareError::Hexa(HexaError::DdsBusy))); + } + info!("Dispatching FREQ command"); + spawner.spawn(freq_task(id, freq, time_ms)).ok(); + } + HexaCommand::Operation { id, sub } => { + if !is_dds_available() { + error!("DDS busy, cannot set OPERATION"); + return Err((id, FirmwareError::Hexa(HexaError::DdsBusy))); + } + info!("Dispatching OPERATION command"); + spawner.spawn(operation_task(id, sub)).ok(); + } + HexaCommand::OperationQuery => { + info!("Dispatching OPERATION query"); + spawner.spawn(operation_status_task()).ok(); + } + _ => { + return Err((id, FirmwareError::Hexa(HexaError::UnknownCommand))); + } + } + Ok(()) +} + +fn command_id(cmd: &HexaCommand) -> u32 { + match cmd { + HexaCommand::SetRgb { id, .. } + | HexaCommand::Reset { id } + | HexaCommand::FwUpdate { id } + | HexaCommand::Freq { id, .. } + | HexaCommand::Operation { id, .. } => *id, + _ => 0, + } +} diff --git a/firmware/src/at/command.rs b/firmware/src/at/command.rs deleted file mode 100644 index 6318713..0000000 --- a/firmware/src/at/command.rs +++ /dev/null @@ -1,162 +0,0 @@ -// SPDX-FileCopyrightText: 2025 hexaTune LLC -// SPDX-License-Identifier: MIT - -use defmt::info; -use heapless::{String, Vec}; - -use crate::error::Error; - -#[derive(Debug, Clone)] -pub struct AtCommand { - pub id: String<16>, - pub name: String<16>, - pub params: Vec, 8>, - pub is_query: bool, -} - -impl AtCommand { - pub fn compile(&self) -> String<64> { - let mut s = String::<64>::new(); - s.push_str("AT+").unwrap(); - s.push_str(self.name.as_str()).unwrap(); - if self.is_query { - s.push('?').unwrap(); - } else { - s.push('=').unwrap(); - s.push_str(self.id.as_str()).unwrap(); - if !self.params.is_empty() { - for p in self.params.iter() { - s.push('#').unwrap(); - s.push_str(p.as_str()).unwrap(); - } - } - } - s - } -} - -pub fn parse(input: &str) -> Result { - info!("Received input: {}", input); - let input = input.trim(); - if !input.starts_with("AT+") { - return Err(Error::InvalidCommand); - } - let cmd = &input[3..]; - if let Some(eq_pos) = cmd.find('=') { - let (name, param_str) = cmd.split_at(eq_pos); - let mut id: String<16> = { - let mut id = String::<16>::new(); - id.push_str("0").map_err(|_| Error::InvalidUtf8)?; - id - }; - let param_str = ¶m_str[1..]; - let mut params = Vec::, 8>::new(); - for (i, p) in param_str - .split('#') - .map(|p| p.trim()) - .filter(|p| !p.is_empty()) - .enumerate() - { - info!("Parsing param: {}", p); - let mut s_hl = String::<16>::new(); - s_hl.push_str(p).map_err(|_| Error::InvalidUtf8)?; - if i == 0 { - id = s_hl; - } else { - params.push(s_hl).map_err(|_| Error::ParamCount)?; - } - } - Ok(AtCommand { - id, - name: { - let mut n = String::<16>::new(); - n.push_str(name).map_err(|_| Error::InvalidUtf8)?; - n - }, - params, - is_query: false, - }) - } else if let Some(name) = cmd.strip_suffix('?') { - Ok(AtCommand { - id: { - let mut id = String::<16>::new(); - id.push_str("0").map_err(|_| Error::InvalidUtf8)?; - id - }, - name: { - let mut n = String::<16>::new(); - n.push_str(name).map_err(|_| Error::InvalidUtf8)?; - n - }, - params: Vec::, 8>::new(), - is_query: true, - }) - } else { - Ok(AtCommand { - id: { - let mut id = String::<16>::new(); - id.push_str("0").map_err(|_| Error::InvalidUtf8)?; - id - }, - name: { - let mut n = String::<16>::new(); - n.push_str(cmd).map_err(|_| Error::InvalidUtf8)?; - n - }, - params: Vec::, 8>::new(), - is_query: false, - }) - } -} - -pub fn get_empty_id() -> String<16> { - let mut id = String::<16>::new(); - id.push_str("0").unwrap(); - id -} - -pub fn compile_at_done(id: String<16>) -> String<64> { - let at_cmd = AtCommand { - id, - name: { - let mut n = String::<16>::new(); - n.push_str("DONE").unwrap(); - n - }, - params: Vec::, 8>::new(), - is_query: false, - }; - at_cmd.compile() -} - -pub fn compile_at_completed(at_command: AtCommand) -> String<64> { - let mut at_cmd = AtCommand { - id: at_command.id, - name: at_command.name, - params: at_command.params, - is_query: false, - }; - let completede_param = String::<16>::try_from("COMPLETED").unwrap(); - at_cmd.params.push(completede_param).ok(); - at_cmd.compile() -} - -pub fn compile_at_error(id: String<16>, e: Error) -> String<64> { - let at_cmd = AtCommand { - id, - name: { - let mut n = String::<16>::new(); - n.push_str("ERROR").unwrap(); - n - }, - params: { - let mut p = Vec::, 8>::new(); - let mut code = String::<16>::new(); - code.push_str(e.code()).unwrap(); - p.push(code).unwrap(); - p - }, - is_query: false, - }; - at_cmd.compile() -} diff --git a/firmware/src/at/dispatcher.rs b/firmware/src/at/dispatcher.rs index 935341e..20ef041 100644 --- a/firmware/src/at/dispatcher.rs +++ b/firmware/src/at/dispatcher.rs @@ -1,60 +1,76 @@ // SPDX-FileCopyrightText: 2025 hexaTune LLC // SPDX-License-Identifier: MIT -use defmt::{error, info}; -use embassy_executor::Spawner; -use heapless::{LinearMap, String}; +use hexa_tune_proto::at::{self, AtOp}; +use hexa_tune_proto_embedded::command::HexaCommand; +use hexa_tune_proto_embedded::dispatch::resolve; -use crate::at::handler::{AtHandler, Handler, register_all}; -use crate::at::parse; -use crate::error::Error; +use crate::channel::MsgString; +use crate::error::FirmwareError; -pub struct AtDispatcher { - handlers: LinearMap, Handler, 8>, +/// Parse an AT payload and resolve it to a typed HexaCommand. +pub fn dispatch_at_payload(payload: &[u8]) -> Result { + let msg = at::parse(payload).map_err(FirmwareError::Proto)?; + resolve(&msg).map_err(FirmwareError::Hexa) } -impl AtDispatcher { - pub fn new() -> Self { - Self { - handlers: register_all(), +/// Encode an AT response (name=id#params...) into a MsgString. +pub fn encode_response(name: &[u8], id: u32, params: &[&[u8]]) -> MsgString { + let mut buf = [0u8; 64]; + if let Ok(n) = at::encode(name, id, AtOp::Response, params, &mut buf) { + if let Ok(s) = core::str::from_utf8(&buf[..n]) { + if let Ok(line) = MsgString::try_from(s) { + return line; + } } } + MsgString::new() +} - pub fn dispatch(&self, spawner: Spawner, input: &str) -> Option { - match parse(input) { - Ok(cmd) => match self.handlers.get(&cmd.name) { - Some(handler) => match handler { - Handler::Version(h) => { - info!("Dispatching VERSION command"); - h.handle(spawner, cmd) - } - Handler::SetRgb(h) => { - info!("Dispatching SETRGB command"); - h.handle(spawner, cmd) - } - Handler::Reset(h) => { - info!("Dispatching RESET command"); - h.handle(spawner, cmd) - } - Handler::FwUpdate(h) => { - info!("Dispatching FWUPDATE command"); - h.handle(spawner, cmd) - } - Handler::Freq(h) => { - info!("Dispatching FREQ command"); - h.handle(spawner, cmd) - } - Handler::Operation(h) => { - info!("Dispatching OPERATION command"); - h.handle(spawner, cmd) - } - }, - None => Some(Error::UnknownCommand), - }, - Err(e) => { - error!("Failed to parse command: {}", &e.code()); - Some(e) - } - } +/// Encode an AT+DONE=id response. +pub fn encode_done(id: u32) -> MsgString { + encode_response(b"DONE", id, &[]) +} + +/// Encode an AT+ERROR=id#code response using u8 error code. +pub fn encode_error_response(id: u32, e: &FirmwareError) -> MsgString { + let code = e.error_code(); + let mut code_buf = [0u8; 3]; + let code_len = u8_to_ascii(code, &mut code_buf); + encode_response(b"ERROR", id, &[&code_buf[..code_len]]) +} + +/// Convert a u32 value to ASCII decimal bytes in a buffer. +pub fn u32_to_ascii_buf(val: u32, buf: &mut [u8; 10]) -> usize { + if val == 0 { + buf[0] = b'0'; + return 1; + } + let mut tmp = [0u8; 10]; + let mut n = val; + let mut i = 10usize; + while n > 0 { + i -= 1; + tmp[i] = b'0' + (n % 10) as u8; + n /= 10; + } + let len = 10 - i; + buf[..len].copy_from_slice(&tmp[i..]); + len +} + +fn u8_to_ascii(val: u8, buf: &mut [u8; 3]) -> usize { + if val >= 100 { + buf[0] = b'0' + val / 100; + buf[1] = b'0' + (val / 10) % 10; + buf[2] = b'0' + val % 10; + 3 + } else if val >= 10 { + buf[0] = b'0' + val / 10; + buf[1] = b'0' + val % 10; + 2 + } else { + buf[0] = b'0' + val; + 1 } } diff --git a/firmware/src/at/handler.rs b/firmware/src/at/handler.rs deleted file mode 100644 index 8567778..0000000 --- a/firmware/src/at/handler.rs +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-FileCopyrightText: 2025 hexaTune LLC -// SPDX-License-Identifier: MIT - -use embassy_executor::Spawner; -use heapless::{LinearMap, String}; - -use crate::at::*; -use crate::error::Error; - -pub trait AtHandler { - fn handle(&self, spawner: Spawner, at_command: AtCommand) -> Option; -} - -pub enum Handler { - Version(VersionHandler), - SetRgb(SetRgbHandler), - Reset(ResetHandler), - FwUpdate(FwUpdateHandler), - Freq(FreqHandler), - Operation(OperationHandler), -} - -pub fn register_all() -> LinearMap, Handler, 8> { - let mut map: LinearMap, Handler, 8> = LinearMap::new(); - - map.insert( - String::<16>::try_from("VERSION").unwrap(), - Handler::Version(VersionHandler), - ) - .ok(); - - map.insert( - String::<16>::try_from("SETRGB").unwrap(), - Handler::SetRgb(SetRgbHandler), - ) - .ok(); - - map.insert( - String::<16>::try_from("RESET").unwrap(), - Handler::Reset(ResetHandler), - ) - .ok(); - - map.insert( - String::<16>::try_from("FWUPDATE").unwrap(), - Handler::FwUpdate(FwUpdateHandler), - ) - .ok(); - - map.insert( - String::<16>::try_from("FREQ").unwrap(), - Handler::Freq(FreqHandler), - ) - .ok(); - - map.insert( - String::<16>::try_from("OPERATION").unwrap(), - Handler::Operation(OperationHandler), - ) - .ok(); - - map -} diff --git a/firmware/src/at/handlers/freq_handler.rs b/firmware/src/at/handlers/freq_handler.rs index 7d5ea45..9e770df 100644 --- a/firmware/src/at/handlers/freq_handler.rs +++ b/firmware/src/at/handlers/freq_handler.rs @@ -1,36 +1,14 @@ // SPDX-FileCopyrightText: 2025 hexaTune LLC // SPDX-License-Identifier: MIT -use defmt::{error, info}; -use embassy_executor::Spawner; +use defmt::info; use crate::DDS_CH; -use crate::at::{AtCommand, AtHandler}; use crate::channel::*; -use crate::error::Error; -use crate::hexa_config::*; - -pub struct FreqHandler; - -impl AtHandler for FreqHandler { - fn handle(&self, spawner: Spawner, at_command: AtCommand) -> Option { - if is_dds_available() { - info!("Setting FREQ: {:?}", at_command.id.as_str()); - let _ = spawner.spawn(freq_task(at_command)); - None - } else { - error!("DDS busy, cannot set FREQ ({})", at_command.id.as_str()); - Some(Error::DdsBusy) - } - } -} #[embassy_executor::task] -async fn freq_task(at_command: AtCommand) { - info!( - "Sending FREQ command to DDS task: {}", - at_command.id.as_str() - ); - DDS_CH.send(Msg::FreqWithValue(at_command)).await; +pub async fn freq_task(id: u32, freq: u32, time_ms: u32) { + info!("Sending FREQ command to DDS task"); + DDS_CH.send(Msg::FreqSet { id, freq, time_ms }).await; info!("FREQ command sent to DDS task"); } diff --git a/firmware/src/at/handlers/fwupdate_handler.rs b/firmware/src/at/handlers/fwupdate_handler.rs index 4effdae..0ab7c87 100644 --- a/firmware/src/at/handlers/fwupdate_handler.rs +++ b/firmware/src/at/handlers/fwupdate_handler.rs @@ -2,23 +2,9 @@ // SPDX-License-Identifier: MIT use defmt::info; -use embassy_executor::Spawner; - -use crate::at::*; -use crate::error::Error; - -pub struct FwUpdateHandler; - -impl AtHandler for FwUpdateHandler { - fn handle(&self, spawner: Spawner, _at_command: AtCommand) -> Option { - info!("Firmware update requested - spawning BOOTSEL task"); - let _ = spawner.spawn(fwupdate_task()); - None - } -} #[embassy_executor::task] -async fn fwupdate_task() { +pub async fn fwupdate_task() { info!("Entering BOOTSEL mode for firmware update"); embassy_time::Timer::after_millis(100).await; embassy_rp::rom_data::reset_to_usb_boot(0, 0); diff --git a/firmware/src/at/handlers/mod.rs b/firmware/src/at/handlers/mod.rs index 7c4329a..239d6d4 100644 --- a/firmware/src/at/handlers/mod.rs +++ b/firmware/src/at/handlers/mod.rs @@ -7,8 +7,6 @@ mod setrgb_handler; pub use setrgb_handler::*; mod fwupdate_handler; pub use fwupdate_handler::*; -mod reset_handler; -pub use reset_handler::*; mod freq_handler; pub use freq_handler::*; mod operation_handler; diff --git a/firmware/src/at/handlers/operation_handler.rs b/firmware/src/at/handlers/operation_handler.rs index 564dcc4..cc4f6c8 100644 --- a/firmware/src/at/handlers/operation_handler.rs +++ b/firmware/src/at/handlers/operation_handler.rs @@ -1,52 +1,23 @@ // SPDX-FileCopyrightText: 2025 hexaTune LLC // SPDX-License-Identifier: MIT -use defmt::{error, info}; -use embassy_executor::Spawner; +use defmt::info; + +use hexa_tune_proto_embedded::command::OperationSub; use crate::AT_CH; use crate::DDS_CH; -use crate::at::{AtCommand, AtHandler}; use crate::channel::*; -use crate::error::Error; -use crate::hexa_config::*; - -pub struct OperationHandler; - -impl AtHandler for OperationHandler { - fn handle(&self, spawner: Spawner, at_command: AtCommand) -> Option { - if at_command.is_query { - let _ = spawner.spawn(operation_status_task()); - None - } else { - info!("Handling OPERATION command"); - if is_dds_available() { - info!("Setting OPERATION: {:?}", at_command.id.as_str()); - let _ = spawner.spawn(operation_task(at_command)); - None - } else { - error!( - "DDS busy, cannot set OPERATION ({})", - at_command.id.as_str() - ); - Some(Error::DdsBusy) - } - } - } -} #[embassy_executor::task] -async fn operation_task(at_command: AtCommand) { - info!( - "Sending OPERATION command to DDS task: {}", - at_command.id.as_str() - ); - DDS_CH.send(Msg::Operation(at_command)).await; +pub async fn operation_task(id: u32, sub: OperationSub) { + info!("Sending OPERATION command to DDS task"); + DDS_CH.send(Msg::OperationCmd { id, sub }).await; info!("OPERATION command sent to DDS task"); } #[embassy_executor::task] -async fn operation_status_task() { +pub async fn operation_status_task() { info!("Requesting OPERATION status"); AT_CH.send(Msg::GetOperationStatus).await; } diff --git a/firmware/src/at/handlers/reset_handler.rs b/firmware/src/at/handlers/reset_handler.rs deleted file mode 100644 index f3785fc..0000000 --- a/firmware/src/at/handlers/reset_handler.rs +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-FileCopyrightText: 2025 hexaTune LLC -// SPDX-License-Identifier: MIT - -use cortex_m::peripheral::SCB; -use embassy_executor::Spawner; - -use crate::at::*; -use crate::error::Error; - -pub struct ResetHandler; - -impl AtHandler for ResetHandler { - fn handle(&self, _spawner: Spawner, _at_command: AtCommand) -> Option { - SCB::sys_reset(); - } -} diff --git a/firmware/src/at/handlers/setrgb_handler.rs b/firmware/src/at/handlers/setrgb_handler.rs index f6413f8..bb2f83a 100644 --- a/firmware/src/at/handlers/setrgb_handler.rs +++ b/firmware/src/at/handlers/setrgb_handler.rs @@ -1,23 +1,10 @@ // SPDX-FileCopyrightText: 2025 hexaTune LLC // SPDX-License-Identifier: MIT -use embassy_executor::Spawner; - use crate::RGB_CH; -use crate::at::{AtCommand, AtHandler}; use crate::channel::*; -use crate::error::Error; - -pub struct SetRgbHandler; - -impl AtHandler for SetRgbHandler { - fn handle(&self, spawner: Spawner, at_command: AtCommand) -> Option { - let _ = spawner.spawn(setrgb_task(at_command)); - None - } -} #[embassy_executor::task] -async fn setrgb_task(at_command: AtCommand) { - RGB_CH.send(Msg::RgbWithValue(at_command)).await; +pub async fn setrgb_task(id: u32, r: u8, g: u8, b: u8) { + RGB_CH.send(Msg::RgbSet { id, r, g, b }).await; } diff --git a/firmware/src/at/handlers/version_handler.rs b/firmware/src/at/handlers/version_handler.rs index 4a2849b..b024aa3 100644 --- a/firmware/src/at/handlers/version_handler.rs +++ b/firmware/src/at/handlers/version_handler.rs @@ -1,44 +1,13 @@ // SPDX-FileCopyrightText: 2025 hexaTune LLC // SPDX-License-Identifier: MIT -use defmt::error; -use embassy_executor::Spawner; -use heapless::{String, Vec}; - use crate::AT_CH; -use crate::at::*; +use crate::at::encode_response; use crate::channel::*; -use crate::error::Error; use crate::hexa_config::CONF_VERSION; -pub struct VersionHandler; - -impl AtHandler for VersionHandler { - fn handle(&self, spawner: Spawner, at_command: AtCommand) -> Option { - if at_command.is_query { - let _ = spawner.spawn(version_task()); - None - } else { - error!("VERSION command is a query only"); - Some(Error::NotAQuery) - } - } -} - #[embassy_executor::task] -async fn version_task() { - let mut name = String::<16>::new(); - name.push_str("VERSION").unwrap(); - - let mut params = Vec::, 8>::new(); - let version_param = String::<16>::try_from(CONF_VERSION).unwrap(); - params.push(version_param).ok(); - - let at_command = AtCommand { - id: get_empty_id(), - name, - params, - is_query: false, - }; - AT_CH.send(Msg::AtCmdOutput(at_command)).await; +pub async fn version_task() { + let line = encode_response(b"VERSION", 0, &[CONF_VERSION.as_bytes()]); + AT_CH.send(Msg::AtCmdResponse(line)).await; } diff --git a/firmware/src/at/mod.rs b/firmware/src/at/mod.rs index 902ff41..d44969d 100644 --- a/firmware/src/at/mod.rs +++ b/firmware/src/at/mod.rs @@ -1,12 +1,8 @@ // SPDX-FileCopyrightText: 2025 hexaTune LLC // SPDX-License-Identifier: MIT -mod command; -pub use command::*; mod dispatcher; pub use dispatcher::*; -mod handler; -pub use handler::*; mod at_task; pub use at_task::*; mod handlers; diff --git a/firmware/src/channel/message_type.rs b/firmware/src/channel/message_type.rs index b4b42ff..2fca2a9 100644 --- a/firmware/src/channel/message_type.rs +++ b/firmware/src/channel/message_type.rs @@ -3,24 +3,22 @@ use heapless::String; -use crate::at::AtCommand; -use crate::error::Error; +use crate::error::FirmwareError; +use hexa_tune_proto_embedded::command::OperationSub; -pub type MsgId = String<16>; +pub type MsgId = u32; pub type MsgString = String<64>; -pub type IsDdsAvailable = bool; pub enum Msg { AtRxLine(MsgString), - AtCmdOutput(AtCommand), + AtCmdResponse(MsgString), Done(MsgId), - Completed(AtCommand), - Err(MsgId, Error), + Err(MsgId, FirmwareError), UsbTxLine(MsgString), - RgbWithValue(AtCommand), - FreqWithValue(AtCommand), - SetDdsAvailable(IsDdsAvailable), + RgbSet { id: u32, r: u8, g: u8, b: u8 }, + FreqSet { id: u32, freq: u32, time_ms: u32 }, + SetDdsAvailable(bool), SetOperationStatus(MsgString), GetOperationStatus, - Operation(AtCommand), + OperationCmd { id: u32, sub: OperationSub }, } diff --git a/firmware/src/dds/ad985x.rs b/firmware/src/dds/ad985x.rs index e1c4b98..90b798b 100644 --- a/firmware/src/dds/ad985x.rs +++ b/firmware/src/dds/ad985x.rs @@ -5,7 +5,7 @@ use defmt::*; use embassy_rp::gpio::Output; use embassy_time::{Duration, Timer}; -use crate::error::Error; +use crate::error::FirmwareError; const CTRL_PWRDOWN: u8 = 1 << 7; const CTRL_SLEEP: u8 = 1 << 6; @@ -76,7 +76,7 @@ impl Ad985x { ((num + den / 2) / den) as u32 } - pub async fn reset(&mut self) -> Option { + pub async fn reset(&mut self) -> Option { self.rst.set_high(); Timer::after(Duration::from_micros(5)).await; self.rst.set_low(); @@ -93,23 +93,23 @@ impl Ad985x { None } - /*pub async fn sleep(&mut self) -> Option { + /*pub async fn sleep(&mut self) -> Option { self.write_ftw_ctrl(0, self.ctrl_base | CTRL_SLEEP).await; None }*/ - pub async fn down(&mut self) -> Option { + pub async fn down(&mut self) -> Option { self.write_ftw_ctrl(0, self.ctrl_base | CTRL_PWRDOWN).await; None } - pub async fn up(&mut self) -> Option { + pub async fn up(&mut self) -> Option { self.write_ftw_ctrl(0, self.ctrl_base & !(CTRL_SLEEP | CTRL_PWRDOWN)) .await; None } - pub async fn set_freq(&mut self, freq_hz: u32, dwell_ms: u32) -> Option { + pub async fn set_freq(&mut self, freq_hz: u32, dwell_ms: u32) -> Option { if let Some(e) = self.down().await { return Some(e); } @@ -135,7 +135,7 @@ impl Ad985x { None } - async fn set_freq_immediate(&mut self, freq_hz: u32) -> Option { + async fn set_freq_immediate(&mut self, freq_hz: u32) -> Option { let ftw = self.hz_to_ftw(freq_hz); self.write_ftw_ctrl(ftw, self.ctrl_base | CTRL_PHASE0).await; None diff --git a/firmware/src/dds/dds_task.rs b/firmware/src/dds/dds_task.rs index a2886d2..7134e5c 100644 --- a/firmware/src/dds/dds_task.rs +++ b/firmware/src/dds/dds_task.rs @@ -6,13 +6,14 @@ use defmt::*; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; use embassy_sync::mutex::Mutex; -use heapless::{String, Vec}; use {defmt_rtt as _, panic_probe as _}; -use crate::at::*; +use hexa_tune_proto_embedded::command::OperationSub; + +use crate::at::{encode_error_response, encode_response, u32_to_ascii_buf}; use crate::channel::*; use crate::dds::*; -use crate::error::Error; +use crate::error::FirmwareError; use crate::{AT_CH, DDS_CH}; static OPERATION: Mutex> = Mutex::new(RefCell::new(Operation::new())); @@ -22,136 +23,86 @@ pub async fn dds_task(mut ad985x: Ad985x) { info!("Starting DDS task"); loop { match DDS_CH.receive().await { - Msg::Operation(at_command) => { - info!( - "Received OPERATION command in DDS task: {}", - at_command.id.as_str() - ); - info!("Parsing OPERATION command parameters"); - if at_command.params.len() != 1 { - error!( - "OPERATION command requires 1 parameters, got {}", - at_command.params.len() - ); - AT_CH.send(Msg::Err(at_command.id, Error::ParamCount)).await; - continue; - } - info!("Parameters parsed successfully"); - info!("Extracting command_type values"); - let command_type = match at_command.params[0].parse::>() { - Ok(v) => v, - Err(_) => { - error!("Invalid command_type value: {}", at_command.id.as_str()); - AT_CH.send(Msg::Err(at_command.id, Error::ParamValue)).await; - continue; - } - }; + Msg::OperationCmd { id, sub } => { + info!("Received OPERATION command in DDS task: {}", id); - match command_type.as_str() { - "PREPARE" => { + match sub { + OperationSub::Prepare => { info!("Preparing DDS operation"); - // Reset the operation by replacing the inner value let operation = OPERATION.lock().await; *operation.borrow_mut() = Operation::new(); - { let mut guard = operation.borrow_mut(); - guard.set_id(at_command.id.clone()); + guard.set_id(id); } drop(operation); info!("DDS operation prepared"); - AT_CH.send(Msg::Completed(at_command.clone())).await; + let completed = + encode_response(b"OPERATION", id, &[b"PREPARE", b"COMPLETED"]); + AT_CH.send(Msg::AtCmdResponse(completed.clone())).await; info!("Completed sent for PREPARE command"); - let compiled_status = compile_at_completed(at_command.clone()); - AT_CH.send(Msg::SetOperationStatus(compiled_status)).await; + AT_CH.send(Msg::SetOperationStatus(completed)).await; } - "GENERATE" => { + OperationSub::Generate => { info!("Starting DDS operation"); - let mut result: Option = None; + let mut result: Option = None; info!("Setting Device Available to false"); AT_CH.send(Msg::SetDdsAvailable(false)).await; info!("Set Device Available to false"); - let compiled_status = compile_at_completed(at_command.clone()); - AT_CH.send(Msg::SetOperationStatus(compiled_status)).await; - - let steps = { + let gen_completed = + encode_response(b"OPERATION", id, &[b"GENERATE", b"COMPLETED"]); + AT_CH + .send(Msg::SetOperationStatus(gen_completed.clone())) + .await; + + // Clone steps out of the mutex + let step_count; + let mut step_ids = [0u32; 64]; + let mut step_freqs = [0u32; 64]; + let mut step_times = [0u32; 64]; + { let operation = OPERATION.lock().await; let guard = operation.borrow(); - guard.get_steps().clone() - }; - - let mut at_command_status = at_command.clone(); - at_command_status.params.clear(); - - for at_command_step in steps.iter() { - let mut params = Vec::, 8>::new(); - let generating_param = String::<16>::try_from("GENERATING").unwrap(); - params.push(generating_param).ok(); - - at_command_status.params = params; - - info!("Parsing FREQ command parameters"); - if at_command_step.params.len() != 2 { - error!( - "FREQ command requires 2 parameters, got {}", - at_command_step.params.len() - ); - result = Some(Error::ParamCount); - break; + let steps = guard.get_steps(); + step_count = steps.len(); + for (i, s) in steps.iter().enumerate() { + step_ids[i] = s.id; + step_freqs[i] = s.freq; + step_times[i] = s.time_ms; } - info!("Parameters parsed successfully"); - - info!("Extracting frequency and time_ms values"); - let freq = match at_command_step.params[0].parse::() { - Ok(v) => v, - Err(_) => { - error!( - "Invalid frequency value: {}", - at_command_step.id.as_str() - ); - result = Some(Error::ParamValue); - break; - } - }; - info!("Frequency value extracted: {}", freq); - let time_ms = match at_command_step.params[1].parse::() { - Ok(v) => v, - Err(_) => { - error!( - "Invalid time_ms value: {}", - at_command_step.id.as_str() - ); - result = Some(Error::ParamValue); - break; - } - }; - info!("time_ms value extracted: {}", time_ms); - - info!( - "Setting FREQ to ({}) over {} ms", - at_command_step.id.as_str(), - time_ms - ); + } - let step_id_param = - String::<16>::try_from(at_command_step.id.as_str()).unwrap(); - at_command_status.params.push(step_id_param).ok(); - let compiled_status = compile_at_completed(at_command_status.clone()); - AT_CH.send(Msg::SetOperationStatus(compiled_status)).await; + for i in 0..step_count { + let step_id = step_ids[i]; + let freq = step_freqs[i]; + let time_ms = step_times[i]; + + // Build status: AT+OPERATION=id#GENERATING#step_id#COMPLETED + let mut sid_buf = [0u8; 10]; + let sid_len = u32_to_ascii_buf(step_id, &mut sid_buf); + let status = encode_response( + b"OPERATION", + id, + &[b"GENERATING", &sid_buf[..sid_len], b"COMPLETED"], + ); + AT_CH.send(Msg::SetOperationStatus(status)).await; - info!("Starting frequency set..."); + info!("Setting FREQ to {} over {} ms", freq, time_ms); let err = ad985x.set_freq(freq, time_ms).await; info!("Frequency set complete."); if let Some(err) = err { - error!("Error setting FREQ: {:?}", err.code()); - result = Some(err); + error!("Error setting FREQ"); + result = Some(FirmwareError::Hexa( + hexa_tune_proto_embedded::HexaError::InvalidParam, + )); + let _ = err; break; } else { info!("FREQ set successfully"); @@ -163,46 +114,47 @@ pub async fn dds_task(mut ad985x: Ad985x) { info!("Set Device Available to true"); if let Some(err) = result { - error!("DDS operation failed: {:?}", err.code()); - AT_CH - .send(Msg::Err(at_command.id.clone(), err.clone())) - .await; + error!("DDS operation failed"); + AT_CH.send(Msg::Err(id, err)).await; - let compiled_status = compile_at_error(at_command.id.clone(), err); - AT_CH.send(Msg::SetOperationStatus(compiled_status)).await; + let error_status = encode_error_response(id, &err); + AT_CH.send(Msg::SetOperationStatus(error_status)).await; } else { - let compiled_status = compile_at_completed(at_command.clone()); - AT_CH.send(Msg::SetOperationStatus(compiled_status)).await; + AT_CH.send(Msg::SetOperationStatus(gen_completed)).await; } } - _ => { - error!("Unknown command_type value: {}", at_command.id.as_str()); - AT_CH.send(Msg::Err(at_command.id, Error::ParamValue)).await; - continue; - } } } - Msg::FreqWithValue(at_command) => { - info!( - "Received FREQ command in DDS task: {}", - at_command.id.as_str() - ); + Msg::FreqSet { id, freq, time_ms } => { + info!("Received FREQ command in DDS task: {}", id); - info!("Adding FREQ command to operation"); + info!("Adding FREQ step to operation"); let operation = OPERATION.lock().await; + let step = FreqStep { id, freq, time_ms }; let add_result = { let mut guard = operation.borrow_mut(); - guard.add_step(at_command.clone()) + guard.add_step(step) }; drop(operation); if let Err(e) = add_result { error!("Failed to add step: operation is full"); - AT_CH.send(Msg::Err(at_command.id, e)).await; + AT_CH.send(Msg::Err(id, e)).await; } else { - info!("FREQ command added to operation"); - AT_CH.send(Msg::Completed(at_command)).await; + info!("FREQ step added to operation"); + + // Build completed response: AT+FREQ=id#freq#time_ms#COMPLETED + let mut freq_buf = [0u8; 10]; + let freq_len = u32_to_ascii_buf(freq, &mut freq_buf); + let mut time_buf = [0u8; 10]; + let time_len = u32_to_ascii_buf(time_ms, &mut time_buf); + let completed = encode_response( + b"FREQ", + id, + &[&freq_buf[..freq_len], &time_buf[..time_len], b"COMPLETED"], + ); + AT_CH.send(Msg::AtCmdResponse(completed)).await; info!("Completed sent for FREQ command"); } } diff --git a/firmware/src/dds/dds_type.rs b/firmware/src/dds/dds_type.rs index b68532a..d247a4b 100644 --- a/firmware/src/dds/dds_type.rs +++ b/firmware/src/dds/dds_type.rs @@ -1,40 +1,45 @@ // SPDX-FileCopyrightText: 2025 hexaTune LLC // SPDX-License-Identifier: MIT -use heapless::{String, Vec}; +use heapless::Vec; -use crate::at::AtCommand; -use crate::error::Error; +use crate::error::FirmwareError; -pub type OperationId = String<16>; +pub struct FreqStep { + pub id: u32, + pub freq: u32, + pub time_ms: u32, +} pub struct Operation { - id: OperationId, - steps: Vec, + id: u32, + steps: Vec, } impl Operation { pub const fn new() -> Self { Self { - id: String::new(), + id: 0, steps: Vec::new(), } } #[allow(dead_code)] - pub fn get_id(&self) -> &OperationId { - &self.id + pub fn get_id(&self) -> u32 { + self.id } - pub fn set_id(&mut self, id: OperationId) { + pub fn set_id(&mut self, id: u32) { self.id = id; } - pub fn get_steps(&self) -> &heapless::Vec { + pub fn get_steps(&self) -> &Vec { &self.steps } - pub fn add_step(&mut self, step: AtCommand) -> Result<(), Error> { - self.steps.push(step).map_err(|_| Error::OperationStepsFull) + pub fn add_step(&mut self, step: FreqStep) -> Result<(), FirmwareError> { + self.steps + .push(step) + .map_err(|_| FirmwareError::OperationStepsFull) } } diff --git a/firmware/src/error/mod.rs b/firmware/src/error/mod.rs index 40c79a3..46e700f 100644 --- a/firmware/src/error/mod.rs +++ b/firmware/src/error/mod.rs @@ -1,34 +1,48 @@ // SPDX-FileCopyrightText: 2025 hexaTune LLC // SPDX-License-Identifier: MIT -#[derive(Debug, Clone)] -pub enum Error { - InvalidCommand, // "Not an AT command" - DdsBusy, // "DDS is busy" - InvalidUtf8, // "Invalid UTF-8 in param" - InvalidSysEx, // "Invalid SysEx" - InvalidDataLength, // "Invalid data length" - ParamCount, // "Param count" - ParamValue, // "Param value" - NotAQuery, // "Not a query" - UnknownCommand, // "Unknown command" - OperationStepsFull, // "Operation steps full" +pub use hexa_tune_proto::ProtoError; +pub use hexa_tune_proto_embedded::HexaError; + +#[derive(Debug, Clone, Copy)] +pub enum FirmwareError { + Proto(ProtoError), + Hexa(HexaError), + OperationStepsFull, +} + +impl From for FirmwareError { + fn from(e: ProtoError) -> Self { + Self::Proto(e) + } +} + +impl From for FirmwareError { + fn from(e: HexaError) -> Self { + Self::Hexa(e) + } } -impl Error { - pub fn code(&self) -> &'static str { +impl FirmwareError { + /// Returns a numeric error code (u8) for wire-format encoding. + pub fn error_code(&self) -> u8 { match self { - // Command errors - Error::InvalidCommand => "E001001", - Error::DdsBusy => "E001002", - Error::InvalidUtf8 => "E001003", - Error::InvalidSysEx => "E001004", - Error::InvalidDataLength => "E001005", - Error::ParamCount => "E001006", - Error::ParamValue => "E001007", - Error::NotAQuery => "E001008", - Error::UnknownCommand => "E001009", - Error::OperationStepsFull => "E001010", + FirmwareError::Proto(e) => e.code(), + FirmwareError::Hexa(e) => match e { + HexaError::Proto(pe) => pe.code(), + HexaError::UnknownCommand => 11, + HexaError::DdsBusy => 12, + HexaError::NotAQuery => 13, + HexaError::MissingParam => 14, + HexaError::InvalidParam => 15, + }, + FirmwareError::OperationStepsFull => 20, } } } + +impl defmt::Format for FirmwareError { + fn format(&self, f: defmt::Formatter) { + defmt::write!(f, "FirmwareError({})", self.error_code()); + } +} diff --git a/firmware/src/main.rs b/firmware/src/main.rs index 6f425bb..5dc62cd 100644 --- a/firmware/src/main.rs +++ b/firmware/src/main.rs @@ -16,7 +16,6 @@ mod dds; mod error; mod hexa_config; mod rgb; -mod sysex; mod usb; use crate::channel::*; @@ -51,8 +50,6 @@ async fn main(spawner: embassy_executor::Spawner) { //AT module info!("Initializing AT command dispatcher"); - static DISPATCHER: static_cell::StaticCell = static_cell::StaticCell::new(); - let dispatcher = DISPATCHER.init(at::AtDispatcher::new()); //Led module info!("Initializing RGB LED"); @@ -82,7 +79,7 @@ async fn main(spawner: embassy_executor::Spawner) { //Dummy Led let led = embassy_rp::gpio::Output::new(p.PIN_25, embassy_rp::gpio::Level::Low); - spawner.spawn(at::at_task(dispatcher, spawner)).unwrap(); + spawner.spawn(at::at_task(spawner)).unwrap(); spawner.spawn(rgb::rgb_task(rgb_led)).unwrap(); spawner.spawn(usb::dev_task(device)).unwrap(); spawner.spawn(usb::usb_io_task(midi_mutex)).unwrap(); diff --git a/firmware/src/rgb/rgb_task.rs b/firmware/src/rgb/rgb_task.rs index b3bce79..2d2c48f 100644 --- a/firmware/src/rgb/rgb_task.rs +++ b/firmware/src/rgb/rgb_task.rs @@ -7,53 +7,18 @@ use {defmt_rtt as _, panic_probe as _}; use crate::AT_CH; use crate::RGB_CH; use crate::channel::*; -use crate::error::Error; use crate::rgb::RgbLed; #[embassy_executor::task] pub async fn rgb_task(mut rgb_led: RgbLed) { info!("Starting RGB task"); loop { - if let Msg::RgbWithValue(at_command) = RGB_CH.receive().await { - if at_command.params.len() != 3 { - error!( - "SETRGB command {} requires 3 parameters, got {}", - at_command.id.as_str(), - at_command.params.len() - ); - AT_CH.send(Msg::Err(at_command.id, Error::ParamCount)).await; - return; - } - // check params are u8 - let r = match at_command.params[0].parse::() { - Ok(v) => v, - Err(_) => { - error!("Invalid R value: {}", at_command.id.as_str()); - AT_CH.send(Msg::Err(at_command.id, Error::ParamValue)).await; - return; - } - }; - let g = match at_command.params[1].parse::() { - Ok(v) => v, - Err(_) => { - error!("Invalid G value: {}", at_command.id.as_str()); - AT_CH.send(Msg::Err(at_command.id, Error::ParamValue)).await; - return; - } - }; - let b = match at_command.params[2].parse::() { - Ok(v) => v, - Err(_) => { - error!("Invalid B value: {}", at_command.id.as_str()); - AT_CH.send(Msg::Err(at_command.id, Error::ParamValue)).await; - return; - } - }; + if let Msg::RgbSet { id, r, g, b } = RGB_CH.receive().await { info!("Setting RGB to ({}, {}, {})", r, g, b); rgb_led.set_rgb(r, g, b).await; info!("RGB set"); info!("Sending DONE from RGB task"); - AT_CH.send(Msg::Done(at_command.id)).await; + AT_CH.send(Msg::Done(id)).await; } } } diff --git a/firmware/src/sysex/mod.rs b/firmware/src/sysex/mod.rs deleted file mode 100644 index 11eb6a3..0000000 --- a/firmware/src/sysex/mod.rs +++ /dev/null @@ -1,96 +0,0 @@ -// SPDX-FileCopyrightText: 2025 hexaTune LLC -// SPDX-License-Identifier: MIT - -use heapless::Vec; - -pub fn build_sysex(payload: &str) -> Option> { - let mut out: Vec = Vec::new(); - - // SysEx start - out.push(0xF0).ok()?; - - // Payload - for b in payload.as_bytes() { - out.push(*b).ok()?; - } - - // SysEx end - out.push(0xF7).ok()?; - - Some(out) -} - -pub fn sysex_to_usb_midi_packets(sysex: &[u8]) -> Vec<[u8; 4], M> { - let mut out: Vec<[u8; 4], M> = Vec::new(); - - let mut i = 0usize; - while i < sysex.len() { - let rem = sysex.len() - i; - if rem >= 3 { - if rem == 3 && sysex[sysex.len() - 1] == 0xF7 { - out.push([0x07, sysex[i], sysex[i + 1], sysex[i + 2]]).ok(); // end with 3 - i += 3; - } else { - out.push([0x04, sysex[i], sysex[i + 1], sysex[i + 2]]).ok(); // start/continue - i += 3; - } - } else if rem == 2 { - out.push([0x06, sysex[i], sysex[i + 1], 0x00]).ok(); // end with 2 - i += 2; - } else { - out.push([0x05, sysex[i], 0x00, 0x00]).ok(); // end with 1 - i += 1; - } - } - out -} - -pub fn extract_sysex_payload(data: &[u8]) -> Option> { - let mut out: Vec = Vec::new(); - - for chunk in data.chunks_exact(4) { - let cin = chunk[0] & 0x0F; - let b1 = chunk[1]; - let b2 = chunk[2]; - let b3 = chunk[3]; - - match cin { - 0x4 => { - // SysEx continue/start (3 byte data) - for &b in &[b1, b2, b3] { - if b != 0 { - out.push(b).ok(); - } - } - } - 0x5 => { - if b1 != 0 { - out.push(b1).ok(); - } - } // end with 1 - 0x6 => { - for &b in &[b1, b2] { - if b != 0 { - out.push(b).ok(); - } - } - } // end with 2 - 0x7 => { - for &b in &[b1, b2, b3] { - if b != 0 { - out.push(b).ok(); - } - } - } // end with 3 - _ => {} - } - } - - if out.first().copied() == Some(0xF0) && out.last().copied() == Some(0xF7) { - let mut payload: Vec = Vec::new(); - payload.extend_from_slice(&out[1..out.len() - 1]).ok()?; - Some(payload) - } else { - None - } -} diff --git a/firmware/src/usb/usb_task.rs b/firmware/src/usb/usb_task.rs index 32bd300..30304f3 100644 --- a/firmware/src/usb/usb_task.rs +++ b/firmware/src/usb/usb_task.rs @@ -6,12 +6,13 @@ use embassy_futures::select::{Either, select}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex as Cs; use embassy_sync::mutex::Mutex; +use hexa_tune_proto::sysex; +use hexa_tune_proto::usb_midi; + use crate::AT_CH; use crate::USB_CH; -use crate::at::get_empty_id; use crate::channel::*; -use crate::error::Error; -use crate::sysex::{build_sysex, extract_sysex_payload, sysex_to_usb_midi_packets}; +use crate::error::FirmwareError; use crate::usb::{MyMidiClass, MyUsbDevice}; #[embassy_executor::task] @@ -43,48 +44,108 @@ pub async fn usb_io_task(midi: &'static Mutex>) { let data = &buf[..n]; info!("Received MIDI packet: {:?}", data); - if let Some(payload) = extract_sysex_payload(data) { - if let Ok(input) = core::str::from_utf8(&payload) { - match heapless::String::<64>::try_from(input) { - Ok(line) => { - AT_CH.send(Msg::AtRxLine(line)).await; - } - Err(_) => { - error!("Error Code: {}", Error::InvalidDataLength.code()); - AT_CH - .send(Msg::Err(get_empty_id(), Error::InvalidDataLength)) - .await; - } + let num_packets = n / 4; + if num_packets == 0 { + continue; + } + + // Collect 4-byte packets from raw data + let mut packets = [[0u8; 4]; 16]; + for i in 0..num_packets.min(16) { + packets[i] = [ + data[i * 4], + data[i * 4 + 1], + data[i * 4 + 2], + data[i * 4 + 3], + ]; + } + + // Depacketize USB MIDI → SysEx + let mut sysex_buf = [0u8; 128]; + let sysex_len = match usb_midi::depacketize( + &packets[..num_packets.min(16)], + &mut sysex_buf, + ) { + Ok(len) => len, + Err(e) => { + error!("USB MIDI depacketize error"); + AT_CH + .send(Msg::Err(0, FirmwareError::Proto(e))) + .await; + continue; + } + }; + + // Unframe SysEx → payload + let payload = match sysex::unframe(&sysex_buf[..sysex_len]) { + Ok(p) => p, + Err(e) => { + error!("SysEx unframe error"); + AT_CH + .send(Msg::Err(0, FirmwareError::Proto(e))) + .await; + continue; + } + }; + + // Convert to UTF-8 string and send to AT channel + match core::str::from_utf8(payload) { + Ok(input) => match MsgString::try_from(input) { + Ok(line) => { + AT_CH.send(Msg::AtRxLine(line)).await; + } + Err(_) => { + error!("AT payload too long for buffer"); + AT_CH + .send(Msg::Err( + 0, + FirmwareError::Proto( + hexa_tune_proto::ProtoError::BufferTooSmall, + ), + )) + .await; } - } else { - error!("Error Code: {}", Error::InvalidUtf8.code()); + }, + Err(_) => { + error!("Invalid UTF-8 in payload"); AT_CH - .send(Msg::Err(get_empty_id(), Error::InvalidUtf8)) + .send(Msg::Err( + 0, + FirmwareError::Proto(hexa_tune_proto::ProtoError::InvalidUtf8), + )) .await; } - } else { - error!("Error Code: {}", Error::InvalidSysEx.code()); - /*AT_CH - .send(Msg::Err(get_empty_id(), Error::InvalidSysEx)) - .await;*/ } } Either::Second(msg) => match msg { Msg::UsbTxLine(line) => { - if let Some(sysex) = build_sysex::<64>(&line) { - let packets = sysex_to_usb_midi_packets::<64>(&sysex); - info!("Sending {} MIDI packets", packets.len()); - - let mut m = midi.lock().await; - for pkt in packets.iter() { - //info!("Sending MIDI packet: {:?}", pkt); - if let Err(e) = m.write_packet(pkt).await { - error!("USB write error: {:?}", e); + let line_bytes = line.as_bytes(); + let mut sysex_buf = [0u8; 128]; + match sysex::frame(line_bytes, &mut sysex_buf) { + Ok(sysex_len) => { + let mut packets = [[0u8; 4]; 32]; + match usb_midi::packetize( + &sysex_buf[..sysex_len], + &mut packets, + ) { + Ok(np) => { + info!("Sending {} MIDI packets", np); + let mut m = midi.lock().await; + for pkt in packets[..np].iter() { + if let Err(e) = m.write_packet(pkt).await { + error!("USB write error: {:?}", e); + } + } + } + Err(_) => { + error!("USB MIDI packetize error"); + } } } - } else { - error!("UsbTxLine too long to fit into SysEx"); + Err(_) => { + error!("UsbTxLine too long to fit into SysEx"); + } } } _ => { From 064b8219f253c1972558251fae24409345dbb3ac Mon Sep 17 00:00:00 2001 From: husamettinarabaci Date: Thu, 5 Mar 2026 03:15:35 +0300 Subject: [PATCH 16/16] chore(firmware): switch hexa-tune-proto deps from local path to crates.io v0.1.1 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- firmware/Cargo.lock | 307 ++++++++++++++++++-------------------------- firmware/Cargo.toml | 6 +- 2 files changed, 126 insertions(+), 187 deletions(-) diff --git a/firmware/Cargo.lock b/firmware/Cargo.lock index 317232d..cc6f576 100644 --- a/firmware/Cargo.lock +++ b/firmware/Cargo.lock @@ -16,9 +16,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] @@ -55,9 +55,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "az" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" +checksum = "be5eb007b7cacc6c660343e96f650fedf4b5a77512399eb952ca6642cf8d13f7" [[package]] name = "bare-metal" @@ -103,9 +103,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.4" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] name = "block-buffer" @@ -118,9 +118,9 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.23.2" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" [[package]] name = "byteorder" @@ -130,9 +130,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cfg-if" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "codespan-reporting" @@ -173,7 +173,7 @@ checksum = "e37549a379a9e0e6e576fd208ee60394ccb8be963889eebba3ffe0980364f472" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -208,9 +208,9 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", "typenum", @@ -237,7 +237,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -248,7 +248,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -277,7 +277,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -291,9 +291,9 @@ dependencies = [ [[package]] name = "defmt-rtt" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2cac3b8a5644a9e02b75085ebad3b6deafdbdbdec04bb25086523828aa4dfd1" +checksum = "93d5a25c99d89c40f5676bec8cefe0614f17f0f40e916f98e345dae941807f9e" dependencies = [ "critical-section", "defmt", @@ -311,9 +311,9 @@ dependencies = [ [[package]] name = "document-features" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" dependencies = [ "litrs", ] @@ -396,7 +396,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -473,7 +473,7 @@ dependencies = [ "nb 1.1.0", "pio", "rand_core 0.6.4", - "rand_core 0.9.3", + "rand_core 0.9.5", "rp-pac", "rp2040-boot2", "sha2-const-stable", @@ -625,9 +625,9 @@ dependencies = [ [[package]] name = "ena" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d248bdd43ce613d87415282f69b9bb99d947d290b10962dd6c56233312c2ad5" +checksum = "eabffdaee24bd1bf95c5ef7cec31260444317e72ea56c4c91750e8b7ee58d5f1" dependencies = [ "log", ] @@ -646,9 +646,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "fixed" -version = "1.29.0" +version = "1.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707070ccf8c4173548210893a0186e29c266901b71ed20cd9e2ca0193dfe95c3" +checksum = "c566da967934c6c7ee0458a9773de9b2a685bd2ce26a3b28ddfc740e640182f5" dependencies = [ "az", "bytemuck", @@ -670,15 +670,15 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-sink" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "generic-array" @@ -692,12 +692,13 @@ dependencies = [ [[package]] name = "half" -version = "2.6.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" dependencies = [ "cfg-if", "crunchy", + "zerocopy", ] [[package]] @@ -720,9 +721,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "heapless" @@ -746,14 +747,18 @@ dependencies = [ [[package]] name = "hexa-tune-proto" -version = "0.1.0" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "083b47eef4a8c3b07d4235104a19b6991498cf59fe7eac89decbcbf542accde8" dependencies = [ "defmt", ] [[package]] name = "hexa-tune-proto-embedded" -version = "0.1.0" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ad329deaf227587caa833be9bf8f28b1f52da0cc3ec3ac1b4861af29335a583" dependencies = [ "defmt", "heapless 0.9.1", @@ -795,12 +800,12 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "indexmap" -version = "2.11.4" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", - "hashbrown 0.16.0", + "hashbrown 0.16.1", ] [[package]] @@ -814,9 +819,9 @@ dependencies = [ [[package]] name = "keccak" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" dependencies = [ "cpufeatures", ] @@ -855,37 +860,36 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.176" +version = "0.2.182" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" [[package]] name = "litrs" -version = "0.4.2" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" [[package]] name = "lock_api" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "memchr" -version = "2.7.6" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "nb" @@ -919,9 +923,9 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" +checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" dependencies = [ "num_enum_derive", "rustversion", @@ -929,13 +933,13 @@ dependencies = [ [[package]] name = "num_enum_derive" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" +checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -956,9 +960,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", "parking_lot_core", @@ -966,15 +970,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.11" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-link", ] [[package]] @@ -1053,14 +1057,14 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] name = "portable-atomic" -version = "1.11.1" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" dependencies = [ "critical-section", ] @@ -1090,23 +1094,23 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] name = "proc-macro2" -version = "1.0.101" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.40" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] @@ -1119,24 +1123,24 @@ checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" [[package]] name = "rand_core" -version = "0.9.3" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" [[package]] name = "redox_syscall" -version = "0.5.17" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.11.0", ] [[package]] name = "regex" -version = "1.11.3" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", @@ -1146,9 +1150,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.11" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -1157,18 +1161,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.6" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "rgb" -version = "0.8.52" +version = "0.8.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce" -dependencies = [ - "bytemuck", -] +checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4" [[package]] name = "rp-pac" @@ -1236,31 +1237,31 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.227" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80ece43fc6fbed4eb5392ab50c07334d3e577cbf40997ee896fe7af40bba4245" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", ] [[package]] name = "serde_core" -version = "1.0.227" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a576275b607a2c86ea29e410193df32bc680303c82f31e275bbfcafe8b33be5" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.227" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e694923b8824cf0e9b382adf0f60d4e05f348f357b38833a3fa5ed7c2ede04" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -1287,9 +1288,9 @@ checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" [[package]] name = "siphasher" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" +checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" [[package]] name = "smallvec" @@ -1308,9 +1309,9 @@ dependencies = [ [[package]] name = "smart-leds-trait" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edeb89c73244414bb0568611690dd095b2358b3fda5bae65ad784806cca00157" +checksum = "a7f4441a131924d58da6b83a7ad765c460e64630cce504376c3a87a2558c487f" dependencies = [ "rgb", ] @@ -1327,9 +1328,9 @@ dependencies = [ [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "static_cell" @@ -1371,9 +1372,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.106" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -1382,9 +1383,9 @@ dependencies = [ [[package]] name = "term" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2111ef44dae28680ae9752bb89409e7310ca33a8c621ebe7b106cf5c928b3ac0" +checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1" dependencies = [ "windows-sys", ] @@ -1400,35 +1401,35 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.16" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.16" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] name = "typenum" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "unicode-ident" -version = "1.0.19" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-width" @@ -1537,99 +1538,35 @@ dependencies = [ [[package]] name = "windows-link" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-sys" -version = "0.61.1" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f109e41dd4a3c848907eb83d5a42ea98b3769495597450cf6d153507b166f0f" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ "windows-link", ] -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - [[package]] name = "zerocopy" -version = "0.8.27" +version = "0.8.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +checksum = "a789c6e490b576db9f7e6b6d661bcc9799f7c0ac8352f56ea20193b2681532e5" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.27" +version = "0.8.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +checksum = "f65c489a7071a749c849713807783f70672b28094011623e200cb86dcb835953" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] diff --git a/firmware/Cargo.toml b/firmware/Cargo.toml index e2878ad..8fe112f 100644 --- a/firmware/Cargo.toml +++ b/firmware/Cargo.toml @@ -61,8 +61,10 @@ heapless = "0.9.1" static_cell = "2.1" portable-atomic = { version = "1.5", features = ["critical-section"] } -hexa-tune-proto = { path = "../../hexaTuneProto/crates/hexa-tune-proto", default-features = false } -hexa-tune-proto-embedded = { path = "../../hexaTuneProto/crates/hexa-tune-proto-embedded", default-features = false, features = ["defmt"] } +hexa-tune-proto-embedded = { version = "0.1.1", default-features = false, features = [ + "defmt", +] } +hexa-tune-proto = "0.1.1" [profile.release] # Enable generation of debug symbols even on release builds