From 3292ad51ea7b0e6cde35bb5a167dfce18c8917a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=81=E6=A0=91=E4=BA=BA?= Date: Sat, 7 Sep 2024 13:13:39 +0100 Subject: [PATCH] feat: added glue exports for kwm/qmc --- Cargo.lock | 3 + um_crypto/kuwo/src/lib.rs | 12 + um_crypto/qmc/Cargo.toml | 4 +- um_crypto/qmc/src/footer/android_qtag.rs | 6 +- um_crypto/qmc/src/footer/android_stag.rs | 8 +- um_crypto/qmc/src/footer/mod.rs | 10 +- um_crypto/qmc/src/footer/musicex_v1.rs | 13 +- um_crypto/qmc/src/footer/pc_v1_legacy.rs | 2 +- um_crypto/qmc/src/footer/pc_v2_musicex.rs | 3 +- um_wasm/Cargo.toml | 1 + um_wasm/src/exports/kuwo.rs | 69 +++- um_wasm/src/exports/mod.rs | 1 + um_wasm/src/exports/qmc.rs | 60 +++ um_wasm/src/lib.rs | 11 +- um_wasm_loader/build.js | 3 +- um_wasm_loader/pnpm-lock.yaml | 433 +++++++++++----------- 16 files changed, 398 insertions(+), 241 deletions(-) create mode 100644 um_wasm/src/exports/qmc.rs diff --git a/Cargo.lock b/Cargo.lock index 5dc9306..3915fc3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -159,8 +159,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -363,6 +365,7 @@ version = "0.1.0" dependencies = [ "anyhow", "console_error_panic_hook", + "getrandom", "umc_kuwo", "umc_qmc", "wasm-bindgen", diff --git a/um_crypto/kuwo/src/lib.rs b/um_crypto/kuwo/src/lib.rs index 7c5817d..ca09125 100644 --- a/um_crypto/kuwo/src/lib.rs +++ b/um_crypto/kuwo/src/lib.rs @@ -36,6 +36,18 @@ pub enum Cipher { V2(CipherV2), } +impl Cipher { + pub fn decrypt(&self, data: &mut T, offset: usize) + where + T: AsMut<[u8]> + ?Sized, + { + match self { + Cipher::V1(cipher) => cipher.decrypt(data, offset), + Cipher::V2(cipher) => cipher.decrypt(data, offset), + } + } +} + pub struct Header { pub magic: [u8; 0x10], diff --git a/um_crypto/qmc/Cargo.toml b/um_crypto/qmc/Cargo.toml index e1fa004..8d0bc02 100644 --- a/um_crypto/qmc/Cargo.toml +++ b/um_crypto/qmc/Cargo.toml @@ -7,6 +7,6 @@ edition = "2021" anyhow = "1.0.86" byteorder = "1.5.0" itertools = "0.13.0" -tc_tea = "0.1.4" +tc_tea = { version = "0.1.4", default-features = false, features = [] } thiserror = "1.0.63" -umc_utils = { path = "../utils" } \ No newline at end of file +umc_utils = { path = "../utils" } diff --git a/um_crypto/qmc/src/footer/android_qtag.rs b/um_crypto/qmc/src/footer/android_qtag.rs index b759742..aac0bed 100644 --- a/um_crypto/qmc/src/footer/android_qtag.rs +++ b/um_crypto/qmc/src/footer/android_qtag.rs @@ -10,7 +10,7 @@ pub struct QTagMetadata { } impl MetadataParser for QTagMetadata { - fn from_byte_slice(buffer: &[u8]) -> anyhow::Result> { + fn from_byte_slice(buffer: &[u8]) -> Result, FooterParseError> { if buffer.len() < 8 { Err(FooterParseError::BufferTooSmall(8))?; } @@ -39,7 +39,9 @@ impl MetadataParser for QTagMetadata { ekey: Some(ekey.into()), size: actual_payload_len + 8, data: Data::AndroidQTag(QTagMetadata { - resource_id: resource_id.parse()?, + resource_id: resource_id.parse().map_err(|_| { + FooterParseError::StringToIntError(resource_id.to_string()) + })?, }), })); } diff --git a/um_crypto/qmc/src/footer/android_stag.rs b/um_crypto/qmc/src/footer/android_stag.rs index e8c12c0..984703e 100644 --- a/um_crypto/qmc/src/footer/android_stag.rs +++ b/um_crypto/qmc/src/footer/android_stag.rs @@ -6,13 +6,13 @@ use itertools::Itertools; pub struct STagMetadata { /// Resource identifier (aka. `file.media_mid`). pub media_mid: String, - + /// Resource id (numeric) pub resource_id: u64, } impl MetadataParser for STagMetadata { - fn from_byte_slice(buffer: &[u8]) -> anyhow::Result> { + fn from_byte_slice(buffer: &[u8]) -> Result, FooterParseError> { if buffer.len() < 8 { Err(FooterParseError::BufferTooSmall(8))?; } @@ -37,7 +37,9 @@ impl MetadataParser for STagMetadata { ekey: None, size: actual_payload_len + 8, data: Data::AndroidSTag(STagMetadata { - resource_id: id.parse()?, + resource_id: id + .parse() + .map_err(|_| FooterParseError::StringToIntError(id.to_string()))?, media_mid: media_mid.to_string(), }), })); diff --git a/um_crypto/qmc/src/footer/mod.rs b/um_crypto/qmc/src/footer/mod.rs index 83e5ec1..e068693 100644 --- a/um_crypto/qmc/src/footer/mod.rs +++ b/um_crypto/qmc/src/footer/mod.rs @@ -9,7 +9,6 @@ use crate::footer::{ android_qtag::QTagMetadata, android_stag::STagMetadata, pc_v1_legacy::PcV1Legacy, pc_v2_musicex::PcV2MusicEx, }; -use anyhow::Result; use thiserror::Error; pub const INITIAL_DETECTION_LEN: usize = 1024; @@ -27,6 +26,8 @@ pub enum FooterParseError { PCv2InvalidVersion(u32), #[error("PCv2/MusicEx: Invalid `MusicEx` size: {0}")] PCv2MusicExUnsupportedPayloadSize(usize), + #[error("PCv2/MusicEx: Invalid `MusicEx` data: {0}")] + PCv2MusicExInvalidError(anyhow::Error), #[error("Android/STag: Invalid ID field: {0}")] STagInvalidId(String), @@ -41,6 +42,9 @@ pub enum FooterParseError { QTagInvalidVersion(String), #[error("Android/QTag: Invalid EKey field: {0}")] QTagInvalidEKey(String), + + #[error("Parse: Failed to parse string '{0}' as integer")] + StringToIntError(String), } /// Footer type @@ -71,10 +75,10 @@ pub struct Metadata { } pub trait MetadataParser { - fn from_byte_slice(buffer: &[u8]) -> Result>; + fn from_byte_slice(buffer: &[u8]) -> Result, FooterParseError>; } -pub fn from_byte_slice(buffer: &[u8]) -> Result> { +pub fn from_byte_slice(buffer: &[u8]) -> Result, FooterParseError> { if let Some(metadata) = STagMetadata::from_byte_slice(buffer)? { return Ok(Some(metadata)); } diff --git a/um_crypto/qmc/src/footer/musicex_v1.rs b/um_crypto/qmc/src/footer/musicex_v1.rs index f3768d5..9038cc5 100644 --- a/um_crypto/qmc/src/footer/musicex_v1.rs +++ b/um_crypto/qmc/src/footer/musicex_v1.rs @@ -1,8 +1,8 @@ -use std::io::{Cursor, Read}; -use byteorder::{ByteOrder,ReadBytesExt, LE}; -use crate::footer::{Data, FooterParseError, Metadata}; use crate::footer::pc_v2_musicex::PcV2MusicEx; use crate::footer::utils::from_ascii_utf16; +use crate::footer::{Data, FooterParseError, Metadata}; +use byteorder::{ByteOrder, ReadBytesExt, LE}; +use std::io::{Cursor, Read}; #[derive(Debug, Clone, PartialEq)] pub struct MusicExV1 { @@ -52,7 +52,7 @@ impl MusicExV1 { } } -pub fn parse_v1(footer: &[u8]) -> anyhow::Result> { +pub fn parse_v1(footer: &[u8]) -> Result, FooterParseError> { let (payload, payload_len) = footer.split_at(footer.len() - 4); let payload_len = LE::read_u32(&payload_len) as usize; if payload_len != 0xC0 { @@ -60,9 +60,10 @@ pub fn parse_v1(footer: &[u8]) -> anyhow::Result> { payload_len, ))?; } - + let payload = &payload[payload.len() - (payload_len - 0x10)..]; - let payload = MusicExV1::from_bytes(payload)?; + let payload = + MusicExV1::from_bytes(payload).map_err(FooterParseError::PCv2MusicExInvalidError)?; let mid = from_ascii_utf16(&payload.mid); let media_filename = from_ascii_utf16(&payload.media_filename); diff --git a/um_crypto/qmc/src/footer/pc_v1_legacy.rs b/um_crypto/qmc/src/footer/pc_v1_legacy.rs index e7f5ad1..e93d4d5 100644 --- a/um_crypto/qmc/src/footer/pc_v1_legacy.rs +++ b/um_crypto/qmc/src/footer/pc_v1_legacy.rs @@ -8,7 +8,7 @@ pub const MAX_ALLOWED_EKEY_LEN: usize = 0x500; pub struct PcV1Legacy; impl MetadataParser for PcV1Legacy { - fn from_byte_slice(buffer: &[u8]) -> anyhow::Result> { + fn from_byte_slice(buffer: &[u8]) -> Result, FooterParseError> { if buffer.len() < 8 { Err(FooterParseError::BufferTooSmall(8))?; } diff --git a/um_crypto/qmc/src/footer/pc_v2_musicex.rs b/um_crypto/qmc/src/footer/pc_v2_musicex.rs index c0799a1..463326e 100644 --- a/um_crypto/qmc/src/footer/pc_v2_musicex.rs +++ b/um_crypto/qmc/src/footer/pc_v2_musicex.rs @@ -1,5 +1,4 @@ use crate::footer::{musicex_v1, FooterParseError, Metadata, MetadataParser}; -use anyhow::Result; use byteorder::{ByteOrder, LE}; #[derive(Debug, Clone, PartialEq)] @@ -12,7 +11,7 @@ pub struct PcV2MusicEx { } impl MetadataParser for PcV2MusicEx { - fn from_byte_slice(payload: &[u8]) -> Result> { + fn from_byte_slice(payload: &[u8]) -> Result, FooterParseError> { if payload.len() < 16 { Err(FooterParseError::BufferTooSmall(16))?; } diff --git a/um_wasm/Cargo.toml b/um_wasm/Cargo.toml index 5c84bf0..a3982ad 100644 --- a/um_wasm/Cargo.toml +++ b/um_wasm/Cargo.toml @@ -16,6 +16,7 @@ default = ["console_error_panic_hook"] [dependencies] wasm-bindgen = "0.2.84" anyhow = "1.0.86" +getrandom = { version = "0.2", features = ["js"] } # The `console_error_panic_hook` crate provides better debugging of panics by # logging them with `console.error`. This is great for development, but requires diff --git a/um_wasm/src/exports/kuwo.rs b/um_wasm/src/exports/kuwo.rs index f7c3f88..68b2886 100644 --- a/um_wasm/src/exports/kuwo.rs +++ b/um_wasm/src/exports/kuwo.rs @@ -1,9 +1,70 @@ use crate::errors::map_js_error; +use crate::exports::qmc::JsQMC2; +use umc_kuwo::kwm_v1::CipherV1; +use umc_kuwo::{Cipher, Header}; use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::JsError; -#[wasm_bindgen(js_name=kuwoDecodeEKey)] -pub fn decode_ekey(ekey: &str) -> Result { - let ekey = umc_kuwo::des::decode_ekey(ekey, &umc_kuwo::SECRET_KEY).map_err(map_js_error)?; - Ok(ekey) +/// Kuwo KWM file header. +#[wasm_bindgen(js_name=KuwoHeader)] +pub struct JsKuwoHeader(Header); + +#[wasm_bindgen(js_class = KuwoHeader)] +impl JsKuwoHeader { + /// Parse the KuWo header (0x400 bytes) + pub fn parse(header: &[u8]) -> Result { + let hdr = Header::from_bytes(header).map_err(map_js_error)?; + Ok(JsKuwoHeader(hdr)) + } + + /// Get quality id (used for Android Kuwo APP), + /// that can be then used to extract ekey from mmkv db. + #[wasm_bindgen(getter, js_name=qualityId)] + pub fn quality_id(&self) -> u32 { + self.0.get_quality_id() + } + + /// Create an instance of cipher (decipher) for decryption + #[wasm_bindgen(js_name=makeCipher)] + pub fn make_cipher(&self, ekey: Option) -> Result { + let cipher = self.0.get_cipher(ekey).map_err(map_js_error)?; + Ok(JsCipher(cipher)) + } +} + +/// Create a decipher instance for "BoDian Music". +#[wasm_bindgen(js_name=kuwoBodianCipherFactory)] +pub fn js_kuwo_bodian_cipher_factory(ekey: &str) -> Result { + let ekey = umc_kuwo::des::decode_ekey(ekey, &umc_kuwo::SECRET_KEY).map_err(map_js_error)?; + JsQMC2::new(ekey.as_str()) +} + +/// Kuwo KWM v1 +#[wasm_bindgen(js_name=KWMCipherV1)] +pub struct JsCipherV1(CipherV1); + +#[wasm_bindgen(js_class=KWMCipherV1)] +impl JsCipherV1 { + /// Create a decipher instance for "Kuwo KWM v1". + pub fn decrypt(&self, buffer: &mut [u8], offset: usize) { + self.0.decrypt(buffer, offset) + } +} + +/// Create a decipher instance for "Kuwo KWM v2". +#[wasm_bindgen(js_name=kuwoV2CipherFactory)] +pub fn js_kuwo_v2_cipher_factory(ekey: &str) -> Result { + JsQMC2::new(ekey) +} + +/// Common V1/V2 wrapper interface, derived from `KuwoHeader.makeCipher` +#[wasm_bindgen(js_name=KWMCipher)] +pub struct JsCipher(Cipher); + +#[wasm_bindgen(js_class=KWMCipher)] +impl JsCipher { + /// Decrypt buffer at given offset. + pub fn decrypt(&self, buffer: &mut [u8], offset: usize) { + self.0.decrypt(buffer, offset) + } } diff --git a/um_wasm/src/exports/mod.rs b/um_wasm/src/exports/mod.rs index 77169f0..a789c2f 100644 --- a/um_wasm/src/exports/mod.rs +++ b/um_wasm/src/exports/mod.rs @@ -1 +1,2 @@ pub mod kuwo; +pub mod qmc; diff --git a/um_wasm/src/exports/qmc.rs b/um_wasm/src/exports/qmc.rs new file mode 100644 index 0000000..098f897 --- /dev/null +++ b/um_wasm/src/exports/qmc.rs @@ -0,0 +1,60 @@ +use crate::errors::map_js_error; +use umc_qmc::footer::FooterParseError; +use umc_qmc::QMCv2Cipher; +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::JsError; + +/// QMC1 (qmcflac) decipher, decrypt buffer at given offset. +#[wasm_bindgen(js_name=decryptQMC1)] +pub fn js_decrypt_qmc1(buffer: &mut [u8], offset: usize) { + umc_qmc::v1::decrypt(buffer, offset) +} + +/// QMC2 (mgg/mflac) cipher +#[wasm_bindgen(js_name=QMC2)] +pub struct JsQMC2(QMCv2Cipher); + +#[wasm_bindgen(js_class=QMC2)] +impl JsQMC2 { + /// Create a new QMC2 (mgg/mflac) cipher instance. + #[wasm_bindgen(constructor)] + pub fn new(ekey: &str) -> Result { + let cipher = QMCv2Cipher::new(ekey.as_bytes()).map_err(map_js_error)?; + Ok(JsQMC2(cipher)) + } + + /// Decrypt buffer at given offset. + pub fn decrypt(&self, buffer: &mut [u8], offset: usize) { + self.0.decrypt(buffer, offset) + } +} + +/// QMC Footer. +#[wasm_bindgen(js_name=QMCFooter)] +pub struct JsQMCFooter(umc_qmc::footer::Metadata); + +#[wasm_bindgen(js_class=QMCFooter)] +impl JsQMCFooter { + /// Parse QMC Footer from byte slice. + /// Recommended to slice the last 1024 bytes of the file. + pub fn parse(footer: &[u8]) -> Result, JsError> { + match umc_qmc::footer::from_byte_slice(footer) { + Ok(Some(metadata)) => Ok(Some(JsQMCFooter(metadata))), + Ok(None) => Ok(None), + Err(FooterParseError::PCv1EKeyTooLarge(_)) => Ok(None), + Err(err) => Err(err.into()), + } + } + + /// Get eKey (if embedded) + #[wasm_bindgen(getter)] + pub fn ekey(&self) -> Option { + self.0.ekey.clone() + } + + /// Get size of footer + #[wasm_bindgen(getter)] + pub fn size(&self) -> usize { + self.0.size + } +} diff --git a/um_wasm/src/lib.rs b/um_wasm/src/lib.rs index b48d59d..60086d2 100644 --- a/um_wasm/src/lib.rs +++ b/um_wasm/src/lib.rs @@ -5,17 +5,8 @@ mod utils; use utils::set_panic_hook; use wasm_bindgen::prelude::*; -#[wasm_bindgen] -extern "C" { - fn alert(s: &str); -} - +/// Init panic hook #[wasm_bindgen] pub fn init() { set_panic_hook(); } - -#[wasm_bindgen] -pub fn greet() { - alert("hello world!"); -} diff --git a/um_wasm_loader/build.js b/um_wasm_loader/build.js index 034edeb..d689266 100644 --- a/um_wasm_loader/build.js +++ b/um_wasm_loader/build.js @@ -37,7 +37,7 @@ async function replaceBytes(filepath, ...binaryReplacements) { let content = await readFile(filepath); for (const [search, replace] of binaryReplacements) { let idx = -1; - while ((idx = content.indexOf(search, idx + 1)) != -1) { + while ((idx = content.indexOf(search, idx + 1)) !== -1) { replace.copy(content, idx, 0, replace.length); } } @@ -91,6 +91,7 @@ async function main() { // Ask rollup to build bundles. await run(['pnpm', 'build:bundle']); + await run(['pnpm', 'exec', 'prettier', '--ignore-path', '', '-w', 'dist/loader.d.ts']); } main() diff --git a/um_wasm_loader/pnpm-lock.yaml b/um_wasm_loader/pnpm-lock.yaml index 0f3a770..f0f5b1c 100644 --- a/um_wasm_loader/pnpm-lock.yaml +++ b/um_wasm_loader/pnpm-lock.yaml @@ -1,68 +1,53 @@ -lockfileVersion: '6.0' +lockfileVersion: '9.0' settings: autoInstallPeers: true excludeLinksFromLockfile: false -devDependencies: - '@rollup/plugin-replace': - specifier: ^5.0.7 - version: 5.0.7(rollup@4.21.2) - '@rollup/plugin-wasm': - specifier: ^6.2.2 - version: 6.2.2(rollup@4.21.2) - '@types/node': - specifier: ^22.5.2 - version: 22.5.2 - prettier: - specifier: ^3.3.3 - version: 3.3.3 - rollup: - specifier: ^4.21.2 - version: 4.21.2 - rollup-plugin-dts: - specifier: ^6.1.1 - version: 6.1.1(rollup@4.21.2)(typescript@5.5.4) - typescript: - specifier: ^5.5.4 - version: 5.5.4 +importers: + + .: + devDependencies: + '@rollup/plugin-replace': + specifier: ^5.0.7 + version: 5.0.7(rollup@4.21.2) + '@rollup/plugin-wasm': + specifier: ^6.2.2 + version: 6.2.2(rollup@4.21.2) + '@types/node': + specifier: ^22.5.2 + version: 22.5.2 + prettier: + specifier: ^3.3.3 + version: 3.3.3 + rollup: + specifier: ^4.21.2 + version: 4.21.2 + rollup-plugin-dts: + specifier: ^6.1.1 + version: 6.1.1(rollup@4.21.2)(typescript@5.5.4) + typescript: + specifier: ^5.5.4 + version: 5.5.4 packages: - /@babel/code-frame@7.24.7: + '@babel/code-frame@7.24.7': resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} engines: {node: '>=6.9.0'} - requiresBuild: true - dependencies: - '@babel/highlight': 7.24.7 - picocolors: 1.0.1 - dev: true - optional: true - /@babel/helper-validator-identifier@7.24.7: + '@babel/helper-validator-identifier@7.24.7': resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} engines: {node: '>=6.9.0'} - requiresBuild: true - dev: true - optional: true - /@babel/highlight@7.24.7: + '@babel/highlight@7.24.7': resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} engines: {node: '>=6.9.0'} - requiresBuild: true - dependencies: - '@babel/helper-validator-identifier': 7.24.7 - chalk: 2.4.2 - js-tokens: 4.0.0 - picocolors: 1.0.1 - dev: true - optional: true - /@jridgewell/sourcemap-codec@1.5.0: + '@jridgewell/sourcemap-codec@1.5.0': resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} - dev: true - /@rollup/plugin-replace@5.0.7(rollup@4.21.2): + '@rollup/plugin-replace@5.0.7': resolution: {integrity: sha512-PqxSfuorkHz/SPpyngLyg5GCEkOcee9M1bkxiVDr41Pd61mqP1PLOoDPbpl44SB2mQGKwV/In74gqQmGITOhEQ==} engines: {node: '>=14.0.0'} peerDependencies: @@ -70,13 +55,8 @@ packages: peerDependenciesMeta: rollup: optional: true - dependencies: - '@rollup/pluginutils': 5.1.0(rollup@4.21.2) - magic-string: 0.30.11 - rollup: 4.21.2 - dev: true - /@rollup/plugin-wasm@6.2.2(rollup@4.21.2): + '@rollup/plugin-wasm@6.2.2': resolution: {integrity: sha512-gpC4R1G9Ni92ZIRTexqbhX7U+9estZrbhP+9SRb0DW9xpB9g7j34r+J2hqrcW/lRI7dJaU84MxZM0Rt82tqYPQ==} engines: {node: '>=14.0.0'} peerDependencies: @@ -84,12 +64,8 @@ packages: peerDependenciesMeta: rollup: optional: true - dependencies: - '@rollup/pluginutils': 5.1.0(rollup@4.21.2) - rollup: 4.21.2 - dev: true - /@rollup/pluginutils@5.1.0(rollup@4.21.2): + '@rollup/pluginutils@5.1.0': resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==} engines: {node: '>=14.0.0'} peerDependencies: @@ -97,258 +73,312 @@ packages: peerDependenciesMeta: rollup: optional: true - dependencies: - '@types/estree': 1.0.5 - estree-walker: 2.0.2 - picomatch: 2.3.1 - rollup: 4.21.2 - dev: true - /@rollup/rollup-android-arm-eabi@4.21.2: + '@rollup/rollup-android-arm-eabi@4.21.2': resolution: {integrity: sha512-fSuPrt0ZO8uXeS+xP3b+yYTCBUd05MoSp2N/MFOgjhhUhMmchXlpTQrTpI8T+YAwAQuK7MafsCOxW7VrPMrJcg==} cpu: [arm] os: [android] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-android-arm64@4.21.2: + '@rollup/rollup-android-arm64@4.21.2': resolution: {integrity: sha512-xGU5ZQmPlsjQS6tzTTGwMsnKUtu0WVbl0hYpTPauvbRAnmIvpInhJtgjj3mcuJpEiuUw4v1s4BimkdfDWlh7gA==} cpu: [arm64] os: [android] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-darwin-arm64@4.21.2: + '@rollup/rollup-darwin-arm64@4.21.2': resolution: {integrity: sha512-99AhQ3/ZMxU7jw34Sq8brzXqWH/bMnf7ZVhvLk9QU2cOepbQSVTns6qoErJmSiAvU3InRqC2RRZ5ovh1KN0d0Q==} cpu: [arm64] os: [darwin] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-darwin-x64@4.21.2: + '@rollup/rollup-darwin-x64@4.21.2': resolution: {integrity: sha512-ZbRaUvw2iN/y37x6dY50D8m2BnDbBjlnMPotDi/qITMJ4sIxNY33HArjikDyakhSv0+ybdUxhWxE6kTI4oX26w==} cpu: [x64] os: [darwin] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-arm-gnueabihf@4.21.2: + '@rollup/rollup-linux-arm-gnueabihf@4.21.2': resolution: {integrity: sha512-ztRJJMiE8nnU1YFcdbd9BcH6bGWG1z+jP+IPW2oDUAPxPjo9dverIOyXz76m6IPA6udEL12reYeLojzW2cYL7w==} cpu: [arm] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-arm-musleabihf@4.21.2: + '@rollup/rollup-linux-arm-musleabihf@4.21.2': resolution: {integrity: sha512-flOcGHDZajGKYpLV0JNc0VFH361M7rnV1ee+NTeC/BQQ1/0pllYcFmxpagltANYt8FYf9+kL6RSk80Ziwyhr7w==} cpu: [arm] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-arm64-gnu@4.21.2: + '@rollup/rollup-linux-arm64-gnu@4.21.2': resolution: {integrity: sha512-69CF19Kp3TdMopyteO/LJbWufOzqqXzkrv4L2sP8kfMaAQ6iwky7NoXTp7bD6/irKgknDKM0P9E/1l5XxVQAhw==} cpu: [arm64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-arm64-musl@4.21.2: + '@rollup/rollup-linux-arm64-musl@4.21.2': resolution: {integrity: sha512-48pD/fJkTiHAZTnZwR0VzHrao70/4MlzJrq0ZsILjLW/Ab/1XlVUStYyGt7tdyIiVSlGZbnliqmult/QGA2O2w==} cpu: [arm64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-powerpc64le-gnu@4.21.2: + '@rollup/rollup-linux-powerpc64le-gnu@4.21.2': resolution: {integrity: sha512-cZdyuInj0ofc7mAQpKcPR2a2iu4YM4FQfuUzCVA2u4HI95lCwzjoPtdWjdpDKyHxI0UO82bLDoOaLfpZ/wviyQ==} cpu: [ppc64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-riscv64-gnu@4.21.2: + '@rollup/rollup-linux-riscv64-gnu@4.21.2': resolution: {integrity: sha512-RL56JMT6NwQ0lXIQmMIWr1SW28z4E4pOhRRNqwWZeXpRlykRIlEpSWdsgNWJbYBEWD84eocjSGDu/XxbYeCmwg==} cpu: [riscv64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-s390x-gnu@4.21.2: + '@rollup/rollup-linux-s390x-gnu@4.21.2': resolution: {integrity: sha512-PMxkrWS9z38bCr3rWvDFVGD6sFeZJw4iQlhrup7ReGmfn7Oukrr/zweLhYX6v2/8J6Cep9IEA/SmjXjCmSbrMQ==} cpu: [s390x] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-x64-gnu@4.21.2: + '@rollup/rollup-linux-x64-gnu@4.21.2': resolution: {integrity: sha512-B90tYAUoLhU22olrafY3JQCFLnT3NglazdwkHyxNDYF/zAxJt5fJUB/yBoWFoIQ7SQj+KLe3iL4BhOMa9fzgpw==} cpu: [x64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-x64-musl@4.21.2: + '@rollup/rollup-linux-x64-musl@4.21.2': resolution: {integrity: sha512-7twFizNXudESmC9oneLGIUmoHiiLppz/Xs5uJQ4ShvE6234K0VB1/aJYU3f/4g7PhssLGKBVCC37uRkkOi8wjg==} cpu: [x64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-win32-arm64-msvc@4.21.2: + '@rollup/rollup-win32-arm64-msvc@4.21.2': resolution: {integrity: sha512-9rRero0E7qTeYf6+rFh3AErTNU1VCQg2mn7CQcI44vNUWM9Ze7MSRS/9RFuSsox+vstRt97+x3sOhEey024FRQ==} cpu: [arm64] os: [win32] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-win32-ia32-msvc@4.21.2: + '@rollup/rollup-win32-ia32-msvc@4.21.2': resolution: {integrity: sha512-5rA4vjlqgrpbFVVHX3qkrCo/fZTj1q0Xxpg+Z7yIo3J2AilW7t2+n6Q8Jrx+4MrYpAnjttTYF8rr7bP46BPzRw==} cpu: [ia32] os: [win32] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-win32-x64-msvc@4.21.2: + '@rollup/rollup-win32-x64-msvc@4.21.2': resolution: {integrity: sha512-6UUxd0+SKomjdzuAcp+HAmxw1FlGBnl1v2yEPSabtx4lBfdXHDVsW7+lQkgz9cNFJGY3AWR7+V8P5BqkD9L9nA==} cpu: [x64] os: [win32] - requiresBuild: true - dev: true - optional: true - /@types/estree@1.0.5: + '@types/estree@1.0.5': resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} - dev: true - /@types/node@22.5.2: + '@types/node@22.5.2': resolution: {integrity: sha512-acJsPTEqYqulZS/Yp/S3GgeE6GZ0qYODUR8aVr/DkhHQ8l9nd4j5x1/ZJy9/gHrRlFMqkO6i0I3E27Alu4jjPg==} - dependencies: - undici-types: 6.19.8 - dev: true - /ansi-styles@3.2.1: + ansi-styles@3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} engines: {node: '>=4'} - requiresBuild: true - dependencies: - color-convert: 1.9.3 - dev: true - optional: true - /chalk@2.4.2: + chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} - requiresBuild: true - dependencies: - ansi-styles: 3.2.1 - escape-string-regexp: 1.0.5 - supports-color: 5.5.0 - dev: true - optional: true - /color-convert@1.9.3: + color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} - requiresBuild: true - dependencies: - color-name: 1.1.3 - dev: true - optional: true - /color-name@1.1.3: + color-name@1.1.3: resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - requiresBuild: true - dev: true - optional: true - /escape-string-regexp@1.0.5: + escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} - requiresBuild: true - dev: true - optional: true - /estree-walker@2.0.2: + estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} - dev: true - /fsevents@2.3.3: + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] - requiresBuild: true - dev: true - optional: true - /has-flag@3.0.0: + has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} - requiresBuild: true - dev: true - optional: true - /js-tokens@4.0.0: + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - requiresBuild: true - dev: true - optional: true - /magic-string@0.30.11: + magic-string@0.30.11: resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} - dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 - dev: true - /picocolors@1.0.1: + picocolors@1.0.1: resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} - requiresBuild: true - dev: true - optional: true - /picomatch@2.3.1: + picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - dev: true - /prettier@3.3.3: + prettier@3.3.3: resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==} engines: {node: '>=14'} hasBin: true - dev: true - /rollup-plugin-dts@6.1.1(rollup@4.21.2)(typescript@5.5.4): + rollup-plugin-dts@6.1.1: resolution: {integrity: sha512-aSHRcJ6KG2IHIioYlvAOcEq6U99sVtqDDKVhnwt70rW6tsz3tv5OSjEiWcgzfsHdLyGXZ/3b/7b/+Za3Y6r1XA==} engines: {node: '>=16'} peerDependencies: rollup: ^3.29.4 || ^4 typescript: ^4.5 || ^5.0 + + rollup@4.21.2: + resolution: {integrity: sha512-e3TapAgYf9xjdLvKQCkQTnbTKd4a6jwlpQSJJFokHGaX2IVjoEqkIIhiQfqsi0cdwlOD+tQGuOd5AJkc5RngBw==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + typescript@5.5.4: + resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + +snapshots: + + '@babel/code-frame@7.24.7': + dependencies: + '@babel/highlight': 7.24.7 + picocolors: 1.0.1 + optional: true + + '@babel/helper-validator-identifier@7.24.7': + optional: true + + '@babel/highlight@7.24.7': + dependencies: + '@babel/helper-validator-identifier': 7.24.7 + chalk: 2.4.2 + js-tokens: 4.0.0 + picocolors: 1.0.1 + optional: true + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@rollup/plugin-replace@5.0.7(rollup@4.21.2)': + dependencies: + '@rollup/pluginutils': 5.1.0(rollup@4.21.2) + magic-string: 0.30.11 + rollup: 4.21.2 + + '@rollup/plugin-wasm@6.2.2(rollup@4.21.2)': + dependencies: + '@rollup/pluginutils': 5.1.0(rollup@4.21.2) + rollup: 4.21.2 + + '@rollup/pluginutils@5.1.0(rollup@4.21.2)': + dependencies: + '@types/estree': 1.0.5 + estree-walker: 2.0.2 + picomatch: 2.3.1 + rollup: 4.21.2 + + '@rollup/rollup-android-arm-eabi@4.21.2': + optional: true + + '@rollup/rollup-android-arm64@4.21.2': + optional: true + + '@rollup/rollup-darwin-arm64@4.21.2': + optional: true + + '@rollup/rollup-darwin-x64@4.21.2': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.21.2': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.21.2': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.21.2': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.21.2': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.21.2': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.21.2': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.21.2': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.21.2': + optional: true + + '@rollup/rollup-linux-x64-musl@4.21.2': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.21.2': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.21.2': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.21.2': + optional: true + + '@types/estree@1.0.5': {} + + '@types/node@22.5.2': + dependencies: + undici-types: 6.19.8 + + ansi-styles@3.2.1: + dependencies: + color-convert: 1.9.3 + optional: true + + chalk@2.4.2: + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + optional: true + + color-convert@1.9.3: + dependencies: + color-name: 1.1.3 + optional: true + + color-name@1.1.3: + optional: true + + escape-string-regexp@1.0.5: + optional: true + + estree-walker@2.0.2: {} + + fsevents@2.3.3: + optional: true + + has-flag@3.0.0: + optional: true + + js-tokens@4.0.0: + optional: true + + magic-string@0.30.11: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + + picocolors@1.0.1: + optional: true + + picomatch@2.3.1: {} + + prettier@3.3.3: {} + + rollup-plugin-dts@6.1.1(rollup@4.21.2)(typescript@5.5.4): dependencies: magic-string: 0.30.11 rollup: 4.21.2 typescript: 5.5.4 optionalDependencies: '@babel/code-frame': 7.24.7 - dev: true - /rollup@4.21.2: - resolution: {integrity: sha512-e3TapAgYf9xjdLvKQCkQTnbTKd4a6jwlpQSJJFokHGaX2IVjoEqkIIhiQfqsi0cdwlOD+tQGuOd5AJkc5RngBw==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true + rollup@4.21.2: dependencies: '@types/estree': 1.0.5 optionalDependencies: @@ -369,23 +399,12 @@ packages: '@rollup/rollup-win32-ia32-msvc': 4.21.2 '@rollup/rollup-win32-x64-msvc': 4.21.2 fsevents: 2.3.3 - dev: true - /supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} - requiresBuild: true + supports-color@5.5.0: dependencies: has-flag: 3.0.0 - dev: true optional: true - /typescript@5.5.4: - resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==} - engines: {node: '>=14.17'} - hasBin: true - dev: true + typescript@5.5.4: {} - /undici-types@6.19.8: - resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} - dev: true + undici-types@6.19.8: {}