From 41c734629dad26f19d01f6b7c7054c8863f207ef Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 16 Feb 2025 17:00:23 -0700 Subject: [PATCH 001/590] Add ratchet-web-train --- Cargo.toml | 2 +- crates/ratchet-web-train/Cargo.toml | 66 +++ crates/ratchet-web-train/README.md | 2 + crates/ratchet-web-train/src/lib.rs | 2 + crates/ratchet-web-train/src/model.rs | 686 ++++++++++++++++++++++++++ 5 files changed, 757 insertions(+), 1 deletion(-) create mode 100644 crates/ratchet-web-train/Cargo.toml create mode 100644 crates/ratchet-web-train/README.md create mode 100644 crates/ratchet-web-train/src/lib.rs create mode 100644 crates/ratchet-web-train/src/model.rs diff --git a/Cargo.toml b/Cargo.toml index eae21a07..78d672bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ members = [ "crates/ratchet-hub", "crates/ratchet-core", - "crates/ratchet-web", + "crates/ratchet-web-train", "crates/ratchet-loader", "crates/ratchet-models", "crates/ratchet-nn", diff --git a/crates/ratchet-web-train/Cargo.toml b/crates/ratchet-web-train/Cargo.toml new file mode 100644 index 00000000..98443f89 --- /dev/null +++ b/crates/ratchet-web-train/Cargo.toml @@ -0,0 +1,66 @@ +[package] +name = "ratchet-web-train" +version = "0.0.0" +edition = "2021" +license = "MIT" +repository = "https://github.com/vinhowe/ratchet-backward" + +[features] +debug = ["ratchet-nn/debug"] + +[lib] +crate-type = ["cdylib", "rlib"] + +[package.metadata.docs.rs] +default-target = "wasm32-unknown-unknown" + +[package.metadata.wasm-pack.profile.dev.wasm-bindgen] +debug-js-glue = true +demangle-name-section = true +dwarf-debug-info = true + +[package.metadata.wasm-pack.profile.release] +wasm-opt = [ + '-O4', + '--enable-simd', + '--flexible-inline-max-function-size', + '4294967295', +] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[dependencies] +ratchet-models = { path = "../ratchet-models" } +ratchet-nn = { path = "../ratchet-nn" } +ratchet-datasets = { path = "../ratchet-datasets" } +ratchet = { path = "../ratchet-core" } +wasm-bindgen = { workspace = true } +wasm-bindgen-futures = { workspace = true } +js-sys = { workspace = true } +thiserror.workspace = true +anyhow.workspace = true +serde = { workspace = true } +serde_json = { workspace = true } +serde-wasm-bindgen = { workspace = true } +console_error_panic_hook = { workspace = true } +console_log = { workspace = true, features = ["color"] } +log.workspace = true +chrono = { workspace = true } +fern = { workspace = true } +tokenizers = { version = "0.19.1", default-features = false, features = [ + "unstable_wasm", +] } +futures = "0.3.30" +async-trait = { workspace = true } +[dependencies.web-sys] +features = [ + 'console', + 'Window', + 'Navigator', +] +workspace = true + + +[dev-dependencies] +wasm-bindgen-test.workspace = true +serde_json = { workspace = true } +ndarray = { workspace = true } diff --git a/crates/ratchet-web-train/README.md b/crates/ratchet-web-train/README.md new file mode 100644 index 00000000..8792012a --- /dev/null +++ b/crates/ratchet-web-train/README.md @@ -0,0 +1,2 @@ +# ratchet-web-train + diff --git a/crates/ratchet-web-train/src/lib.rs b/crates/ratchet-web-train/src/lib.rs new file mode 100644 index 00000000..8c866e04 --- /dev/null +++ b/crates/ratchet-web-train/src/lib.rs @@ -0,0 +1,2 @@ +#![cfg(target_arch = "wasm32")] +mod model; diff --git a/crates/ratchet-web-train/src/model.rs b/crates/ratchet-web-train/src/model.rs new file mode 100644 index 00000000..8b6a43be --- /dev/null +++ b/crates/ratchet-web-train/src/model.rs @@ -0,0 +1,686 @@ +use anyhow::Result; +use async_trait::async_trait; +use ratchet::{DType, Device, DeviceRequest, GradStore, Tensor, Var}; +use ratchet_datasets::batcher::IterResult2; +use ratchet_datasets::{ + nlp::{ + tinystories::{ + Dataset as TinyStoriesDataset, DatasetRandomIter as TinyStoriesDatasetRandomIter, + }, + toy::{ + AddTask, CountTask, ModAddTask, SlapjackTask, SortTask, ToyTaskIter, TwoSumTask, + ZerosTask, + }, + }, + Batcher, +}; +use ratchet_models::gpt2::{Config, GPT2Input, PositionalEncoding}; +use ratchet_models::gpt2::{LayerNormPosition, GPT2}; +use ratchet_nn::{ + clip_grad_norm, cross_entropy, Activation, AdamW, ConstantLR, CosineAnnealingLR, LRScheduler, + LRSchedulerCore, LinearLR, Module, Optimizer, ParamsAdamW, VarBuilder, VarMap, SGD, +}; +use serde::{Deserialize, Serialize}; +use std::iter::Iterator; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen(start)] +pub fn start() { + console_error_panic_hook::set_once(); + let logger = fern::Dispatch::new() + .format(|out, message, record| { + out.finish(format_args!( + "{}[{}][{}] {}", + chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S]"), + record.target(), + record.level(), + message + )) + }) + .level_for("tokenizers", log::LevelFilter::Off) + .level(log::LevelFilter::Info) + .chain(fern::Output::call(console_log::log)) + .apply(); + match logger { + Ok(_) => log::info!("Logging initialized."), + Err(error) => eprintln!("Error initializing logging: {:?}", error), + } +} + +#[derive(Serialize, Deserialize)] +pub struct OptimizerConfig { + pub optimizer_type: String, // "adamw" or "sgd" + pub lr: f64, + #[serde(default = "default_beta1")] + pub beta1: f64, + #[serde(default = "default_beta2")] + pub beta2: f64, + #[serde(default = "default_eps")] + pub eps: f64, + #[serde(default = "default_weight_decay")] + pub weight_decay: f64, + #[serde(default = "default_momentum")] + pub momentum: f64, // For SGD + #[serde(default = "default_scheduler_type")] + pub scheduler_type: String, // "none", "constant", "linear", "cosine" + #[serde(default = "default_scheduler_factor")] + pub scheduler_factor: f64, // For constant/linear scheduler + #[serde(default = "default_scheduler_steps")] + pub scheduler_steps: usize, // For all schedulers + #[serde(default = "default_scheduler_eta_min")] + pub scheduler_eta_min: f64, // For cosine scheduler +} + +fn default_beta1() -> f64 { + 0.9 +} +fn default_beta2() -> f64 { + 0.999 +} +fn default_eps() -> f64 { + 1e-8 +} +fn default_weight_decay() -> f64 { + 0.0 +} +fn default_momentum() -> f64 { + 0.0 +} +fn default_scheduler_type() -> String { + "none".to_string() +} +fn default_scheduler_factor() -> f64 { + 1.0 +} +fn default_scheduler_steps() -> usize { + 1000 +} +fn default_scheduler_eta_min() -> f64 { + 0.0 +} + +// ------------------------------------------------------------------- +// Trainer configuration that is deserializable from JavaScript. +// Note the additional fields for batch size and dataset selection. +// ------------------------------------------------------------------- +#[derive(Serialize, Deserialize)] +pub struct TrainerConfig { + pub vocab_size: usize, + pub n_embd: usize, + pub n_layer: usize, + pub n_head: usize, + pub block_size: usize, + pub batch_size: usize, + /// A string specifying which dataset to use: "two_sum" (default), "zeros", or "tinystories". + pub dataset: String, + #[serde(default = "default_optimizer_config")] + pub optimizer: OptimizerConfig, + #[serde(default = "default_activation")] + pub activation: String, + #[serde(default = "default_attention_only")] + pub attention_only: bool, + #[serde(default = "default_positional_encoding")] + pub positional_encoding: String, + #[serde(default = "default_layernorm_position")] + pub layernorm_position: String, + #[serde(default = "default_seed")] + pub seed: Option, +} + +fn default_optimizer_config() -> OptimizerConfig { + OptimizerConfig { + optimizer_type: "adamw".to_string(), + lr: 1e-3, + beta1: default_beta1(), + beta2: default_beta2(), + eps: default_eps(), + weight_decay: default_weight_decay(), + momentum: default_momentum(), + scheduler_type: default_scheduler_type(), + scheduler_factor: default_scheduler_factor(), + scheduler_steps: default_scheduler_steps(), + scheduler_eta_min: default_scheduler_eta_min(), + } +} + +fn default_activation() -> String { + "gelu".to_string() +} + +fn default_attention_only() -> bool { + false +} + +fn default_positional_encoding() -> String { + "learned".to_string() +} + +fn default_layernorm_position() -> String { + "pre".to_string() +} + +fn default_seed() -> Option { + None +} + +fn string_to_activation(s: &str) -> Activation { + match s.to_lowercase().as_str() { + "gelu" => Activation::Gelu, + "relu" => Activation::Relu, + "relu2" => Activation::Relu2, + "silu" => Activation::Silu, + "sigmoid" => Activation::Sigmoid, + "swiglu" => Activation::Swiglu, + _ => Activation::Relu2, // Default to Relu2 for unrecognized values + } +} + +// ------------------------------------------------------------------- +// Batch and BatchHandle definitions. +// The inner Batch holds the (input, target) tensors on the current device. +// ------------------------------------------------------------------- +pub struct Batch { + pub input: Tensor, + pub target: Tensor, +} + +#[wasm_bindgen(js_name = "Batch")] +pub struct BatchHandle { + inner: Batch, +} + +#[wasm_bindgen] +impl BatchHandle { + /// Asynchronously converts the underlying batch data into a JS object. + /// This operation transfers the tensors to the CPU and then converts them into Vec. + #[wasm_bindgen] + pub async fn to_js(&self) -> Result { + // Transfer the input and target tensors to CPU. + let input_cpu = self + .inner + .input + .to(&Device::CPU) + .await + .map_err(|e| e.to_string())?; + let target_cpu = self + .inner + .target + .to(&Device::CPU) + .await + .map_err(|e| e.to_string())?; + // Convert the tensors to Vec. + let input_vec = input_cpu.to_vec::().map_err(|e| e.to_string())?; + let target_vec = target_cpu.to_vec::().map_err(|e| e.to_string())?; + // Create a JS-friendly object. + let batch_obj = serde_json::json!({ + "input": input_vec, + "target": target_vec, + }); + serde_wasm_bindgen::to_value(&batch_obj).map_err(|e| e.to_string().into()) + } +} + +// Updated to: + +enum BatcherType<'a> { + ToyTwoSum( + Batcher< + IterResult2< + ratchet_datasets::nlp::toy::ToyTaskIter, + >, + >, + ), + ToyZeros( + Batcher< + IterResult2< + ratchet_datasets::nlp::toy::ToyTaskIter, + >, + >, + ), + ToySort( + Batcher< + IterResult2< + ratchet_datasets::nlp::toy::ToyTaskIter, + >, + >, + ), + ToyAdd( + Batcher< + IterResult2< + ratchet_datasets::nlp::toy::ToyTaskIter, + >, + >, + ), + ToyCount( + Batcher< + IterResult2< + ratchet_datasets::nlp::toy::ToyTaskIter, + >, + >, + ), + ToySlapjack( + Batcher< + IterResult2< + ratchet_datasets::nlp::toy::ToyTaskIter, + >, + >, + ), + ToyModAdd( + Batcher< + IterResult2< + ratchet_datasets::nlp::toy::ToyTaskIter, + >, + >, + ), + TinyStories(Batcher>>), +} + +/// An enum for optimizer configurations +#[derive(Debug, Clone)] +pub enum OptimizerConfigEnum { + AdamW(ParamsAdamW), + SGD(f64), +} + +/// An enum that wraps the concrete optimizer types +pub enum OptimizerEnum { + AdamW(AdamW), + SGD(SGD), +} + +#[async_trait] +impl Optimizer for OptimizerEnum { + type Config = OptimizerConfigEnum; + + fn new(vars: Vec<(Option, Var)>, config: Self::Config) -> anyhow::Result { + match config { + OptimizerConfigEnum::AdamW(params) => Ok(Self::AdamW(AdamW::new(vars, params)?)), + OptimizerConfigEnum::SGD(params) => Ok(Self::SGD(SGD::new(vars, params)?)), + } + } + + async fn step(&mut self, grads: &GradStore, device: &Device) -> anyhow::Result<()> { + match self { + OptimizerEnum::AdamW(opt) => opt.step(grads, device).await, + OptimizerEnum::SGD(opt) => opt.step(grads, device).await, + } + } + + fn learning_rate(&self) -> f64 { + match self { + OptimizerEnum::AdamW(opt) => opt.learning_rate(), + OptimizerEnum::SGD(opt) => opt.learning_rate(), + } + } + + fn set_learning_rate(&mut self, lr: f64) { + match self { + OptimizerEnum::AdamW(opt) => opt.set_learning_rate(lr), + OptimizerEnum::SGD(opt) => opt.set_learning_rate(lr), + } + } +} + +// ------------------------------------------------------------------- +// The Trainer manages the model, optimizer, device, and dataset batcher. +// ------------------------------------------------------------------- +#[wasm_bindgen] +pub struct Trainer { + model: GPT2, + optimizer: Box + Send + Sync>, + device: Device, + config: TrainerConfig, + batcher: BatcherType<'static>, +} + +#[wasm_bindgen] +impl Trainer { + /// Create a new Trainer from a JavaScript configuration object. + /// This async constructor initializes the device, model, optimizer, + /// and dataset iterator (wrapped in a Batcher) based on the config. + #[wasm_bindgen(constructor)] + pub async fn new(config: JsValue) -> Result { + // Deserialize the configuration from JS. + let cfg: TrainerConfig = + serde_wasm_bindgen::from_value(config).map_err(|e| JsValue::from(e.to_string()))?; + + // Request a GPU device. + let device = Device::request_device(DeviceRequest::GPU) + .await + .map_err(|e| e.to_string())?; + + // Set the seed if provided + if let Some(seed) = cfg.seed { + device.set_seed(seed); + } + + // Set up the variable map and variable builder for model initialization. + let varmap = VarMap::new(); + let vb = VarBuilder::from_varmap(&varmap, DType::F32, &device); + + // Create the GPT2 model configuration. + let gpt2_config = Config { + vocab_size: cfg.vocab_size, + hidden_act: string_to_activation(&cfg.activation), + n_embd: cfg.n_embd, + n_layer: cfg.n_layer, + n_head: cfg.n_head, + block_size: cfg.block_size, + attention_only: cfg.attention_only, + positional_encoding: match cfg.positional_encoding.to_lowercase().as_str() { + "rope" => PositionalEncoding::RoPE, + "alibi" => PositionalEncoding::ALiBi, + "sinusoidal" => PositionalEncoding::Sinusoidal, + _ => PositionalEncoding::Learned, + }, + layernorm_position: match cfg.layernorm_position.to_lowercase().as_str() { + "pre" => LayerNormPosition::Pre, + "post" => LayerNormPosition::Post, + _ => LayerNormPosition::Pre, + }, + }; + // Initialize the GPT2 model. + let model = GPT2::new(&gpt2_config, vb) + .await + .map_err(|e| e.to_string())?; + + // Set up the optimizer based on the configuration + let vars = varmap + .all_labeled_vars() + .iter() + .map(|(label, var)| (Some(label.to_owned()), var.to_owned())) + .collect::>(); + + let optimizer_config = match cfg.optimizer.optimizer_type.to_lowercase().as_str() { + "sgd" => OptimizerConfigEnum::SGD(cfg.optimizer.lr), + _ => OptimizerConfigEnum::AdamW(ParamsAdamW { + lr: cfg.optimizer.lr, + beta1: cfg.optimizer.beta1, + beta2: cfg.optimizer.beta2, + eps: cfg.optimizer.eps, + weight_decay: cfg.optimizer.weight_decay, + }), + }; + + let base_optimizer = + OptimizerEnum::new(vars, optimizer_config).map_err(|e| e.to_string())?; + + // Wrap the optimizer in the selected scheduler + let optimizer: Box + Send + Sync> = + match cfg.optimizer.scheduler_type.to_lowercase().as_str() { + "constant" => Box::new(ConstantLR::new( + base_optimizer, + cfg.optimizer.scheduler_factor, + cfg.optimizer.scheduler_steps, + )), + "linear" => Box::new(LinearLR::new( + base_optimizer, + 1.0, // start_factor + cfg.optimizer.scheduler_factor, // end_factor + cfg.optimizer.scheduler_steps, + )), + "cosine" => Box::new(CosineAnnealingLR::new( + base_optimizer, + cfg.optimizer.scheduler_steps, + cfg.optimizer.scheduler_eta_min, + )), + _ => Box::new(NoopScheduler::new(base_optimizer)), // Default to no scheduler + }; + + // Based on the config.dataset value, choose the dataset iterator and wrap it in a Batcher. + // Supported options (case-insensitive): "two_sum" (default), "zeros", "tinystories", "sort", "add", "count", "slapjack", "mod_add" + let batcher = match cfg.dataset.to_lowercase().as_str() { + "zeros" => { + let task = ZerosTask::new(cfg.block_size); + let dataset_iter = ToyTaskIter::new(task, device.clone()); + BatcherType::ToyZeros(Batcher::new_r2(dataset_iter).batch_size(cfg.batch_size)) + } + "sort" => { + let task = SortTask::new(20, cfg.seed); + let dataset_iter = ToyTaskIter::new(task, device.clone()); + BatcherType::ToySort(Batcher::new_r2(dataset_iter).batch_size(cfg.batch_size)) + } + "add" => { + let task = AddTask::new(1000, cfg.seed); + let dataset_iter = ToyTaskIter::new(task, device.clone()); + BatcherType::ToyAdd(Batcher::new_r2(dataset_iter).batch_size(cfg.batch_size)) + } + "count" => { + let task = CountTask::new(10, 'd', cfg.seed); + let dataset_iter = ToyTaskIter::new(task, device.clone()); + BatcherType::ToyCount(Batcher::new_r2(dataset_iter).batch_size(cfg.batch_size)) + } + "slapjack" => { + let task = SlapjackTask::new(48, cfg.seed); + let dataset_iter = ToyTaskIter::new(task, device.clone()); + BatcherType::ToySlapjack(Batcher::new_r2(dataset_iter).batch_size(cfg.batch_size)) + } + "mod_add" => { + let task = ModAddTask::new(113, cfg.seed); + let dataset_iter = ToyTaskIter::new(task, device.clone()); + BatcherType::ToyModAdd(Batcher::new_r2(dataset_iter).batch_size(cfg.batch_size)) + } + // "tinystories" => { + // let dataset = TinyStoriesDataset::new(); + // let dataset_iter = TinyStoriesDatasetRandomIter::new(dataset, device.clone()); + // BatcherEnum::TinyStories(Batcher::new_r2(dataset_iter).batch_size(cfg.batch_size)) + // } + // Default (including "two_sum" and unrecognized values): use the TwoSum task. + _ => { + let task = TwoSumTask::new(5, 5, cfg.seed); + let dataset_iter = ToyTaskIter::new(task, device.clone()); + BatcherType::ToyTwoSum(Batcher::new_r2(dataset_iter).batch_size(cfg.batch_size)) + } + }; + + Ok(Trainer { + model, + optimizer, + device, + config: cfg, + batcher, + }) + } + + /// Asynchronously retrieve the next batch as a BatchHandle. + /// The returned BatchHandle wraps a Batch consisting of (input, target) tensors, + /// which reside on the device. + #[wasm_bindgen] + pub async fn next_batch(&mut self) -> Result { + // Pull the next (input, target) tuple from the dataset iterator. + let (input, target) = match &mut self.batcher { + BatcherType::ToyTwoSum(batcher) => batcher + .next() + .ok_or_else(|| { + JsValue::from_str("No more data available in the ToyTwoSum dataset") + })? + .map_err(|e| e.to_string())?, + BatcherType::ToyZeros(batcher) => batcher + .next() + .ok_or_else(|| JsValue::from_str("No more data available in the ToyZeros dataset"))? + .map_err(|e| e.to_string())?, + BatcherType::ToySort(batcher) => batcher + .next() + .ok_or_else(|| JsValue::from_str("No more data available in the ToySort dataset"))? + .map_err(|e| e.to_string())?, + BatcherType::ToyAdd(batcher) => batcher + .next() + .ok_or_else(|| JsValue::from_str("No more data available in the ToyAdd dataset"))? + .map_err(|e| e.to_string())?, + BatcherType::ToyCount(batcher) => batcher + .next() + .ok_or_else(|| JsValue::from_str("No more data available in the ToyCount dataset"))? + .map_err(|e| e.to_string())?, + BatcherType::ToySlapjack(batcher) => batcher + .next() + .ok_or_else(|| { + JsValue::from_str("No more data available in the ToySlapjack dataset") + })? + .map_err(|e| e.to_string())?, + BatcherType::ToyModAdd(batcher) => batcher + .next() + .ok_or_else(|| { + JsValue::from_str("No more data available in the ToyModAdd dataset") + })? + .map_err(|e| e.to_string())?, + BatcherType::TinyStories(batcher) => batcher + .next() + .ok_or_else(|| { + JsValue::from_str("No more data available in the TinyStories dataset") + })? + .map_err(|e| e.to_string())?, + }; + Ok(BatchHandle { + inner: Batch { input, target }, + }) + } + + /// Asynchronously train on the provided batch. + /// Here we assume that `batch.input` and `batch.target` are already on the correct device. + /// They are passed directly into the model; loss, backpropagation, and the optimizer step + /// are performed, and the scalar loss value is returned. + #[wasm_bindgen] + pub async fn train_step(&mut self, batch_handle: BatchHandle) -> Result { + // Extract the batch from the handle. + let batch = batch_handle.inner; + + // Forward pass: compute logits from the model. + let (logits, attn_masks) = self + .model + .schedule(GPT2Input { + x: batch.input, + index_pos: 0, + }) + .map_err(|e| e.to_string())?; + + // Flatten the logits and targets. + let logits_flat = logits.flatten_to(1).map_err(|e| e.to_string())?; + let target_flat = batch.target.flatten_to(1).map_err(|e| e.to_string())?; + + // Compute the cross-entropy loss. + let loss = cross_entropy(logits_flat, target_flat).map_err(|e| e.to_string())?; + + // Backpropagate to compute gradients. + let grads = loss.backward().map_err(|e| e.to_string())?; + + // Run an optimizer step (updating model parameters). + self.optimizer + .step(&grads, &self.device) + .await + .map_err(|e| e.to_string())?; + + log::debug!("Done training step"); + + // Transfer the loss to CPU and extract its scalar value. + let loss_cpu = loss.to(&Device::CPU).await.map_err(|e| e.to_string())?; + let loss_vec = loss_cpu.to_vec::().map_err(|e| e.to_string())?; + + // Transfer attention masks to CPU and get their shape and flattened data + let attn_masks_cpu = attn_masks + .to(&Device::CPU) + .await + .map_err(|e| e.to_string())?; + let attn_masks_shape = attn_masks_cpu.shape().to_vec(); + let attn_masks_data = attn_masks_cpu.to_vec::().map_err(|e| e.to_string())?; + + // Create a JS-friendly object with both loss and attention masks + let result = serde_json::json!({ + "loss": loss_vec[0], + "learning_rate": self.optimizer.get_lr(), + "attn_masks": { + "data": attn_masks_data, + "shape": attn_masks_shape, + } + }); + + serde_wasm_bindgen::to_value(&result).map_err(|e| e.to_string().into()) + } + + #[wasm_bindgen] + pub fn usage_bytes(&self) -> u64 { + self.device.try_gpu().unwrap().usage_bytes() + } +} + +/// A scheduler that does nothing, just passes through to the underlying optimizer. +/// This is used when no scheduler is selected. +pub struct NoopScheduler { + core: LRSchedulerCore, +} + +impl NoopScheduler { + pub fn new(optimizer: O) -> Self { + Self { + core: LRSchedulerCore::new(optimizer), + } + } +} + +impl LRScheduler for NoopScheduler { + fn optimizer_mut(&mut self) -> &mut O { + &mut self.core.optimizer + } + + fn base_lr(&self) -> f64 { + self.core.base_lr + } + + fn step_count(&self) -> usize { + self.core.step_count + } + + fn set_step_count(&mut self, count: usize) { + self.core.step_count = count; + } + + fn compute_lr(&self) -> f64 { + self.core.base_lr + } +} + +#[cfg(all(test, target_arch = "wasm32"))] +mod tests { + use crate::test_utils::log_init; + + use super::*; + use ratchet::{ + shape, + test_utils::{to_vec0_round, to_vec1_round}, + }; + use ratchet_nn::Linear; + use wasm_bindgen_test::*; + + wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + + #[wasm_bindgen_test] + async fn train_browser() -> Result<(), JsValue> { + log_init(); + let json_config = r#"{ + "vocab_size": 1024, + "n_embd": 768, + "n_layer": 6, + "n_head": 6, + "block_size": 24, + "batch_size": 1, + "dataset": "two_sum", + "optimizer": { + "lr": 1e-3 + } + }"#; + // Parse the JSON string into a TrainerConfig first to validate it + let config: TrainerConfig = + serde_json::from_str(json_config).map_err(|e| JsValue::from(e.to_string()))?; + // Convert to JsValue + let config_js = + serde_wasm_bindgen::to_value(&config).map_err(|e| JsValue::from(e.to_string()))?; + + let mut trainer = Trainer::new(config_js).await.unwrap(); + for i in 0..10 { + let batch = trainer.next_batch().await.unwrap(); + let loss = trainer.train_step(batch).await.unwrap(); + log::error!("step {}: loss: {:?}", i, loss); + } + // log::error!("config_js: {:?}", config_js); + Ok(()) + } +} From d4d55ae874ca108a24c3d8fd1e5ff41441531cce Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 16 Feb 2025 17:03:50 -0700 Subject: [PATCH 002/590] Add ratchet-train-toy demo --- examples/ratchet-train-toy/.gitignore | 23 + examples/ratchet-train-toy/.npmrc | 1 + examples/ratchet-train-toy/.prettierignore | 4 + examples/ratchet-train-toy/.prettierrc | 17 + examples/ratchet-train-toy/README.md | 1 + examples/ratchet-train-toy/eslint.config.js | 34 + examples/ratchet-train-toy/package.json | 43 + examples/ratchet-train-toy/src/app.css | 1 + examples/ratchet-train-toy/src/app.d.ts | 13 + examples/ratchet-train-toy/src/app.html | 12 + .../lib/components/ActivationPicker.svelte | 79 ++ .../src/lib/components/BaseSlider.svelte | 96 ++ .../src/lib/components/LogSlider.svelte | 127 ++ .../src/lib/components/Slider.svelte | 25 + .../src/lib/components/TickSlider.svelte | 69 ++ examples/ratchet-train-toy/src/lib/index.ts | 1 + .../src/routes/+layout.svelte | 6 + .../ratchet-train-toy/src/routes/+layout.ts | 1 + .../ratchet-train-toy/src/routes/+page.svelte | 1033 +++++++++++++++++ examples/ratchet-train-toy/src/trainWorker.ts | 109 ++ examples/ratchet-train-toy/svelte.config.js | 15 + examples/ratchet-train-toy/tsconfig.json | 19 + examples/ratchet-train-toy/vite.config.ts | 18 + 23 files changed, 1747 insertions(+) create mode 100644 examples/ratchet-train-toy/.gitignore create mode 100644 examples/ratchet-train-toy/.npmrc create mode 100644 examples/ratchet-train-toy/.prettierignore create mode 100644 examples/ratchet-train-toy/.prettierrc create mode 100644 examples/ratchet-train-toy/README.md create mode 100644 examples/ratchet-train-toy/eslint.config.js create mode 100644 examples/ratchet-train-toy/package.json create mode 100644 examples/ratchet-train-toy/src/app.css create mode 100644 examples/ratchet-train-toy/src/app.d.ts create mode 100644 examples/ratchet-train-toy/src/app.html create mode 100644 examples/ratchet-train-toy/src/lib/components/ActivationPicker.svelte create mode 100644 examples/ratchet-train-toy/src/lib/components/BaseSlider.svelte create mode 100644 examples/ratchet-train-toy/src/lib/components/LogSlider.svelte create mode 100644 examples/ratchet-train-toy/src/lib/components/Slider.svelte create mode 100644 examples/ratchet-train-toy/src/lib/components/TickSlider.svelte create mode 100644 examples/ratchet-train-toy/src/lib/index.ts create mode 100644 examples/ratchet-train-toy/src/routes/+layout.svelte create mode 100644 examples/ratchet-train-toy/src/routes/+layout.ts create mode 100644 examples/ratchet-train-toy/src/routes/+page.svelte create mode 100644 examples/ratchet-train-toy/src/trainWorker.ts create mode 100644 examples/ratchet-train-toy/svelte.config.js create mode 100644 examples/ratchet-train-toy/tsconfig.json create mode 100644 examples/ratchet-train-toy/vite.config.ts diff --git a/examples/ratchet-train-toy/.gitignore b/examples/ratchet-train-toy/.gitignore new file mode 100644 index 00000000..3b462cb0 --- /dev/null +++ b/examples/ratchet-train-toy/.gitignore @@ -0,0 +1,23 @@ +node_modules + +# Output +.output +.vercel +.netlify +.wrangler +/.svelte-kit +/build + +# OS +.DS_Store +Thumbs.db + +# Env +.env +.env.* +!.env.example +!.env.test + +# Vite +vite.config.js.timestamp-* +vite.config.ts.timestamp-* diff --git a/examples/ratchet-train-toy/.npmrc b/examples/ratchet-train-toy/.npmrc new file mode 100644 index 00000000..b6f27f13 --- /dev/null +++ b/examples/ratchet-train-toy/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/examples/ratchet-train-toy/.prettierignore b/examples/ratchet-train-toy/.prettierignore new file mode 100644 index 00000000..ab78a95d --- /dev/null +++ b/examples/ratchet-train-toy/.prettierignore @@ -0,0 +1,4 @@ +# Package Managers +package-lock.json +pnpm-lock.yaml +yarn.lock diff --git a/examples/ratchet-train-toy/.prettierrc b/examples/ratchet-train-toy/.prettierrc new file mode 100644 index 00000000..b5132629 --- /dev/null +++ b/examples/ratchet-train-toy/.prettierrc @@ -0,0 +1,17 @@ +{ + "useTabs": true, + "singleQuote": true, + "trailingComma": "none", + "printWidth": 100, + "plugins": [ + "prettier-plugin-svelte" + ], + "overrides": [ + { + "files": "*.svelte", + "options": { + "parser": "svelte" + } + } + ] +} diff --git a/examples/ratchet-train-toy/README.md b/examples/ratchet-train-toy/README.md new file mode 100644 index 00000000..22309833 --- /dev/null +++ b/examples/ratchet-train-toy/README.md @@ -0,0 +1 @@ +# ratchet-train-toy demo \ No newline at end of file diff --git a/examples/ratchet-train-toy/eslint.config.js b/examples/ratchet-train-toy/eslint.config.js new file mode 100644 index 00000000..e18311fa --- /dev/null +++ b/examples/ratchet-train-toy/eslint.config.js @@ -0,0 +1,34 @@ +import prettier from "eslint-config-prettier"; +import js from '@eslint/js'; +import { includeIgnoreFile } from '@eslint/compat'; +import svelte from 'eslint-plugin-svelte'; +import globals from 'globals'; +import { fileURLToPath } from 'node:url'; +import ts from 'typescript-eslint'; +const gitignorePath = fileURLToPath(new URL("./.gitignore", import.meta.url)); + +export default ts.config( + includeIgnoreFile(gitignorePath), + js.configs.recommended, + ...ts.configs.recommended, + ...svelte.configs["flat/recommended"], + prettier, + ...svelte.configs['flat/prettier'], + { + languageOptions: { + globals: { + ...globals.browser, + ...globals.node + } + } + }, + { + files: ["**/*.svelte"], + + languageOptions: { + parserOptions: { + parser: ts.parser + } + } + } +); diff --git a/examples/ratchet-train-toy/package.json b/examples/ratchet-train-toy/package.json new file mode 100644 index 00000000..375b32e4 --- /dev/null +++ b/examples/ratchet-train-toy/package.json @@ -0,0 +1,43 @@ +{ + "name": "ratchet-train-toy", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "prepare": "svelte-kit sync || echo ''", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", + "lint": "eslint . && prettier --check .", + "format": "prettier --write ." + }, + "dependencies": { + "@ratchet-ml/ratchet-web-train": "link:../../target/pkg/ratchet-web-train", + "chart.js": "^4.4.7", + "mathjs": "^14.2.1" + }, + "devDependencies": { + "@eslint/compat": "^1.2.5", + "@eslint/js": "^9.18.0", + "@sveltejs/adapter-static": "^3.0.8", + "@sveltejs/kit": "^2.16.0", + "@sveltejs/vite-plugin-svelte": "^5.0.0", + "@tailwindcss/vite": "^4.0.0", + "eslint": "^9.18.0", + "eslint-config-prettier": "^10.0.1", + "eslint-plugin-svelte": "^2.46.1", + "globals": "^15.14.0", + "prettier": "^3.4.2", + "prettier-plugin-svelte": "^3.3.3", + "svelte": "^5.0.0", + "svelte-check": "^4.0.0", + "tailwindcss": "^4.0.0", + "typescript": "^5.0.0", + "typescript-eslint": "^8.20.0", + "vite": "^6.1.0", + "vite-plugin-top-level-await": "^1.4.4", + "vite-plugin-wasm": "^3.4.1" + } +} \ No newline at end of file diff --git a/examples/ratchet-train-toy/src/app.css b/examples/ratchet-train-toy/src/app.css new file mode 100644 index 00000000..f86f709d --- /dev/null +++ b/examples/ratchet-train-toy/src/app.css @@ -0,0 +1 @@ +@import 'tailwindcss' diff --git a/examples/ratchet-train-toy/src/app.d.ts b/examples/ratchet-train-toy/src/app.d.ts new file mode 100644 index 00000000..da08e6da --- /dev/null +++ b/examples/ratchet-train-toy/src/app.d.ts @@ -0,0 +1,13 @@ +// See https://svelte.dev/docs/kit/types#app.d.ts +// for information about these interfaces +declare global { + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface PageState {} + // interface Platform {} + } +} + +export {}; diff --git a/examples/ratchet-train-toy/src/app.html b/examples/ratchet-train-toy/src/app.html new file mode 100644 index 00000000..5889edbd --- /dev/null +++ b/examples/ratchet-train-toy/src/app.html @@ -0,0 +1,12 @@ + + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/examples/ratchet-train-toy/src/lib/components/ActivationPicker.svelte b/examples/ratchet-train-toy/src/lib/components/ActivationPicker.svelte new file mode 100644 index 00000000..5ba7fe76 --- /dev/null +++ b/examples/ratchet-train-toy/src/lib/components/ActivationPicker.svelte @@ -0,0 +1,79 @@ + + +
+
+ +
+ +
+
+
+ + + + + + + + +
+
\ No newline at end of file diff --git a/examples/ratchet-train-toy/src/lib/components/BaseSlider.svelte b/examples/ratchet-train-toy/src/lib/components/BaseSlider.svelte new file mode 100644 index 00000000..eacc08b5 --- /dev/null +++ b/examples/ratchet-train-toy/src/lib/components/BaseSlider.svelte @@ -0,0 +1,96 @@ + + +
+ +
+ +
+
+ + +
+
+
+ + \ No newline at end of file diff --git a/examples/ratchet-train-toy/src/lib/components/LogSlider.svelte b/examples/ratchet-train-toy/src/lib/components/LogSlider.svelte new file mode 100644 index 00000000..63372085 --- /dev/null +++ b/examples/ratchet-train-toy/src/lib/components/LogSlider.svelte @@ -0,0 +1,127 @@ + + + +
+ {formatNumber(logValue)} +
+ +
+ {#each ticks as tick} + {@const position = `${((tick.value - minExp) / (maxExp - minExp)) * 100}%`} + {@const isSnapPoint = snapPoints.includes(tick.value)} +
+
+ {#if tick.type === 'major'} +
+ {@html tick.label} +
+ {/if} +
+ {/each} +
+ + + \ No newline at end of file diff --git a/examples/ratchet-train-toy/src/lib/components/Slider.svelte b/examples/ratchet-train-toy/src/lib/components/Slider.svelte new file mode 100644 index 00000000..1ba7c119 --- /dev/null +++ b/examples/ratchet-train-toy/src/lib/components/Slider.svelte @@ -0,0 +1,25 @@ + + + +
+ {formatter(value)} +
+ +
\ No newline at end of file diff --git a/examples/ratchet-train-toy/src/lib/components/TickSlider.svelte b/examples/ratchet-train-toy/src/lib/components/TickSlider.svelte new file mode 100644 index 00000000..02fc9868 --- /dev/null +++ b/examples/ratchet-train-toy/src/lib/components/TickSlider.svelte @@ -0,0 +1,69 @@ + + + +
+ {formatter(value)} +
+ +
+ {#each ticks as tick} + {@const position = `${((tick.value - min) / (max - min)) * 100}%`} +
+
+
+ {/each} +
+ + + \ No newline at end of file diff --git a/examples/ratchet-train-toy/src/lib/index.ts b/examples/ratchet-train-toy/src/lib/index.ts new file mode 100644 index 00000000..856f2b6c --- /dev/null +++ b/examples/ratchet-train-toy/src/lib/index.ts @@ -0,0 +1 @@ +// place files you want to import through the `$lib` alias in this folder. diff --git a/examples/ratchet-train-toy/src/routes/+layout.svelte b/examples/ratchet-train-toy/src/routes/+layout.svelte new file mode 100644 index 00000000..9b776b77 --- /dev/null +++ b/examples/ratchet-train-toy/src/routes/+layout.svelte @@ -0,0 +1,6 @@ + + +{@render children()} diff --git a/examples/ratchet-train-toy/src/routes/+layout.ts b/examples/ratchet-train-toy/src/routes/+layout.ts new file mode 100644 index 00000000..189f71e2 --- /dev/null +++ b/examples/ratchet-train-toy/src/routes/+layout.ts @@ -0,0 +1 @@ +export const prerender = true; diff --git a/examples/ratchet-train-toy/src/routes/+page.svelte b/examples/ratchet-train-toy/src/routes/+page.svelte new file mode 100644 index 00000000..056e425b --- /dev/null +++ b/examples/ratchet-train-toy/src/routes/+page.svelte @@ -0,0 +1,1033 @@ + + +
+ {#if !hasWebGPU} + + {/if} +
+
+
+

toy transformer

+ +
+
+

train loss: {loss.toFixed(4)}

+

val loss: -.----

+
+
+ +
+ + + {#if isTraining} +
+
+ + +
+ + {#if showAttention && attentionCanvases.length > 0} + +
+ {#each attentionCanvases as layerCanvases, layerIdx} +
+

Layer {layerIdx + 1}

+
+ {#each layerCanvases as canvas, headIdx} +
+ +
Head {headIdx + 1}
+
+ {/each} +
+
+ {/each} +
+ {/if} +
+ {/if} +
+ +
+
+ +
+
+ +
+ +
+
+ {#each messages as message} +
+ [{message.timestamp}] + {message.text} +
+ {/each} +
+
+
+
+
+ + {#if isTraining} + + {/if} +
+
+

Dataset

+
+ +
+ +
+ +
+
+ +
+
+
+

Model Architecture

+ + + + +
+ `${v} per head`} + /> +
+ +
+ + +
+ +
+ +
+ +
+ + + +
+
+
+ +
+ +
+ +
+ + + +
+
+
+ +
+
+ + +
+
+
+ +
+

Optimizer

+ +
+ +
+ +
+ + + +
+
+
+ + + + {#if optimizer_type === 'adamw'} + + + {#if showAdvanced} +
+
+
+ + +
+ +
+ + +
+
+ +
+ +
+ +
+ +
+
+ {/if} + {/if} + +
+ +
+ +
+ + + +
+
+
+ + {#if scheduler_type !== 'none'} +
+
+ + +
+ + {#if scheduler_type === 'constant' || scheduler_type === 'linear'} +
+ + +
+ {/if} + + {#if scheduler_type === 'cosine'} +
+ + +
+ {/if} +
+ {/if} +
+ +
+

Training Options

+ +
+ +
+ +
+ + +
+
+
+
+
diff --git a/examples/ratchet-train-toy/src/trainWorker.ts b/examples/ratchet-train-toy/src/trainWorker.ts new file mode 100644 index 00000000..babcadb2 --- /dev/null +++ b/examples/ratchet-train-toy/src/trainWorker.ts @@ -0,0 +1,109 @@ +import { Trainer } from '@ratchet-ml/ratchet-web-train'; + +let trainer: Trainer; +let sessionCounter = 0; +let currentSession = 0; +const trainingSessions: Record = {}; + +export interface TrainerConfig { + vocab_size: number; + n_embd: number; + n_layer: number; + n_head: number; + block_size: number; + batch_size: number; + dataset: string; + activation: string; + attention_only: boolean; + position_encoding: string; + seed?: number; + layernorm_position: string; + optimizer: { + optimizer_type: string; + lr: number; + beta1: number; + beta2: number; + eps: number; + weight_decay: number; + momentum: number; + scheduler_type: string; + scheduler_factor: number; + scheduler_steps: number; + scheduler_eta_min: number; + }; +} + +function markStopTraining() { + Object.keys(trainingSessions).forEach((key) => { + trainingSessions[Number(key)] = false; + }); +} + +async function initializeTrainer(config: TrainerConfig) { + // Stop all existing training sessions + markStopTraining(); + + trainer = await new Trainer(config); + self.postMessage({ type: 'modelReady' }); + currentSession = sessionCounter++; + trainingSessions[currentSession] = true; + trainingLoop(currentSession); +} + +async function trainingLoop(sessionId: number) { + if (!trainer) { + console.error('Trainer not initialized'); + return; + } + while (trainingSessions[sessionId]) { + // Skip if this isn't the current session anymore + if (sessionId !== currentSession) { + break; + } + + try { + const batch = await trainer.next_batch(); + const result: Map = await trainer.train_step(batch); + const attn_masks = result.get('attn_masks') as Map; + const usage_bytes = trainer.usage_bytes(); + + // Only send message if this is still the current session + if (sessionId === currentSession) { + self.postMessage({ + type: 'step', + loss: result.get('loss'), + learning_rate: result.get('learning_rate'), + usage_bytes, + attn_masks: { + data: attn_masks.get('data'), + shape: attn_masks.get('shape') + } + }); + } + } catch (error: Error | unknown) { + console.error('Training error in worker:', error); + if (sessionId === currentSession) { + self.postMessage({ + type: 'error', + error: error instanceof Error ? error.message : String(error) + }); + } + break; + } + // Yield to keep the worker responsive + await new Promise((resolve) => setTimeout(resolve, 0)); + } +} + +self.onmessage = async (e: MessageEvent) => { + if (e.data.type === 'stop') { + markStopTraining(); + return; + } + // If we receive a configuration object, initialize a new trainer + if (typeof e.data === 'object') { + await initializeTrainer(e.data); + } +}; + +self.postMessage({ type: 'ready' }); diff --git a/examples/ratchet-train-toy/svelte.config.js b/examples/ratchet-train-toy/svelte.config.js new file mode 100644 index 00000000..92d0e09f --- /dev/null +++ b/examples/ratchet-train-toy/svelte.config.js @@ -0,0 +1,15 @@ +import adapter from "@sveltejs/adapter-static"; +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + // Consult https://svelte.dev/docs/kit/integrations + // for more information about preprocessors + preprocess: vitePreprocess(), + + kit: { + adapter: adapter() + } +}; + +export default config; diff --git a/examples/ratchet-train-toy/tsconfig.json b/examples/ratchet-train-toy/tsconfig.json new file mode 100644 index 00000000..0b2d8865 --- /dev/null +++ b/examples/ratchet-train-toy/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "moduleResolution": "bundler" + } + // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias + // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files + // + // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes + // from the referenced tsconfig.json - TypeScript does not merge them in +} diff --git a/examples/ratchet-train-toy/vite.config.ts b/examples/ratchet-train-toy/vite.config.ts new file mode 100644 index 00000000..6b6a3913 --- /dev/null +++ b/examples/ratchet-train-toy/vite.config.ts @@ -0,0 +1,18 @@ +import tailwindcss from '@tailwindcss/vite'; +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; +import wasm from 'vite-plugin-wasm'; +import topLevelAwait from 'vite-plugin-top-level-await'; + +export default defineConfig({ + plugins: [sveltekit(), tailwindcss(), wasm(), topLevelAwait()], + worker: { + format: 'es', + plugins: () => [wasm(), topLevelAwait()] + }, + esbuild: { + supported: { + 'top-level-await': true + } + } +}); From 1471004cc04837ba3ae9dbe287f299b6fbacd49c Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 16 Feb 2025 17:04:38 -0700 Subject: [PATCH 003/590] Target bundler with wasm builds --- justfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/justfile b/justfile index 5cf75054..003dd30f 100644 --- a/justfile +++ b/justfile @@ -5,9 +5,9 @@ install-pyo3: pyenv local 3.10.6 echo $(python --version) wasm CRATE: - node_modules/.bin/wasm-pack build -s ratchet --target web -d `pwd`/target/pkg/{{CRATE}} --out-name {{CRATE}} ./crates/{{CRATE}} --release + node_modules/.bin/wasm-pack build -s ratchet --target bundler -d `pwd`/target/pkg/{{CRATE}} --out-name {{CRATE}} ./crates/{{CRATE}} --release wasm-dbg CRATE: - node_modules/.bin/wasm-pack build -s ratchet --target web -d `pwd`/target/pkg/{{CRATE}} --out-name {{CRATE}} ./crates/{{CRATE}} --dev + node_modules/.bin/wasm-pack build -s ratchet --target bundler -d `pwd`/target/pkg/{{CRATE}} --out-name {{CRATE}} ./crates/{{CRATE}} --dev wasm-test CRATE BROWSER: cp ./config/webdriver-macos.json ./crates/{{CRATE}}/webdriver.json node_modules/.bin/wasm-pack test --{{BROWSER}} --headless `pwd`/crates/{{CRATE}} From be56a276d53da2da110a509dffe5c0ec2415dca7 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 16 Feb 2025 17:05:10 -0700 Subject: [PATCH 004/590] Update pnpm lock (: --- pnpm-lock.yaml | 2251 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 2202 insertions(+), 49 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ba57a17d..3f4932cc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,9 +23,9 @@ importers: '@ffmpeg/util': specifier: ^0.12.1 version: 0.12.1 - '@ratchet-ml/ratchet-web': - specifier: link:../../target/pkg/ratchet-web - version: link:../../target/pkg/ratchet-web + '@ratchet-ml/ratchet-web-train': + specifier: link:../../target/pkg/ratchet-web-train + version: link:../../target/pkg/ratchet-web-train fix-webm-duration: specifier: ^1.0.5 version: 1.0.5 @@ -67,6 +67,76 @@ importers: specifier: ^5.3.3 version: 5.3.3 + examples/ratchet-train-toy: + dependencies: + chart.js: + specifier: ^4.4.7 + version: 4.4.7 + mathjs: + specifier: ^14.2.1 + version: 14.2.1 + devDependencies: + '@eslint/compat': + specifier: ^1.2.5 + version: 1.2.6(eslint@9.20.1) + '@eslint/js': + specifier: ^9.18.0 + version: 9.20.0 + '@sveltejs/adapter-static': + specifier: ^3.0.8 + version: 3.0.8(@sveltejs/kit@2.17.1) + '@sveltejs/kit': + specifier: ^2.16.0 + version: 2.17.1(@sveltejs/vite-plugin-svelte@5.0.3)(svelte@5.19.10)(vite@6.1.0) + '@sveltejs/vite-plugin-svelte': + specifier: ^5.0.0 + version: 5.0.3(svelte@5.19.10)(vite@6.1.0) + '@tailwindcss/vite': + specifier: ^4.0.0 + version: 4.0.6(vite@6.1.0) + eslint: + specifier: ^9.18.0 + version: 9.20.1 + eslint-config-prettier: + specifier: ^10.0.1 + version: 10.0.1(eslint@9.20.1) + eslint-plugin-svelte: + specifier: ^2.46.1 + version: 2.46.1(eslint@9.20.1)(svelte@5.19.10) + globals: + specifier: ^15.14.0 + version: 15.14.0 + prettier: + specifier: ^3.4.2 + version: 3.5.0 + prettier-plugin-svelte: + specifier: ^3.3.3 + version: 3.3.3(prettier@3.5.0)(svelte@5.19.10) + svelte: + specifier: ^5.0.0 + version: 5.19.10 + svelte-check: + specifier: ^4.0.0 + version: 4.1.4(svelte@5.19.10)(typescript@5.3.3) + tailwindcss: + specifier: ^4.0.0 + version: 4.0.6 + typescript: + specifier: ^5.0.0 + version: 5.3.3 + typescript-eslint: + specifier: ^8.20.0 + version: 8.24.0(eslint@9.20.1)(typescript@5.3.3) + vite: + specifier: ^6.1.0 + version: 6.1.0 + vite-plugin-top-level-await: + specifier: ^1.4.4 + version: 1.4.4(vite@6.1.0) + vite-plugin-wasm: + specifier: ^3.4.1 + version: 3.4.1(vite@6.1.0) + examples/ratchet-whisper: dependencies: '@ffmpeg/ffmpeg': @@ -126,6 +196,21 @@ packages: engines: {node: '>=10'} dev: true + /@ampproject/remapping@2.3.0: + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + dev: true + + /@babel/runtime@7.26.7: + resolution: {integrity: sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==} + engines: {node: '>=6.9.0'} + dependencies: + regenerator-runtime: 0.14.1 + dev: false + /@bedrock-layout/use-forwarded-ref@1.6.1(react@18.2.0): resolution: {integrity: sha512-GD9A9AFLzFNjr7k6fgerSqxfwDWl+wsPS11PErOKe1zkVz0y7RGC9gzlOiX/JrgpyB3NFHWIuGtoOQqifJQQpw==} peerDependencies: @@ -143,6 +228,318 @@ packages: react: 18.2.0 dev: false + /@esbuild/aix-ppc64@0.24.2: + resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm64@0.24.2: + resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm@0.24.2: + resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64@0.24.2: + resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64@0.24.2: + resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64@0.24.2: + resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.24.2: + resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.24.2: + resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64@0.24.2: + resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm@0.24.2: + resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32@0.24.2: + resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64@0.24.2: + resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el@0.24.2: + resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64@0.24.2: + resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64@0.24.2: + resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.24.2: + resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64@0.24.2: + resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-arm64@0.24.2: + resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64@0.24.2: + resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-arm64@0.24.2: + resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.24.2: + resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64@0.24.2: + resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64@0.24.2: + resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32@0.24.2: + resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64@0.24.2: + resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@eslint-community/eslint-utils@4.4.1(eslint@9.20.1): + resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 9.20.1 + eslint-visitor-keys: 3.4.3 + dev: true + + /@eslint-community/regexpp@4.12.1: + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + + /@eslint/compat@1.2.6(eslint@9.20.1): + resolution: {integrity: sha512-k7HNCqApoDHM6XzT30zGoETj+D+uUcZUb+IVAJmar3u6bvHf7hhHJcWx09QHj4/a2qrKZMWU0E16tvkiAdv06Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^9.10.0 + peerDependenciesMeta: + eslint: + optional: true + dependencies: + eslint: 9.20.1 + dev: true + + /@eslint/config-array@0.19.2: + resolution: {integrity: sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dependencies: + '@eslint/object-schema': 2.1.6 + debug: 4.4.0 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@eslint/core@0.10.0: + resolution: {integrity: sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dependencies: + '@types/json-schema': 7.0.15 + dev: true + + /@eslint/core@0.11.0: + resolution: {integrity: sha512-DWUB2pksgNEb6Bz2fggIy1wh6fGgZP4Xyy/Mt0QZPiloKKXerbqq9D3SBQTlCRYOrcRPu4vuz+CGjwdfqxnoWA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dependencies: + '@types/json-schema': 7.0.15 + dev: true + + /@eslint/eslintrc@3.2.0: + resolution: {integrity: sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dependencies: + ajv: 6.12.6 + debug: 4.4.0 + espree: 10.3.0 + globals: 14.0.0 + ignore: 5.3.1 + import-fresh: 3.3.1 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@eslint/js@9.20.0: + resolution: {integrity: sha512-iZA07H9io9Wn836aVTytRaNqh00Sad+EamwOVJT12GTLw1VGMFV/4JaME+JjLtr9fiGaoWgYnS54wrfWsSs4oQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dev: true + + /@eslint/object-schema@2.1.6: + resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dev: true + + /@eslint/plugin-kit@0.2.5: + resolution: {integrity: sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dependencies: + '@eslint/core': 0.10.0 + levn: 0.4.1 + dev: true + /@ffmpeg/ffmpeg@0.12.6: resolution: {integrity: sha512-4CuXDaqrCga5qBwVtiDDR45y65OGPYZd7VzwGCGz3QLdrQH7xaLYEjU19XL4DTCL0WnTSH8752b8Atyb1SiiLw==} engines: {node: '>=18.x'} @@ -160,6 +557,34 @@ packages: engines: {node: '>=18.x'} dev: false + /@humanfs/core@0.19.1: + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + dev: true + + /@humanfs/node@0.16.6: + resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} + engines: {node: '>=18.18.0'} + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.3.1 + dev: true + + /@humanwhocodes/module-importer@1.0.1: + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + dev: true + + /@humanwhocodes/retry@0.3.1: + resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} + engines: {node: '>=18.18'} + dev: true + + /@humanwhocodes/retry@0.4.1: + resolution: {integrity: sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==} + engines: {node: '>=18.18'} + dev: true + /@isaacs/cliui@8.0.2: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -177,7 +602,7 @@ packages: engines: {node: '>=6.0.0'} dependencies: '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.5.0 '@jridgewell/trace-mapping': 0.3.25 dev: true @@ -191,15 +616,15 @@ packages: engines: {node: '>=6.0.0'} dev: true - /@jridgewell/sourcemap-codec@1.4.15: - resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + /@jridgewell/sourcemap-codec@1.5.0: + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} dev: true /@jridgewell/trace-mapping@0.3.25: resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.5.0 dev: true /@jsdevtools/ez-spawn@3.0.4: @@ -212,6 +637,10 @@ packages: type-detect: 4.0.8 dev: true + /@kurkle/color@0.3.4: + resolution: {integrity: sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==} + dev: false + /@next/env@14.1.0: resolution: {integrity: sha512-Py8zIo+02ht82brwwhTg36iogzFqGLPXlRGKQw5s+qP/kMNc4MAyDeEwBKDijk6zTIbegEgu8Qy7C1LboslQAw==} dev: false @@ -221,7 +650,6 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - requiresBuild: true dev: false optional: true @@ -230,7 +658,6 @@ packages: engines: {node: '>= 10'} cpu: [x64] os: [darwin] - requiresBuild: true dev: false optional: true @@ -239,7 +666,6 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] - requiresBuild: true dev: false optional: true @@ -248,7 +674,6 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] - requiresBuild: true dev: false optional: true @@ -257,7 +682,6 @@ packages: engines: {node: '>= 10'} cpu: [x64] os: [linux] - requiresBuild: true dev: false optional: true @@ -266,7 +690,6 @@ packages: engines: {node: '>= 10'} cpu: [x64] os: [linux] - requiresBuild: true dev: false optional: true @@ -275,7 +698,6 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [win32] - requiresBuild: true dev: false optional: true @@ -284,7 +706,6 @@ packages: engines: {node: '>= 10'} cpu: [ia32] os: [win32] - requiresBuild: true dev: false optional: true @@ -293,7 +714,6 @@ packages: engines: {node: '>= 10'} cpu: [x64] os: [win32] - requiresBuild: true dev: false optional: true @@ -435,30 +855,536 @@ packages: /@pkgjs/parseargs@0.11.0: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - requiresBuild: true dev: true optional: true - /@swc/helpers@0.5.2: - resolution: {integrity: sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==} - dependencies: - tslib: 2.6.2 - dev: false - - /@types/node@20.11.24: - resolution: {integrity: sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==} - dependencies: - undici-types: 5.26.5 + /@polka/url@1.0.0-next.28: + resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==} dev: true - /@types/prop-types@15.7.11: - resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==} + /@rollup/plugin-virtual@3.0.2: + resolution: {integrity: sha512-10monEYsBp3scM4/ND4LNH5Rxvh3e/cVeL3jWTgZ2SrQ+BmUoQcopVQvnaMcOnykb1VkxUFuDAN+0FnpTFRy2A==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true dev: true - /@types/react-dom@18.2.19: - resolution: {integrity: sha512-aZvQL6uUbIJpjZk4U8JZGbau9KDeAwMfmhyWorxgBkqDIEf6ROjRozcmPIicqsUwPUjbkDfHKgGee1Lq65APcA==} - dependencies: - '@types/react': 18.2.61 + /@rollup/rollup-android-arm-eabi@4.34.6: + resolution: {integrity: sha512-+GcCXtOQoWuC7hhX1P00LqjjIiS/iOouHXhMdiDSnq/1DGTox4SpUvO52Xm+div6+106r+TcvOeo/cxvyEyTgg==} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-android-arm64@4.34.6: + resolution: {integrity: sha512-E8+2qCIjciYUnCa1AiVF1BkRgqIGW9KzJeesQqVfyRITGQN+dFuoivO0hnro1DjT74wXLRZ7QF8MIbz+luGaJA==} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-darwin-arm64@4.34.6: + resolution: {integrity: sha512-z9Ib+OzqN3DZEjX7PDQMHEhtF+t6Mi2z/ueChQPLS/qUMKY7Ybn5A2ggFoKRNRh1q1T03YTQfBTQCJZiepESAg==} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-darwin-x64@4.34.6: + resolution: {integrity: sha512-PShKVY4u0FDAR7jskyFIYVyHEPCPnIQY8s5OcXkdU8mz3Y7eXDJPdyM/ZWjkYdR2m0izD9HHWA8sGcXn+Qrsyg==} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-freebsd-arm64@4.34.6: + resolution: {integrity: sha512-YSwyOqlDAdKqs0iKuqvRHLN4SrD2TiswfoLfvYXseKbL47ht1grQpq46MSiQAx6rQEN8o8URtpXARCpqabqxGQ==} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-freebsd-x64@4.34.6: + resolution: {integrity: sha512-HEP4CgPAY1RxXwwL5sPFv6BBM3tVeLnshF03HMhJYCNc6kvSqBgTMmsEjb72RkZBAWIqiPUyF1JpEBv5XT9wKQ==} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm-gnueabihf@4.34.6: + resolution: {integrity: sha512-88fSzjC5xeH9S2Vg3rPgXJULkHcLYMkh8faix8DX4h4TIAL65ekwuQMA/g2CXq8W+NJC43V6fUpYZNjaX3+IIg==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm-musleabihf@4.34.6: + resolution: {integrity: sha512-wM4ztnutBqYFyvNeR7Av+reWI/enK9tDOTKNF+6Kk2Q96k9bwhDDOlnCUNRPvromlVXo04riSliMBs/Z7RteEg==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm64-gnu@4.34.6: + resolution: {integrity: sha512-9RyprECbRa9zEjXLtvvshhw4CMrRa3K+0wcp3KME0zmBe1ILmvcVHnypZ/aIDXpRyfhSYSuN4EPdCCj5Du8FIA==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm64-musl@4.34.6: + resolution: {integrity: sha512-qTmklhCTyaJSB05S+iSovfo++EwnIEZxHkzv5dep4qoszUMX5Ca4WM4zAVUMbfdviLgCSQOu5oU8YoGk1s6M9Q==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-loongarch64-gnu@4.34.6: + resolution: {integrity: sha512-4Qmkaps9yqmpjY5pvpkfOerYgKNUGzQpFxV6rnS7c/JfYbDSU0y6WpbbredB5cCpLFGJEqYX40WUmxMkwhWCjw==} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-powerpc64le-gnu@4.34.6: + resolution: {integrity: sha512-Zsrtux3PuaxuBTX/zHdLaFmcofWGzaWW1scwLU3ZbW/X+hSsFbz9wDIp6XvnT7pzYRl9MezWqEqKy7ssmDEnuQ==} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-riscv64-gnu@4.34.6: + resolution: {integrity: sha512-aK+Zp+CRM55iPrlyKiU3/zyhgzWBxLVrw2mwiQSYJRobCURb781+XstzvA8Gkjg/hbdQFuDw44aUOxVQFycrAg==} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-s390x-gnu@4.34.6: + resolution: {integrity: sha512-WoKLVrY9ogmaYPXwTH326+ErlCIgMmsoRSx6bO+l68YgJnlOXhygDYSZe/qbUJCSiCiZAQ+tKm88NcWuUXqOzw==} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-x64-gnu@4.34.6: + resolution: {integrity: sha512-Sht4aFvmA4ToHd2vFzwMFaQCiYm2lDFho5rPcvPBT5pCdC+GwHG6CMch4GQfmWTQ1SwRKS0dhDYb54khSrjDWw==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-x64-musl@4.34.6: + resolution: {integrity: sha512-zmmpOQh8vXc2QITsnCiODCDGXFC8LMi64+/oPpPx5qz3pqv0s6x46ps4xoycfUiVZps5PFn1gksZzo4RGTKT+A==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-arm64-msvc@4.34.6: + resolution: {integrity: sha512-3/q1qUsO/tLqGBaD4uXsB6coVGB3usxw3qyeVb59aArCgedSF66MPdgRStUd7vbZOsko/CgVaY5fo2vkvPLWiA==} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-ia32-msvc@4.34.6: + resolution: {integrity: sha512-oLHxuyywc6efdKVTxvc0135zPrRdtYVjtVD5GUm55I3ODxhU/PwkQFD97z16Xzxa1Fz0AEe4W/2hzRtd+IfpOA==} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-x64-msvc@4.34.6: + resolution: {integrity: sha512-0PVwmgzZ8+TZ9oGBmdZoQVXflbvuwzN/HRclujpl4N/q3i+y0lqLw8n1bXA8ru3sApDjlmONaNAuYr38y1Kr9w==} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@sveltejs/adapter-static@3.0.8(@sveltejs/kit@2.17.1): + resolution: {integrity: sha512-YaDrquRpZwfcXbnlDsSrBQNCChVOT9MGuSg+dMAyfsAa1SmiAhrA5jUYUiIMC59G92kIbY/AaQOWcBdq+lh+zg==} + peerDependencies: + '@sveltejs/kit': ^2.0.0 + dependencies: + '@sveltejs/kit': 2.17.1(@sveltejs/vite-plugin-svelte@5.0.3)(svelte@5.19.10)(vite@6.1.0) + dev: true + + /@sveltejs/kit@2.17.1(@sveltejs/vite-plugin-svelte@5.0.3)(svelte@5.19.10)(vite@6.1.0): + resolution: {integrity: sha512-CpoGSLqE2MCmcQwA2CWJvOsZ9vW+p/1H3itrFykdgajUNAEyQPbsaSn7fZb6PLHQwe+07njxje9ss0fjZoCAyw==} + engines: {node: '>=18.13'} + hasBin: true + peerDependencies: + '@sveltejs/vite-plugin-svelte': ^3.0.0 || ^4.0.0-next.1 || ^5.0.0 + svelte: ^4.0.0 || ^5.0.0-next.0 + vite: ^5.0.3 || ^6.0.0 + dependencies: + '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.19.10)(vite@6.1.0) + '@types/cookie': 0.6.0 + cookie: 0.6.0 + devalue: 5.1.1 + esm-env: 1.2.2 + import-meta-resolve: 4.1.0 + kleur: 4.1.5 + magic-string: 0.30.17 + mrmime: 2.0.0 + sade: 1.8.1 + set-cookie-parser: 2.7.1 + sirv: 3.0.0 + svelte: 5.19.10 + vite: 6.1.0 + dev: true + + /@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.0.3)(svelte@5.19.10)(vite@6.1.0): + resolution: {integrity: sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22} + peerDependencies: + '@sveltejs/vite-plugin-svelte': ^5.0.0 + svelte: ^5.0.0 + vite: ^6.0.0 + dependencies: + '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.19.10)(vite@6.1.0) + debug: 4.4.0 + svelte: 5.19.10 + vite: 6.1.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.10)(vite@6.1.0): + resolution: {integrity: sha512-MCFS6CrQDu1yGwspm4qtli0e63vaPCehf6V7pIMP15AsWgMKrqDGCPFF/0kn4SP0ii4aySu4Pa62+fIRGFMjgw==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22} + peerDependencies: + svelte: ^5.0.0 + vite: ^6.0.0 + dependencies: + '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.0.3)(svelte@5.19.10)(vite@6.1.0) + debug: 4.4.0 + deepmerge: 4.3.1 + kleur: 4.1.5 + magic-string: 0.30.17 + svelte: 5.19.10 + vite: 6.1.0 + vitefu: 1.0.5(vite@6.1.0) + transitivePeerDependencies: + - supports-color + dev: true + + /@swc/core-darwin-arm64@1.10.15: + resolution: {integrity: sha512-zFdZ6/yHqMCPk7OhLFqHy/MQ1EqJhcZMpNHd1gXYT7VRU3FaqvvKETrUlG3VYl65McPC7AhMRfXPyJ0JO/jARQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@swc/core-darwin-x64@1.10.15: + resolution: {integrity: sha512-8g4yiQwbr8fxOOjKXdot0dEkE5zgE8uNZudLy/ZyAhiwiZ8pbJ8/wVrDOu6dqbX7FBXAoDnvZ7fwN1jk4C8jdA==} + engines: {node: '>=10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-arm-gnueabihf@1.10.15: + resolution: {integrity: sha512-rl+eVOltl2+7WXOnvmWBpMgh6aO13G5x0U0g8hjwlmD6ku3Y9iRcThpOhm7IytMEarUp5pQxItNoPq+VUGjVHg==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-arm64-gnu@1.10.15: + resolution: {integrity: sha512-qxWEQeyAJMWJqjaN4hi58WMpPdt3Tn0biSK9CYRegQtvZWCbewr6v2agtSu5AZ2rudeH6OfCWAMDQQeSgn6PJQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-arm64-musl@1.10.15: + resolution: {integrity: sha512-QcELd9/+HjZx0WCxRrKcyKGWTiQ0485kFb5w8waxcSNd0d9Lgk4EFfWWVyvIb5gIHpDQmhrgzI/yRaWQX4YSZQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-x64-gnu@1.10.15: + resolution: {integrity: sha512-S1+ZEEn3+a/MiMeQqQypbwTGoBG8/sPoCvpNbk+uValyygT+jSn3U0xVr45FbukpmMB+NhBMqfedMLqKA0QnJA==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-x64-musl@1.10.15: + resolution: {integrity: sha512-qW+H9g/2zTJ4jP7NDw4VAALY0ZlNEKzYsEoSj/HKi7k3tYEHjMzsxjfsY9I8WZCft23bBdV3RTCPoxCshaj1CQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-win32-arm64-msvc@1.10.15: + resolution: {integrity: sha512-AhRB11aA6LxjIqut+mg7qsu/7soQDmbK6MKR9nP3hgBszpqtXbRba58lr24xIbBCMr+dpo6kgEapWt+t5Po6Zg==} + engines: {node: '>=10'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@swc/core-win32-ia32-msvc@1.10.15: + resolution: {integrity: sha512-UGdh430TQwbDn6KjgvRTg1fO022sbQ4yCCHUev0+5B8uoBwi9a89qAz3emy2m56C8TXxUoihW9Y9OMfaRwPXUw==} + engines: {node: '>=10'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@swc/core-win32-x64-msvc@1.10.15: + resolution: {integrity: sha512-XJzBCqO1m929qbJsOG7FZXQWX26TnEoMctS3QjuCoyBmkHxxQmZsy78KjMes1aomTcKHCyFYgrRGWgVmk7tT4Q==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@swc/core@1.10.15: + resolution: {integrity: sha512-/iFeQuNaGdK7mfJbQcObhAhsMqLT7qgMYl7jX2GEIO+VDTejESpzAyKwaMeYXExN8D6e5BRHBCe7M5YlsuzjDA==} + engines: {node: '>=10'} + requiresBuild: true + peerDependencies: + '@swc/helpers': '*' + peerDependenciesMeta: + '@swc/helpers': + optional: true + dependencies: + '@swc/counter': 0.1.3 + '@swc/types': 0.1.17 + optionalDependencies: + '@swc/core-darwin-arm64': 1.10.15 + '@swc/core-darwin-x64': 1.10.15 + '@swc/core-linux-arm-gnueabihf': 1.10.15 + '@swc/core-linux-arm64-gnu': 1.10.15 + '@swc/core-linux-arm64-musl': 1.10.15 + '@swc/core-linux-x64-gnu': 1.10.15 + '@swc/core-linux-x64-musl': 1.10.15 + '@swc/core-win32-arm64-msvc': 1.10.15 + '@swc/core-win32-ia32-msvc': 1.10.15 + '@swc/core-win32-x64-msvc': 1.10.15 + dev: true + + /@swc/counter@0.1.3: + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + dev: true + + /@swc/helpers@0.5.2: + resolution: {integrity: sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==} + dependencies: + tslib: 2.6.2 + dev: false + + /@swc/types@0.1.17: + resolution: {integrity: sha512-V5gRru+aD8YVyCOMAjMpWR1Ui577DD5KSJsHP8RAxopAH22jFz6GZd/qxqjO6MJHQhcsjvjOFXyDhyLQUnMveQ==} + dependencies: + '@swc/counter': 0.1.3 + dev: true + + /@tailwindcss/node@4.0.6: + resolution: {integrity: sha512-jb6E0WeSq7OQbVYcIJ6LxnZTeC4HjMvbzFBMCrQff4R50HBlo/obmYNk6V2GCUXDeqiXtvtrQgcIbT+/boB03Q==} + dependencies: + enhanced-resolve: 5.18.1 + jiti: 2.4.2 + tailwindcss: 4.0.6 + dev: true + + /@tailwindcss/oxide-android-arm64@4.0.6: + resolution: {integrity: sha512-xDbym6bDPW3D2XqQqX3PjqW3CKGe1KXH7Fdkc60sX5ZLVUbzPkFeunQaoP+BuYlLc2cC1FoClrIRYnRzof9Sow==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@tailwindcss/oxide-darwin-arm64@4.0.6: + resolution: {integrity: sha512-1f71/ju/tvyGl5c2bDkchZHy8p8EK/tDHCxlpYJ1hGNvsYihZNurxVpZ0DefpN7cNc9RTT8DjrRoV8xXZKKRjg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@tailwindcss/oxide-darwin-x64@4.0.6: + resolution: {integrity: sha512-s/hg/ZPgxFIrGMb0kqyeaqZt505P891buUkSezmrDY6lxv2ixIELAlOcUVTkVh245SeaeEiUVUPiUN37cwoL2g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@tailwindcss/oxide-freebsd-x64@4.0.6: + resolution: {integrity: sha512-Z3Wo8FWZnmio8+xlcbb7JUo/hqRMSmhQw8IGIRoRJ7GmLR0C+25Wq+bEX/135xe/yEle2lFkhu9JBHd4wZYiig==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@tailwindcss/oxide-linux-arm-gnueabihf@4.0.6: + resolution: {integrity: sha512-SNSwkkim1myAgmnbHs4EjXsPL7rQbVGtjcok5EaIzkHkCAVK9QBQsWeP2Jm2/JJhq4wdx8tZB9Y7psMzHYWCkA==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@tailwindcss/oxide-linux-arm64-gnu@4.0.6: + resolution: {integrity: sha512-tJ+mevtSDMQhKlwCCuhsFEFg058kBiSy4TkoeBG921EfrHKmexOaCyFKYhVXy4JtkaeeOcjJnCLasEeqml4i+Q==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@tailwindcss/oxide-linux-arm64-musl@4.0.6: + resolution: {integrity: sha512-IoArz1vfuTR4rALXMUXI/GWWfx2EaO4gFNtBNkDNOYhlTD4NVEwE45nbBoojYiTulajI4c2XH8UmVEVJTOJKxA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@tailwindcss/oxide-linux-x64-gnu@4.0.6: + resolution: {integrity: sha512-QtsUfLkEAeWAC3Owx9Kg+7JdzE+k9drPhwTAXbXugYB9RZUnEWWx5x3q/au6TvUYcL+n0RBqDEO2gucZRvRFgQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@tailwindcss/oxide-linux-x64-musl@4.0.6: + resolution: {integrity: sha512-QthvJqIji2KlGNwLcK/PPYo7w1Wsi/8NK0wAtRGbv4eOPdZHkQ9KUk+oCoP20oPO7i2a6X1aBAFQEL7i08nNMA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@tailwindcss/oxide-win32-arm64-msvc@4.0.6: + resolution: {integrity: sha512-+oka+dYX8jy9iP00DJ9Y100XsqvbqR5s0yfMZJuPR1H/lDVtDfsZiSix1UFBQ3X1HWxoEEl6iXNJHWd56TocVw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@tailwindcss/oxide-win32-x64-msvc@4.0.6: + resolution: {integrity: sha512-+o+juAkik4p8Ue/0LiflQXPmVatl6Av3LEZXpBTfg4qkMIbZdhCGWFzHdt2NjoMiLOJCFDddoV6GYaimvK1Olw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@tailwindcss/oxide@4.0.6: + resolution: {integrity: sha512-lVyKV2y58UE9CeKVcYykULe9QaE1dtKdxDEdrTPIdbzRgBk6bdxHNAoDqvcqXbIGXubn3VOl1O/CFF77v/EqSA==} + engines: {node: '>= 10'} + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.0.6 + '@tailwindcss/oxide-darwin-arm64': 4.0.6 + '@tailwindcss/oxide-darwin-x64': 4.0.6 + '@tailwindcss/oxide-freebsd-x64': 4.0.6 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.0.6 + '@tailwindcss/oxide-linux-arm64-gnu': 4.0.6 + '@tailwindcss/oxide-linux-arm64-musl': 4.0.6 + '@tailwindcss/oxide-linux-x64-gnu': 4.0.6 + '@tailwindcss/oxide-linux-x64-musl': 4.0.6 + '@tailwindcss/oxide-win32-arm64-msvc': 4.0.6 + '@tailwindcss/oxide-win32-x64-msvc': 4.0.6 + dev: true + + /@tailwindcss/vite@4.0.6(vite@6.1.0): + resolution: {integrity: sha512-O25vZ/URWbZ2JHdk2o8wH7jOKqEGCsYmX3GwGmYS5DjE4X3mpf93a72Rn7VRnefldNauBzr5z2hfZptmBNtTUQ==} + peerDependencies: + vite: ^5.2.0 || ^6 + dependencies: + '@tailwindcss/node': 4.0.6 + '@tailwindcss/oxide': 4.0.6 + lightningcss: 1.29.1 + tailwindcss: 4.0.6 + vite: 6.1.0 + dev: true + + /@types/cookie@0.6.0: + resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + dev: true + + /@types/estree@1.0.6: + resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + dev: true + + /@types/json-schema@7.0.15: + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + dev: true + + /@types/node@20.11.24: + resolution: {integrity: sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==} + dependencies: + undici-types: 5.26.5 + dev: true + + /@types/prop-types@15.7.11: + resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==} + dev: true + + /@types/react-dom@18.2.19: + resolution: {integrity: sha512-aZvQL6uUbIJpjZk4U8JZGbau9KDeAwMfmhyWorxgBkqDIEf6ROjRozcmPIicqsUwPUjbkDfHKgGee1Lq65APcA==} + dependencies: + '@types/react': 18.2.61 dev: true /@types/react@18.2.61: @@ -473,12 +1399,167 @@ packages: resolution: {integrity: sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==} dev: true + /@typescript-eslint/eslint-plugin@8.24.0(@typescript-eslint/parser@8.24.0)(eslint@9.20.1)(typescript@5.3.3): + resolution: {integrity: sha512-aFcXEJJCI4gUdXgoo/j9udUYIHgF23MFkg09LFz2dzEmU0+1Plk4rQWv/IYKvPHAtlkkGoB3m5e6oUp+JPsNaQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 8.24.0(eslint@9.20.1)(typescript@5.3.3) + '@typescript-eslint/scope-manager': 8.24.0 + '@typescript-eslint/type-utils': 8.24.0(eslint@9.20.1)(typescript@5.3.3) + '@typescript-eslint/utils': 8.24.0(eslint@9.20.1)(typescript@5.3.3) + '@typescript-eslint/visitor-keys': 8.24.0 + eslint: 9.20.1 + graphemer: 1.4.0 + ignore: 5.3.1 + natural-compare: 1.4.0 + ts-api-utils: 2.0.1(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser@8.24.0(eslint@9.20.1)(typescript@5.3.3): + resolution: {integrity: sha512-MFDaO9CYiard9j9VepMNa9MTcqVvSny2N4hkY6roquzj8pdCBRENhErrteaQuu7Yjn1ppk0v1/ZF9CG3KIlrTA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' + dependencies: + '@typescript-eslint/scope-manager': 8.24.0 + '@typescript-eslint/types': 8.24.0 + '@typescript-eslint/typescript-estree': 8.24.0(typescript@5.3.3) + '@typescript-eslint/visitor-keys': 8.24.0 + debug: 4.4.0 + eslint: 9.20.1 + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager@8.24.0: + resolution: {integrity: sha512-HZIX0UByphEtdVBKaQBgTDdn9z16l4aTUz8e8zPQnyxwHBtf5vtl1L+OhH+m1FGV9DrRmoDuYKqzVrvWDcDozw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dependencies: + '@typescript-eslint/types': 8.24.0 + '@typescript-eslint/visitor-keys': 8.24.0 + dev: true + + /@typescript-eslint/type-utils@8.24.0(eslint@9.20.1)(typescript@5.3.3): + resolution: {integrity: sha512-8fitJudrnY8aq0F1wMiPM1UUgiXQRJ5i8tFjq9kGfRajU+dbPyOuHbl0qRopLEidy0MwqgTHDt6CnSeXanNIwA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' + dependencies: + '@typescript-eslint/typescript-estree': 8.24.0(typescript@5.3.3) + '@typescript-eslint/utils': 8.24.0(eslint@9.20.1)(typescript@5.3.3) + debug: 4.4.0 + eslint: 9.20.1 + ts-api-utils: 2.0.1(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/types@8.24.0: + resolution: {integrity: sha512-VacJCBTyje7HGAw7xp11q439A+zeGG0p0/p2zsZwpnMzjPB5WteaWqt4g2iysgGFafrqvyLWqq6ZPZAOCoefCw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dev: true + + /@typescript-eslint/typescript-estree@8.24.0(typescript@5.3.3): + resolution: {integrity: sha512-ITjYcP0+8kbsvT9bysygfIfb+hBj6koDsu37JZG7xrCiy3fPJyNmfVtaGsgTUSEuTzcvME5YI5uyL5LD1EV5ZQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.8.0' + dependencies: + '@typescript-eslint/types': 8.24.0 + '@typescript-eslint/visitor-keys': 8.24.0 + debug: 4.4.0 + fast-glob: 3.3.2 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.1 + ts-api-utils: 2.0.1(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils@8.24.0(eslint@9.20.1)(typescript@5.3.3): + resolution: {integrity: sha512-07rLuUBElvvEb1ICnafYWr4hk8/U7X9RDCOqd9JcAMtjh/9oRmcfN4yGzbPVirgMR0+HLVHehmu19CWeh7fsmQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' + dependencies: + '@eslint-community/eslint-utils': 4.4.1(eslint@9.20.1) + '@typescript-eslint/scope-manager': 8.24.0 + '@typescript-eslint/types': 8.24.0 + '@typescript-eslint/typescript-estree': 8.24.0(typescript@5.3.3) + eslint: 9.20.1 + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/visitor-keys@8.24.0: + resolution: {integrity: sha512-kArLq83QxGLbuHrTMoOEWO+l2MwsNS2TGISEdx8xgqpkbytB07XmlQyQdNDrCc1ecSqx0cnmhGvpX+VBwqqSkg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dependencies: + '@typescript-eslint/types': 8.24.0 + eslint-visitor-keys: 4.2.0 + dev: true + + /acorn-jsx@5.3.2(acorn@8.12.1): + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.12.1 + dev: true + + /acorn-jsx@5.3.2(acorn@8.14.0): + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.14.0 + dev: true + + /acorn-typescript@1.4.13(acorn@8.12.1): + resolution: {integrity: sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q==} + peerDependencies: + acorn: '>=8.9.0' + dependencies: + acorn: 8.12.1 + dev: true + /acorn@8.12.1: resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} engines: {node: '>=0.4.0'} hasBin: true dev: true + /acorn@8.14.0: + resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + /ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -517,6 +1598,15 @@ packages: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} dev: true + /argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + + /aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + dev: true + /autoprefixer@10.4.18(postcss@8.4.35): resolution: {integrity: sha512-1DKbDfsr6KUElM6wg+0zRNkB/Q7WcKYAaK+pzXn+Xqmszm/5Xa9coeNdtP88Vi+dPzZnMjhge8GIV49ZQkDa+g==} engines: {node: ^10 || ^12 || >=14} @@ -541,6 +1631,11 @@ packages: - debug dev: true + /axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + dev: true + /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true @@ -611,6 +1706,11 @@ packages: resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} dev: true + /callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + /camelcase-css@2.0.1: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} @@ -619,6 +1719,21 @@ packages: /caniuse-lite@1.0.30001591: resolution: {integrity: sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ==} + /chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /chart.js@4.4.7: + resolution: {integrity: sha512-pwkcKfdzTMAU/+jNosKhNL2bHtJc/sSmYgVbuGTEDhzkrhmyihmP7vUc/5ZK9WopidMDHNe3Wm7jOd/WhuHWuw==} + engines: {pnpm: '>=8'} + dependencies: + '@kurkle/color': 0.3.4 + dev: false + /chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} @@ -634,6 +1749,13 @@ packages: fsevents: 2.3.3 dev: true + /chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + dependencies: + readdirp: 4.1.1 + dev: true + /chownr@2.0.0: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} @@ -647,6 +1769,11 @@ packages: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} dev: false + /clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + dev: true + /color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -663,6 +1790,10 @@ packages: engines: {node: '>= 6'} dev: true + /complex.js@2.4.2: + resolution: {integrity: sha512-qtx7HRhPGSCBtGiST4/WGHuW+zeaND/6Ld+db6PbrulIB1i2Ev/2UPiqcmpQNPSyfBKraC0EOvOKCB5dGZKt3g==} + dev: false + /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} dev: true @@ -671,6 +1802,11 @@ packages: resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} dev: true + /cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} + dev: true + /cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -680,6 +1816,15 @@ packages: which: 2.0.2 dev: true + /cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + /cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} @@ -689,15 +1834,50 @@ packages: /csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + /debug@4.4.0: + resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.3 + dev: true + + /decimal.js@10.5.0: + resolution: {integrity: sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==} + dev: false + /decode-uri-component@0.4.1: resolution: {integrity: sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ==} engines: {node: '>=14.16'} dev: true + /deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + dev: true + + /deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + dev: true + /deprecation@2.3.1: resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==} dev: true + /detect-libc@1.0.3: + resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} + engines: {node: '>=0.10'} + hasBin: true + dev: true + + /devalue@5.1.1: + resolution: {integrity: sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==} + dev: true + /didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} dev: true @@ -722,11 +1902,237 @@ packages: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} dev: true + /enhanced-resolve@5.18.1: + resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} + engines: {node: '>=10.13.0'} + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.1 + dev: true + + /esbuild@0.24.2: + resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==} + engines: {node: '>=18'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/aix-ppc64': 0.24.2 + '@esbuild/android-arm': 0.24.2 + '@esbuild/android-arm64': 0.24.2 + '@esbuild/android-x64': 0.24.2 + '@esbuild/darwin-arm64': 0.24.2 + '@esbuild/darwin-x64': 0.24.2 + '@esbuild/freebsd-arm64': 0.24.2 + '@esbuild/freebsd-x64': 0.24.2 + '@esbuild/linux-arm': 0.24.2 + '@esbuild/linux-arm64': 0.24.2 + '@esbuild/linux-ia32': 0.24.2 + '@esbuild/linux-loong64': 0.24.2 + '@esbuild/linux-mips64el': 0.24.2 + '@esbuild/linux-ppc64': 0.24.2 + '@esbuild/linux-riscv64': 0.24.2 + '@esbuild/linux-s390x': 0.24.2 + '@esbuild/linux-x64': 0.24.2 + '@esbuild/netbsd-arm64': 0.24.2 + '@esbuild/netbsd-x64': 0.24.2 + '@esbuild/openbsd-arm64': 0.24.2 + '@esbuild/openbsd-x64': 0.24.2 + '@esbuild/sunos-x64': 0.24.2 + '@esbuild/win32-arm64': 0.24.2 + '@esbuild/win32-ia32': 0.24.2 + '@esbuild/win32-x64': 0.24.2 + dev: true + /escalade@3.1.2: resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} engines: {node: '>=6'} dev: true + /escape-latex@1.2.0: + resolution: {integrity: sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==} + dev: false + + /escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + dev: true + + /eslint-compat-utils@0.5.1(eslint@9.20.1): + resolution: {integrity: sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==} + engines: {node: '>=12'} + peerDependencies: + eslint: '>=6.0.0' + dependencies: + eslint: 9.20.1 + semver: 7.7.1 + dev: true + + /eslint-config-prettier@10.0.1(eslint@9.20.1): + resolution: {integrity: sha512-lZBts941cyJyeaooiKxAtzoPHTN+GbQTJFAIdQbRhA4/8whaAraEh47Whw/ZFfrjNSnlAxqfm9i0XVAEkULjCw==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + dependencies: + eslint: 9.20.1 + dev: true + + /eslint-plugin-svelte@2.46.1(eslint@9.20.1)(svelte@5.19.10): + resolution: {integrity: sha512-7xYr2o4NID/f9OEYMqxsEQsCsj4KaMy4q5sANaKkAb6/QeCjYFxRmDm2S3YC3A3pl1kyPZ/syOx/i7LcWYSbIw==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0-0 || ^9.0.0-0 + svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + svelte: + optional: true + dependencies: + '@eslint-community/eslint-utils': 4.4.1(eslint@9.20.1) + '@jridgewell/sourcemap-codec': 1.5.0 + eslint: 9.20.1 + eslint-compat-utils: 0.5.1(eslint@9.20.1) + esutils: 2.0.3 + known-css-properties: 0.35.0 + postcss: 8.5.2 + postcss-load-config: 3.1.4(postcss@8.5.2) + postcss-safe-parser: 6.0.0(postcss@8.5.2) + postcss-selector-parser: 6.1.2 + semver: 7.7.1 + svelte: 5.19.10 + svelte-eslint-parser: 0.43.0(svelte@5.19.10) + transitivePeerDependencies: + - ts-node + dev: true + + /eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + + /eslint-scope@8.2.0: + resolution: {integrity: sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + + /eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /eslint-visitor-keys@4.2.0: + resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dev: true + + /eslint@9.20.1: + resolution: {integrity: sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + dependencies: + '@eslint-community/eslint-utils': 4.4.1(eslint@9.20.1) + '@eslint-community/regexpp': 4.12.1 + '@eslint/config-array': 0.19.2 + '@eslint/core': 0.11.0 + '@eslint/eslintrc': 3.2.0 + '@eslint/js': 9.20.0 + '@eslint/plugin-kit': 0.2.5 + '@humanfs/node': 0.16.6 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.1 + '@types/estree': 1.0.6 + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.0 + escape-string-regexp: 4.0.0 + eslint-scope: 8.2.0 + eslint-visitor-keys: 4.2.0 + espree: 10.3.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.1 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + dev: true + + /esm-env@1.2.2: + resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} + dev: true + + /espree@10.3.0: + resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dependencies: + acorn: 8.14.0 + acorn-jsx: 5.3.2(acorn@8.14.0) + eslint-visitor-keys: 4.2.0 + dev: true + + /espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.12.1 + acorn-jsx: 5.3.2(acorn@8.12.1) + eslint-visitor-keys: 3.4.3 + dev: true + + /esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + /esrap@1.4.4: + resolution: {integrity: sha512-tDN6xP/r/b3WmdpWm7LybrD252hY52IokcycPnO+WHfhFF0+n5AWtcLLK7VNV6m0uYgVRhGVs8OkZwRyfC7HzQ==} + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + dev: true + + /esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + /estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + dev: true + + /esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + dev: true + + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: true + /fast-glob@3.3.2: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} engines: {node: '>=8.6.0'} @@ -738,12 +2144,36 @@ packages: micromatch: 4.0.5 dev: true + /fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + /fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + dev: true + /fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} dependencies: reusify: 1.0.4 dev: true + /fdir@6.4.3: + resolution: {integrity: sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + dev: true + + /file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + dependencies: + flat-cache: 4.0.1 + dev: true + /fill-range@7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} @@ -756,10 +2186,30 @@ packages: engines: {node: '>=14.16'} dev: true + /find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + /fix-webm-duration@1.0.5: resolution: {integrity: sha512-b6oula3OfSknx0aWoLsxvp4DVIYbwsf+UAkr6EDAK3iuMYk/OSNKzmeSI61GXK0MmFTEuzle19BPvTxMIKjkZg==} dev: false + /flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + dependencies: + flatted: 3.3.2 + keyv: 4.5.4 + dev: true + + /flatted@3.3.2: + resolution: {integrity: sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==} + dev: true + /follow-redirects@1.15.6: resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} engines: {node: '>=4.0'} @@ -782,6 +2232,11 @@ packages: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} dev: true + /fraction.js@5.2.1: + resolution: {integrity: sha512-Ah6t/7YCYjrPUFUFsOsRLMXAdnYM+aQwmojD2Ayb/Ezr82SwES0vuyQ8qZ3QO8n9j7W14VJuVZZet8U3bhSdQQ==} + engines: {node: '>= 12'} + dev: false + /fs-minipass@2.1.0: resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} engines: {node: '>= 8'} @@ -843,6 +2298,16 @@ packages: path-is-absolute: 1.0.1 dev: true + /globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + dev: true + + /globals@15.14.0: + resolution: {integrity: sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==} + engines: {node: '>=18'} + dev: true + /goober@2.1.14(csstype@3.1.3): resolution: {integrity: sha512-4UpC0NdGyAFqLNPnhCT2iHpza2q+RAY3GV85a/mRPdzyPQMsj0KmMMuetdIkzWRbJ+Hgau1EZztq8ImmiMGhsg==} peerDependencies: @@ -853,7 +2318,15 @@ packages: /graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - dev: false + + /graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + dev: true + + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true /hasown@2.0.1: resolution: {integrity: sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==} @@ -867,6 +2340,23 @@ packages: engines: {node: '>= 4'} dev: true + /import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + /import-meta-resolve@4.1.0: + resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==} + dev: true + + /imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + dev: true + /inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. @@ -914,6 +2404,12 @@ packages: engines: {node: '>=0.12.0'} dev: true + /is-reference@3.0.3: + resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} + dependencies: + '@types/estree': 1.0.6 + dev: true + /isbinaryfile@5.0.2: resolution: {integrity: sha512-GvcjojwonMjWbTkfMpnVHVqXW/wKMYDfEpY94/8zy8HFMOqb/VL6oeONq9v87q4ttVlaTLnGXnJD4B5B1OTGIg==} engines: {node: '>= 18.0.0'} @@ -923,24 +2419,183 @@ packages: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true - /jackspeak@2.3.6: - resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} - engines: {node: '>=14'} + /jackspeak@2.3.6: + resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} + engines: {node: '>=14'} + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + dev: true + + /javascript-natural-sort@0.7.1: + resolution: {integrity: sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==} + dev: false + + /jiti@1.21.0: + resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==} + hasBin: true + dev: true + + /jiti@2.4.2: + resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} + hasBin: true + dev: true + + /js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + dev: false + + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + /json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + dev: true + + /json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + /json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + dev: true + + /keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + dependencies: + json-buffer: 3.0.1 + dev: true + + /kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + dev: true + + /known-css-properties@0.35.0: + resolution: {integrity: sha512-a/RAk2BfKk+WFGhhOCAYqSiFLc34k8Mt/6NWRI4joER0EYUzXIcFivjjnoD3+XU1DggLn/tZc3DOAgke7l8a4A==} + dev: true + + /levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /lightningcss-darwin-arm64@1.29.1: + resolution: {integrity: sha512-HtR5XJ5A0lvCqYAoSv2QdZZyoHNttBpa5EP9aNuzBQeKGfbyH5+UipLWvVzpP4Uml5ej4BYs5I9Lco9u1fECqw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /lightningcss-darwin-x64@1.29.1: + resolution: {integrity: sha512-k33G9IzKUpHy/J/3+9MCO4e+PzaFblsgBjSGlpAaFikeBFm8B/CkO3cKU9oI4g+fjS2KlkLM/Bza9K/aw8wsNA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /lightningcss-freebsd-x64@1.29.1: + resolution: {integrity: sha512-0SUW22fv/8kln2LnIdOCmSuXnxgxVC276W5KLTwoehiO0hxkacBxjHOL5EtHD8BAXg2BvuhsJPmVMasvby3LiQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /lightningcss-linux-arm-gnueabihf@1.29.1: + resolution: {integrity: sha512-sD32pFvlR0kDlqsOZmYqH/68SqUMPNj+0pucGxToXZi4XZgZmqeX/NkxNKCPsswAXU3UeYgDSpGhu05eAufjDg==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /lightningcss-linux-arm64-gnu@1.29.1: + resolution: {integrity: sha512-0+vClRIZ6mmJl/dxGuRsE197o1HDEeeRk6nzycSy2GofC2JsY4ifCRnvUWf/CUBQmlrvMzt6SMQNMSEu22csWQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /lightningcss-linux-arm64-musl@1.29.1: + resolution: {integrity: sha512-UKMFrG4rL/uHNgelBsDwJcBqVpzNJbzsKkbI3Ja5fg00sgQnHw/VrzUTEc4jhZ+AN2BvQYz/tkHu4vt1kLuJyw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /lightningcss-linux-x64-gnu@1.29.1: + resolution: {integrity: sha512-u1S+xdODy/eEtjADqirA774y3jLcm8RPtYztwReEXoZKdzgsHYPl0s5V52Tst+GKzqjebkULT86XMSxejzfISw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /lightningcss-linux-x64-musl@1.29.1: + resolution: {integrity: sha512-L0Tx0DtaNUTzXv0lbGCLB/c/qEADanHbu4QdcNOXLIe1i8i22rZRpbT3gpWYsCh9aSL9zFujY/WmEXIatWvXbw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /lightningcss-win32-arm64-msvc@1.29.1: + resolution: {integrity: sha512-QoOVnkIEFfbW4xPi+dpdft/zAKmgLgsRHfJalEPYuJDOWf7cLQzYg0DEh8/sn737FaeMJxHZRc1oBreiwZCjog==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /lightningcss-win32-x64-msvc@1.29.1: + resolution: {integrity: sha512-NygcbThNBe4JElP+olyTI/doBNGJvLs3bFCRPdvuCcxZCcCZ71B858IHpdm7L1btZex0FvCmM17FK98Y9MRy1Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /lightningcss@1.29.1: + resolution: {integrity: sha512-FmGoeD4S05ewj+AkhTY+D+myDvXI6eL27FjHIjoyUkO/uw7WZD1fBVs0QxeYWa7E17CUHJaYX/RUGISCtcrG4Q==} + engines: {node: '>= 12.0.0'} dependencies: - '@isaacs/cliui': 8.0.2 + detect-libc: 1.0.3 optionalDependencies: - '@pkgjs/parseargs': 0.11.0 - dev: true - - /jiti@1.21.0: - resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==} - hasBin: true + lightningcss-darwin-arm64: 1.29.1 + lightningcss-darwin-x64: 1.29.1 + lightningcss-freebsd-x64: 1.29.1 + lightningcss-linux-arm-gnueabihf: 1.29.1 + lightningcss-linux-arm64-gnu: 1.29.1 + lightningcss-linux-arm64-musl: 1.29.1 + lightningcss-linux-x64-gnu: 1.29.1 + lightningcss-linux-x64-musl: 1.29.1 + lightningcss-win32-arm64-msvc: 1.29.1 + lightningcss-win32-x64-msvc: 1.29.1 dev: true - /js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - dev: false - /lilconfig@2.1.0: resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} engines: {node: '>=10'} @@ -955,6 +2610,21 @@ packages: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} dev: true + /locate-character@3.0.0: + resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} + dev: true + + /locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + /loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -967,6 +2637,28 @@ packages: engines: {node: 14 || >=16.14} dev: true + /magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + dev: true + + /mathjs@14.2.1: + resolution: {integrity: sha512-vARWETUx75+kR2K9qBV20n6NYtGXCuQKX8Zo4+AhJI5LX+ukSM1NYebv+wLnJG8KMvEe9H01sJUyC5bMciA4Tg==} + engines: {node: '>= 18'} + hasBin: true + dependencies: + '@babel/runtime': 7.26.7 + complex.js: 2.4.2 + decimal.js: 10.5.0 + escape-latex: 1.2.0 + fraction.js: 5.2.1 + javascript-natural-sort: 0.7.1 + seedrandom: 3.0.5 + tiny-emitter: 2.1.0 + typed-function: 4.2.1 + dev: false + /merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -993,6 +2685,13 @@ packages: brace-expansion: 2.0.1 dev: true + /minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: true + /minipass@3.3.6: resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} engines: {node: '>=8'} @@ -1033,6 +2732,20 @@ packages: ufo: 1.5.3 dev: true + /mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + dev: true + + /mrmime@2.0.0: + resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==} + engines: {node: '>=10'} + dev: true + + /ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + dev: true + /mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} dependencies: @@ -1046,6 +2759,16 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + /nanoid@3.3.8: + resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + + /natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: true + /next@14.1.0(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-wlzrsbfeSU48YQBjZhDzOwhWhGsy+uQycR8bHAOt1LY1bn3zZEcDyHQOEoN3aWzQ8LHCAJ1nqrWCc9XF2+O45Q==} engines: {node: '>=18.17.0'} @@ -1115,6 +2838,44 @@ packages: wrappy: 1.0.2 dev: true + /optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + dev: true + + /p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + + /path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + /path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} @@ -1144,6 +2905,10 @@ packages: /picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + /picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + dev: true + /picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} @@ -1202,6 +2967,23 @@ packages: postcss: 8.4.35 dev: true + /postcss-load-config@3.1.4(postcss@8.5.2): + resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} + engines: {node: '>= 10'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + dependencies: + lilconfig: 2.1.0 + postcss: 8.5.2 + yaml: 1.10.2 + dev: true + /postcss-load-config@4.0.2(postcss@8.4.35): resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} engines: {node: '>= 14'} @@ -1229,6 +3011,24 @@ packages: postcss-selector-parser: 6.0.15 dev: true + /postcss-safe-parser@6.0.0(postcss@8.5.2): + resolution: {integrity: sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.3.3 + dependencies: + postcss: 8.5.2 + dev: true + + /postcss-scss@4.0.9(postcss@8.5.2): + resolution: {integrity: sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.4.29 + dependencies: + postcss: 8.5.2 + dev: true + /postcss-selector-parser@6.0.15: resolution: {integrity: sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==} engines: {node: '>=4'} @@ -1237,6 +3037,14 @@ packages: util-deprecate: 1.0.2 dev: true + /postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + dev: true + /postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} dev: true @@ -1259,6 +3067,41 @@ packages: source-map-js: 1.0.2 dev: true + /postcss@8.5.2: + resolution: {integrity: sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.8 + picocolors: 1.1.1 + source-map-js: 1.2.1 + dev: true + + /prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + dev: true + + /prettier-plugin-svelte@3.3.3(prettier@3.5.0)(svelte@5.19.10): + resolution: {integrity: sha512-yViK9zqQ+H2qZD1w/bH7W8i+bVfKrD8GIFjkFe4Thl6kCT9SlAsXVNmt3jCvQOCsnOhcvYgsoVlRV/Eu6x5nNw==} + peerDependencies: + prettier: ^3.0.0 + svelte: ^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0 + dependencies: + prettier: 3.5.0 + svelte: 5.19.10 + dev: true + + /prettier@3.5.0: + resolution: {integrity: sha512-quyMrVt6svPS7CjQ9gKb3GLEX/rl3BCL2oa/QkNcXv4YNVBC9olt3s+H7ukto06q7B1Qz46PbrKLO34PR6vXcA==} + engines: {node: '>=14'} + hasBin: true + dev: true + + /punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + dev: true + /query-registry@3.0.1: resolution: {integrity: sha512-M9RxRITi2mHMVPU5zysNjctUT8bAPx6ltEXo/ir9+qmiM47Y7f0Ir3+OxUO5OjYAWdicBQRew7RtHtqUXydqlg==} engines: {node: '>=20'} @@ -1346,6 +3189,20 @@ packages: picomatch: 2.3.1 dev: true + /readdirp@4.1.1: + resolution: {integrity: sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==} + engines: {node: '>= 14.18.0'} + dev: true + + /regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + dev: false + + /resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + /resolve@1.22.8: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true @@ -1368,18 +3225,68 @@ packages: glob: 7.2.3 dev: true + /rollup@4.34.6: + resolution: {integrity: sha512-wc2cBWqJgkU3Iz5oztRkQbfVkbxoz5EhnCGOrnJvnLnQ7O0WhQUYyv18qQI79O8L7DdHrrlJNeCHd4VGpnaXKQ==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + dependencies: + '@types/estree': 1.0.6 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.34.6 + '@rollup/rollup-android-arm64': 4.34.6 + '@rollup/rollup-darwin-arm64': 4.34.6 + '@rollup/rollup-darwin-x64': 4.34.6 + '@rollup/rollup-freebsd-arm64': 4.34.6 + '@rollup/rollup-freebsd-x64': 4.34.6 + '@rollup/rollup-linux-arm-gnueabihf': 4.34.6 + '@rollup/rollup-linux-arm-musleabihf': 4.34.6 + '@rollup/rollup-linux-arm64-gnu': 4.34.6 + '@rollup/rollup-linux-arm64-musl': 4.34.6 + '@rollup/rollup-linux-loongarch64-gnu': 4.34.6 + '@rollup/rollup-linux-powerpc64le-gnu': 4.34.6 + '@rollup/rollup-linux-riscv64-gnu': 4.34.6 + '@rollup/rollup-linux-s390x-gnu': 4.34.6 + '@rollup/rollup-linux-x64-gnu': 4.34.6 + '@rollup/rollup-linux-x64-musl': 4.34.6 + '@rollup/rollup-win32-arm64-msvc': 4.34.6 + '@rollup/rollup-win32-ia32-msvc': 4.34.6 + '@rollup/rollup-win32-x64-msvc': 4.34.6 + fsevents: 2.3.3 + dev: true + /run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: queue-microtask: 1.2.3 dev: true + /sade@1.8.1: + resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} + engines: {node: '>=6'} + dependencies: + mri: 1.2.0 + dev: true + /scheduler@0.23.0: resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} dependencies: loose-envify: 1.4.0 dev: false + /seedrandom@3.0.5: + resolution: {integrity: sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==} + dev: false + + /semver@7.7.1: + resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} + engines: {node: '>=10'} + hasBin: true + dev: true + + /set-cookie-parser@2.7.1: + resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} + dev: true + /shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -1397,10 +3304,24 @@ packages: engines: {node: '>=14'} dev: true + /sirv@3.0.0: + resolution: {integrity: sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg==} + engines: {node: '>=18'} + dependencies: + '@polka/url': 1.0.0-next.28 + mrmime: 2.0.0 + totalist: 3.0.1 + dev: true + /source-map-js@1.0.2: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} + /source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + dev: true + /split-on-first@3.0.0: resolution: {integrity: sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA==} engines: {node: '>=12'} @@ -1448,6 +3369,11 @@ packages: ansi-regex: 6.0.1 dev: true + /strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + dev: true + /styled-jsx@5.1.1(react@18.2.0): resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} engines: {node: '>= 12.0.0'} @@ -1479,11 +3405,74 @@ packages: ts-interface-checker: 0.1.13 dev: true + /supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + /supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} dev: true + /svelte-check@4.1.4(svelte@5.19.10)(typescript@5.3.3): + resolution: {integrity: sha512-v0j7yLbT29MezzaQJPEDwksybTE2Ups9rUxEXy92T06TiA0cbqcO8wAOwNUVkFW6B0hsYHA+oAX3BS8b/2oHtw==} + engines: {node: '>= 18.0.0'} + hasBin: true + peerDependencies: + svelte: ^4.0.0 || ^5.0.0-next.0 + typescript: '>=5.0.0' + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + chokidar: 4.0.3 + fdir: 6.4.3 + picocolors: 1.0.0 + sade: 1.8.1 + svelte: 5.19.10 + typescript: 5.3.3 + transitivePeerDependencies: + - picomatch + dev: true + + /svelte-eslint-parser@0.43.0(svelte@5.19.10): + resolution: {integrity: sha512-GpU52uPKKcVnh8tKN5P4UZpJ/fUDndmq7wfsvoVXsyP+aY0anol7Yqo01fyrlaWGMFfm4av5DyrjlaXdLRJvGA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + svelte: + optional: true + dependencies: + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + postcss: 8.5.2 + postcss-scss: 4.0.9(postcss@8.5.2) + svelte: 5.19.10 + dev: true + + /svelte@5.19.10: + resolution: {integrity: sha512-7lId+z36IZWzuo0N0oGOStEPi3/Wv1VQEnIzMmDaLDSlJSruKplhhVr+NaZ0Wh7ZILfOjbeU7PbTjqmQQYZF4A==} + engines: {node: '>=18'} + dependencies: + '@ampproject/remapping': 2.3.0 + '@jridgewell/sourcemap-codec': 1.5.0 + '@types/estree': 1.0.6 + acorn: 8.12.1 + acorn-typescript: 1.4.13(acorn@8.12.1) + aria-query: 5.3.2 + axobject-query: 4.1.0 + clsx: 2.1.1 + esm-env: 1.2.2 + esrap: 1.4.4 + is-reference: 3.0.3 + locate-character: 3.0.0 + magic-string: 0.30.17 + zimmerframe: 1.1.2 + dev: true + /tailwindcss@3.4.1: resolution: {integrity: sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==} engines: {node: '>=14.0.0'} @@ -1515,6 +3504,15 @@ packages: - ts-node dev: true + /tailwindcss@4.0.6: + resolution: {integrity: sha512-mysewHYJKaXgNOW6pp5xon/emCsfAMnO8WMaGKZZ35fomnR/T5gYnRg2/yRTTrtXiEl1tiVkeRt0eMO6HxEZqw==} + dev: true + + /tapable@2.2.1: + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} + dev: true + /tar@6.2.1: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} @@ -1540,6 +3538,10 @@ packages: any-promise: 1.3.0 dev: true + /tiny-emitter@2.1.0: + resolution: {integrity: sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==} + dev: false + /to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -1547,6 +3549,20 @@ packages: is-number: 7.0.0 dev: true + /totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + dev: true + + /ts-api-utils@2.0.1(typescript@5.3.3): + resolution: {integrity: sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + dependencies: + typescript: 5.3.3 + dev: true + /ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} dev: true @@ -1555,11 +3571,39 @@ packages: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} dev: false + /type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + dev: true + /type-detect@4.0.8: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} dev: true + /typed-function@4.2.1: + resolution: {integrity: sha512-EGjWssW7Tsk4DGfE+5yluuljS1OGYWiI1J6e8puZz9nTMM51Oug8CD5Zo4gWMsOhq5BI+1bF+rWTm4Vbj3ivRA==} + engines: {node: '>= 18'} + dev: false + + /typescript-eslint@8.24.0(eslint@9.20.1)(typescript@5.3.3): + resolution: {integrity: sha512-/lmv4366en/qbB32Vz5+kCNZEMf6xYHwh1z48suBwZvAtnXKbP+YhGe8OLE2BqC67LMqKkCNLtjejdwsdW6uOQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' + dependencies: + '@typescript-eslint/eslint-plugin': 8.24.0(@typescript-eslint/parser@8.24.0)(eslint@9.20.1)(typescript@5.3.3) + '@typescript-eslint/parser': 8.24.0(eslint@9.20.1)(typescript@5.3.3) + '@typescript-eslint/utils': 8.24.0(eslint@9.20.1)(typescript@5.3.3) + eslint: 9.20.1 + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + /typescript@5.3.3: resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} engines: {node: '>=14.17'} @@ -1594,6 +3638,12 @@ packages: picocolors: 1.0.0 dev: true + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.3.1 + dev: true + /url-join@5.0.0: resolution: {integrity: sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -1603,15 +3653,99 @@ packages: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: true + /uuid@10.0.0: + resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} + hasBin: true + dev: true + /validate-npm-package-name@5.0.1: resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dev: true + /vite-plugin-top-level-await@1.4.4(vite@6.1.0): + resolution: {integrity: sha512-QyxQbvcMkgt+kDb12m2P8Ed35Sp6nXP+l8ptGrnHV9zgYDUpraO0CPdlqLSeBqvY2DToR52nutDG7mIHuysdiw==} + peerDependencies: + vite: '>=2.8' + dependencies: + '@rollup/plugin-virtual': 3.0.2 + '@swc/core': 1.10.15 + uuid: 10.0.0 + vite: 6.1.0 + transitivePeerDependencies: + - '@swc/helpers' + - rollup + dev: true + + /vite-plugin-wasm@3.4.1(vite@6.1.0): + resolution: {integrity: sha512-ja3nSo2UCkVeitltJGkS3pfQHAanHv/DqGatdI39ja6McgABlpsZ5hVgl6wuR8Qx5etY3T5qgDQhOWzc5RReZA==} + peerDependencies: + vite: ^2 || ^3 || ^4 || ^5 || ^6 + dependencies: + vite: 6.1.0 + dev: true + + /vite@6.1.0: + resolution: {integrity: sha512-RjjMipCKVoR4hVfPY6GQTgveinjNuyLw+qruksLDvA5ktI1150VmcMBKmQaEWJhg/j6Uaf6dNCNA0AfdzUb/hQ==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + dependencies: + esbuild: 0.24.2 + postcss: 8.5.2 + rollup: 4.34.6 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /vitefu@1.0.5(vite@6.1.0): + resolution: {integrity: sha512-h4Vflt9gxODPFNGPwp4zAMZRpZR7eslzwH2c5hn5kNZ5rhnKyRJ50U+yGCdc2IRaBs8O4haIgLNGrV5CrpMsCA==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 + peerDependenciesMeta: + vite: + optional: true + dependencies: + vite: 6.1.0 + dev: true + /wasm-pack@0.12.1: resolution: {integrity: sha512-dIyKWUumPFsGohdndZjDXRFaokUT/kQS+SavbbiXVAvA/eN4riX5QNdB6AhXQx37zNxluxQkuixZUgJ8adKjOg==} hasBin: true - requiresBuild: true dependencies: binary-install: 1.1.0 transitivePeerDependencies: @@ -1626,6 +3760,11 @@ packages: isexe: 2.0.0 dev: true + /word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + dev: true + /wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -1652,12 +3791,26 @@ packages: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: true + /yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + dev: true + /yaml@2.4.0: resolution: {integrity: sha512-j9iR8g+/t0lArF4V6NE/QCfT+CO7iLqrXAHZbJdo+LfjqP1vR8Fg5bSiaq6Q2lOD1AUEVrEVIgABvBFYojJVYQ==} engines: {node: '>= 14'} hasBin: true dev: true + /yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true + + /zimmerframe@1.1.2: + resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==} + dev: true + /zod-package-json@1.0.3: resolution: {integrity: sha512-Mb6GzuRyUEl8X+6V6xzHbd4XV0au/4gOYrYP+CAfHL32uPmGswES+v2YqonZiW1NZWVA3jkssCKSU2knonm/aQ==} engines: {node: '>=20'} From d25faf92338e4a1e2192778845727833704d6791 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 16 Feb 2025 17:06:39 -0700 Subject: [PATCH 005/590] I think this is important? --- package.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 6cbe15d0..1061aeac 100644 --- a/package.json +++ b/package.json @@ -6,5 +6,10 @@ "devDependencies": { "pkg-pr-new": "0.0.15", "wasm-pack": "0.12.1" + }, + "pnpm": { + "onlyBuiltDependencies": [ + "esbuild" + ] } -} +} \ No newline at end of file From 9387f05244ec41b91c5585de4719b3d32e922ab5 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 16 Feb 2025 17:18:26 -0700 Subject: [PATCH 006/590] Yeah we're just gonna switch this for now --- .cargo/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 6330d419..a14c7a91 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -2,4 +2,4 @@ [build] rustflags = [ "--cfg=web_sys_unstable_apis" ] rustdocflags = [ "--cfg=web_sys_unstable_apis" ] -#target = "wasm32-unknown-unknown" +target = "wasm32-unknown-unknown" From 3d2fcc4446f45931e91aa42e19b37e69c1b8285b Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 16 Feb 2025 17:23:07 -0700 Subject: [PATCH 007/590] Remove tokenizers import for now? --- crates/ratchet-datasets/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/ratchet-datasets/Cargo.toml b/crates/ratchet-datasets/Cargo.toml index 79f7f358..5eb88f7f 100644 --- a/crates/ratchet-datasets/Cargo.toml +++ b/crates/ratchet-datasets/Cargo.toml @@ -28,9 +28,9 @@ wasm-opt = ['-O3', '--enable-simd'] ratchet = { path = "../ratchet-core" } ratchet-loader = { path = "../ratchet-loader" } log.workspace = true -tokenizers = { version = "0.19.1", default-features = false, features = [ - "unstable_wasm", -] } +# tokenizers = { version = "0.19.1", default-features = false, features = [ +# "unstable_wasm", +# ] } anyhow.workspace = true memmap2.workspace = true rand.workspace = true From 9718539c5cd54031b4fccfc8e47af298ff006144 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 16 Feb 2025 17:26:28 -0700 Subject: [PATCH 008/590] Revert "Remove tokenizers import for now?" This reverts commit d802af9a7e496acc4167d0e6c60f8cff9421684e. --- crates/ratchet-datasets/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/ratchet-datasets/Cargo.toml b/crates/ratchet-datasets/Cargo.toml index 5eb88f7f..79f7f358 100644 --- a/crates/ratchet-datasets/Cargo.toml +++ b/crates/ratchet-datasets/Cargo.toml @@ -28,9 +28,9 @@ wasm-opt = ['-O3', '--enable-simd'] ratchet = { path = "../ratchet-core" } ratchet-loader = { path = "../ratchet-loader" } log.workspace = true -# tokenizers = { version = "0.19.1", default-features = false, features = [ -# "unstable_wasm", -# ] } +tokenizers = { version = "0.19.1", default-features = false, features = [ + "unstable_wasm", +] } anyhow.workspace = true memmap2.workspace = true rand.workspace = true From d383da9d66da5bdfb387923ce92f5fcb4a9d84a8 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Mon, 17 Feb 2025 22:20:14 -0700 Subject: [PATCH 009/590] Add sort of hacky cached inference --- crates/ratchet-models/src/gpt2/attn.rs | 30 +++-- crates/ratchet-models/src/gpt2/generate.rs | 128 ++++++++++++++------- crates/ratchet-models/src/gpt2/mod.rs | 5 +- crates/ratchet-models/src/gpt2/model.rs | 79 +++++++++++-- crates/ratchet-nn/src/kv_cache.rs | 33 +++++- 5 files changed, 212 insertions(+), 63 deletions(-) diff --git a/crates/ratchet-models/src/gpt2/attn.rs b/crates/ratchet-models/src/gpt2/attn.rs index f92481ca..7d2ca833 100644 --- a/crates/ratchet-models/src/gpt2/attn.rs +++ b/crates/ratchet-models/src/gpt2/attn.rs @@ -104,19 +104,31 @@ impl Module for GPT2SelfAttention { let mut q = q.view(qkv_shape.clone())?.permute(&[0, 2, 1, 3])?; let v = v.view(qkv_shape.clone())?.permute(&[0, 2, 1, 3])?; + let cache_entry = if cache.borrow_mut().use_kv_cache() { + let cache_ref = cache.borrow_mut(); + let entry = cache_ref[block_idx].clone(); + Some(entry) + } else { + None + }; + let offset = cache_entry.as_ref().map(|kv| kv.entries).unwrap_or(0); // Apply RoPE if enabled if let Some(rope) = &self.rope { - q = rope.schedule(RotaryInput { - input: q, - offset: index_pos, - })?; - k = rope.schedule(RotaryInput { - input: k, - offset: index_pos, - })?; + q = rope.schedule(RotaryInput { input: q, offset })?; + k = rope.schedule(RotaryInput { input: k, offset })?; } - let mut att = q.matmul(k, false, true)?.mul(self.softmax_scale.clone())?; + let (k, v) = if let Some(cache_entry) = cache_entry { + let k_cache = cache_entry.k_cache.cache(k, 2, offset)?; + let v_cache = cache_entry.v_cache.cache(v, 2, offset)?; + (k_cache, v_cache) + } else { + (k, v) + }; + + let mut att = q + .matmul(k.clone(), false, true)? + .mul(self.softmax_scale.clone())?; // Apply ALiBi if enabled if let Some(alibi) = &self.alibi { diff --git a/crates/ratchet-models/src/gpt2/generate.rs b/crates/ratchet-models/src/gpt2/generate.rs index 984c242b..5c3e1fbd 100644 --- a/crates/ratchet-models/src/gpt2/generate.rs +++ b/crates/ratchet-models/src/gpt2/generate.rs @@ -1,57 +1,103 @@ -#![cfg(target_arch = "wasm32")] -use crate::gpt2::GPT2; -use crate::TokenOutputStream; -use ndarray::Axis; +use crate::gpt2::{GPT2Input, GPT2}; +use maybe_async::maybe_async; +use ndarray::{Array3, Axis, Ix3}; use ndarray_stats::QuantileExt; use ratchet::{shape, Device, Tensor}; use ratchet_nn::Module; -use tokenizers::Tokenizer; +#[maybe_async] pub async fn generate( model: &mut GPT2, - tokenizer: Tokenizer, - prompt: String, - callback: impl Fn(String), -) -> anyhow::Result<()> { - use web_time::Instant; - log::warn!("Prompt: {}", prompt); - - let mut tos = TokenOutputStream::new(tokenizer); - let encoding = tos.tokenizer().encode(prompt, true).unwrap(); - let mut tokens = encoding - .get_ids() - .iter() - .map(|&x| x as i32) - .collect::>(); - let mut all_tokens = tokens.clone(); - let mut loop_cnt = 0; - let start = Instant::now(); - while tokens[tokens.len() - 1] != 50256 && loop_cnt < 256 { + prompt: Vec, + // The callback now receives: + // - A Vec of the tokens fed in this pass (either the prompt, or the latest token) + // - An ndarray (shape: [1, seq_len, vocab_size]) containing the logits for that pass. + callback: impl Fn(Vec, Array3), + max_tokens: usize, +) -> anyhow::Result>> { + // Preserve original cache setting and enable kv-cache for generation. + let use_kv_cache = model.cache_mut().use_kv_cache(); + model.cache_mut().set_use_kv_cache(true); + + // This vector will accumulate the logits (as ndarray on CPU) from each model call. + let mut all_logits_ndarray: Vec> = Vec::new(); + + // all_tokens holds the entire context (prompt + generated tokens). + let mut all_tokens = prompt.clone(); + // Count only the tokens that are generated (not in the original prompt) + let mut generated_count = 0; + + while generated_count < max_tokens { + // For the first pass, feed the entire prompt. + // For subsequent passes, feed only the latest generated token. + let tokens_to_feed = if generated_count == 0 { + &all_tokens[..] + } else { + &all_tokens[all_tokens.len() - 1..] + }; + + // Build the input tensor from tokens_to_feed. let input = Tensor::from_data( - tokens.clone(), - shape![1, tokens.len()], + tokens_to_feed, + shape![1, tokens_to_feed.len()], model.device.clone(), ); - let result = model.schedule(input)?; - let logits = result.to(&Device::CPU).await?; - model.cache_mut().update(tokens.len()); - tokens = logits + // The index_pos is the total length of the context so far. + let (result, _) = model.schedule(GPT2Input { + x: input, + index_pos: all_tokens.len(), + })?; + + // Bring the logits to the CPU. + let logits_cpu = result.to(&Device::CPU).await?; + + // Update the kv-cache: + // - For the first pass, update with the full length. + // - For subsequent passes, update only with the new token. + if generated_count == 0 { + model.cache_mut().update(all_tokens.len() + 1); + } else { + model.cache_mut().update(1); + } + + // Convert the logits to an owned ndarray. + // The logits have shape [1, seq_len, vocab_size] where: + // - seq_len == prompt.len() on first pass, or 1 on subsequent passes. + let logits_nd = logits_cpu .to_ndarray_view::() - .map_axis(Axis(2), |row| row.argmax_skipnan().unwrap()) - .iter() - .map(|&x| x as i32) - .collect::>(); + .into_owned() + .into_dimensionality::() + .unwrap(); + // Store them in our accumulator. + all_logits_ndarray.push(logits_nd.clone()); + + // *** Stream the current pass via the callback *** + // Pass the tokens that were fed (as a Vec) and the corresponding logits ndarray. + callback(tokens_to_feed.to_vec(), logits_nd.clone()); - if let Some(t) = tos.next_token(tokens[0] as u32)? { - callback(t); + // Extract the logits for the last token in this pass: + // - For the first pass, that's at index (prompt.len() - 1). + // - For later passes, the only token is at index 0. + let seq_len_this_pass = tokens_to_feed.len(); + let last_logits = logits_nd.index_axis(Axis(1), seq_len_this_pass - 1); + // last_logits has shape [1, vocab_size]; take the 0th row. + let vocab_logits = last_logits.index_axis(Axis(0), 0); + // Use argmax_skipnan from ndarray_stats to get the next token id. + let next_token_id = vocab_logits.argmax_skipnan().unwrap() as i32; + + // Stop if the end-of-text token is generated. + if next_token_id == 50256 { + break; } - all_tokens.extend(tokens.clone()); - loop_cnt += 1; + + // Append the generated token to the full context. + all_tokens.push(next_token_id); + generated_count += 1; } - let elapsed = start.elapsed(); - log::warn!("Elapsed: {:?}", elapsed); - log::warn!("Tok/s {}", all_tokens.len() as f64 / elapsed.as_secs_f64()); + + // Clean up: reset model state and restore the original kv-cache setting. model.reset(); - Ok(()) + model.cache_mut().set_use_kv_cache(use_kv_cache); + Ok(all_logits_ndarray) } diff --git a/crates/ratchet-models/src/gpt2/mod.rs b/crates/ratchet-models/src/gpt2/mod.rs index da60582c..2a4edae6 100644 --- a/crates/ratchet-models/src/gpt2/mod.rs +++ b/crates/ratchet-models/src/gpt2/mod.rs @@ -1,5 +1,5 @@ mod attn; -// mod generate; +mod generate; mod linear; mod mlp; mod model; @@ -10,5 +10,4 @@ pub use model::LayerNormPosition; pub use model::PositionalEncoding; pub use model::GPT2; -// #[cfg(target_arch = "wasm32")] -// pub use generate::generate; +pub use generate::generate; diff --git a/crates/ratchet-models/src/gpt2/model.rs b/crates/ratchet-models/src/gpt2/model.rs index e3f12af7..fb81853d 100644 --- a/crates/ratchet-models/src/gpt2/model.rs +++ b/crates/ratchet-models/src/gpt2/model.rs @@ -217,7 +217,7 @@ impl Module for GPT2 { impl GPT2 { const MAX_CACHE: usize = 4096; - pub async fn new(cfg: &Config, vb: VarBuilder<'_>) -> anyhow::Result { + pub async fn new(cfg: &Config, vb: VarBuilder<'_>, use_kv_cache: bool) -> anyhow::Result { let vb_m = vb.pp("model"); let wte = embedding(cfg.vocab_size, cfg.n_embd, vb_m.pp("wte")).await?; @@ -246,7 +246,7 @@ impl GPT2 { let ln_post = layer_norm(cfg.n_embd, Default::default(), vb_m.pp("norm")).await?; let lm_head = linear_no_bias_gpt2(cfg.n_embd, cfg.vocab_size, vb.pp("lm_head")).await?; - let cache_shape = shape![1, n_layers as _, Self::MAX_CACHE, cfg.head_dim() as _]; + let cache_shape = shape![1, cfg.n_head as _, Self::MAX_CACHE, cfg.head_dim() as _]; Ok(Self { wte, @@ -257,13 +257,21 @@ impl GPT2 { lm_head, kv_cache: Rc::new(RefCell::new(KVCache::new::( n_layers as _, - false, + use_kv_cache, cache_shape, vb.device(), ))), device: vb.device().clone(), }) } + + pub fn reset(&mut self) { + self.kv_cache.borrow_mut().reset(); + } + + pub fn cache_mut(&mut self) -> std::cell::RefMut<'_, KVCache> { + (*self.kv_cache).borrow_mut() + } } #[cfg(all(test, not(target_arch = "wasm32"), feature = "pyo3"))] @@ -282,7 +290,11 @@ mod tests { }; use super::GPT2; - use crate::gpt2::model::{Config, GPT2Input, PositionalEncoding}; + use crate::gpt2::{ + generate, + model::{Config, GPT2Input, PositionalEncoding}, + LayerNormPosition, + }; #[test] #[cfg_attr(feature = "ci", ignore)] @@ -308,7 +320,7 @@ mod tests { let varmap = VarMap::new(); let vb = VarBuilder::from_varmap(&varmap, ratchet::DType::F32, &device.clone()); - let model = GPT2::new(&config, vb)?; + let model = GPT2::new(&config, vb, false)?; let params = ParamsAdamW { lr: 1e-4, @@ -377,7 +389,7 @@ mod tests { let varmap = VarMap::new(); let vb = VarBuilder::from_varmap(&varmap, ratchet::DType::F32, &device.clone()); - let model = GPT2::new(&config, vb)?; + let model = GPT2::new(&config, vb, false)?; const BATCH_SIZE: usize = 10; @@ -429,7 +441,7 @@ mod tests { let varmap = VarMap::new(); let vb = VarBuilder::from_varmap(&varmap, DType::F32, &device.clone()); - let model = GPT2::new(&config, vb).unwrap(); + let mut model = GPT2::new(&config, vb, false).unwrap(); let params = ParamsAdamW { lr: 1e-4, @@ -475,6 +487,20 @@ mod tests { println!("{:?} loss: {:?}, norm: {:?}", batch_index, loss_vec[0], "?"); } + + model.reset(); + + generate( + &mut model, + "12,35,07,99,03:134=".chars().map(|c| c as i32).collect(), + |s, _logits| { + println!("{}", s.iter().map(|&c| c as u8 as char).collect::()); + // println!("{:?}", logits); + }, + 24, + ) + .unwrap(); + Ok(()) } @@ -517,7 +543,7 @@ mod tests { let iter = DatasetRandomIter::new(&dataset, false, config.block_size, device.clone()); let batch_iter = Batcher::new_r2(iter).batch_size(BATCH_SIZE); - let model = GPT2::new(&config, vb)?; + let model = GPT2::new(&config, vb, false)?; let params = ParamsAdamW { lr: 0.0001, @@ -556,4 +582,41 @@ mod tests { Ok(()) } + + #[test] + fn generate_from_initialization() -> anyhow::Result<()> { + let _ = env_logger::builder().is_test(true).try_init(); + + let device = Device::request_device(DeviceRequest::GPU).unwrap(); + + let varmap = VarMap::new(); + let vb = VarBuilder::from_varmap(&varmap, ratchet::DType::F32, &device.clone()); + + let config = Config { + vocab_size: 256, + hidden_act: ratchet_nn::Activation::Relu2, + n_embd: 128, + n_layer: 1, + n_head: 1, + block_size: 20, + attention_only: false, + positional_encoding: PositionalEncoding::Learned, + layernorm_position: LayerNormPosition::Pre, + }; + + let mut model = GPT2::new(&config, vb, false)?; + + generate( + &mut model, + "Hello, world".chars().map(|c| c as i32).collect(), + |s, logits| { + println!("{:?}", s); + println!("{:?}", logits); + }, + 24, + ) + .unwrap(); + + Ok(()) + } } diff --git a/crates/ratchet-nn/src/kv_cache.rs b/crates/ratchet-nn/src/kv_cache.rs index cb5a16eb..932cafdb 100644 --- a/crates/ratchet-nn/src/kv_cache.rs +++ b/crates/ratchet-nn/src/kv_cache.rs @@ -24,6 +24,9 @@ pub struct KVCache { use_kv_cache: bool, masks: HashMap, device: Device, + n_layers: usize, + allocated: bool, + shape: Shape, } impl std::ops::Index for KVCache { @@ -42,14 +45,22 @@ impl KVCache { device: &Device, ) -> Self { let mut entries = Vec::with_capacity(n_layers as _); - for _ in 0..n_layers { - entries.push(KVEntry::allocate::(&shape, device)); + // TODO: This is really bad; look at actual patterns for how people do KV caches + let mut allocated = false; + if use_kv_cache { + for _ in 0..n_layers { + entries.push(KVEntry::allocate::(&shape, device)); + } + allocated = true; } KVCache { entries, masks: HashMap::default(), device: device.clone(), + n_layers: n_layers as _, use_kv_cache, + allocated, + shape, } } @@ -69,6 +80,24 @@ impl KVCache { } } + pub fn use_kv_cache(&self) -> bool { + self.use_kv_cache + } + + pub fn set_use_kv_cache(&mut self, use_kv_cache: bool) { + self.use_kv_cache = use_kv_cache; + if !use_kv_cache && self.allocated { + self.entries.clear(); + self.allocated = false; + } else if use_kv_cache && !self.allocated { + for _ in 0..self.n_layers { + self.entries + .push(KVEntry::allocate::(&self.shape, &self.device)); + } + self.allocated = true; + } + } + pub fn mask(&mut self, t: usize) -> anyhow::Result { if let Some(mask) = self.masks.get(&t) { log::debug!("Using existing mask for {:?}", t); From bf43c8091fef0fbaa8d3c058e6029309992fb0e4 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Mon, 17 Feb 2025 22:23:49 -0700 Subject: [PATCH 010/590] Generate batches/toy tasks on frontend --- crates/ratchet-web-train/src/model.rs | 215 ++++++------------ examples/ratchet-train-toy/src/tasks.ts | 186 +++++++++++++++ examples/ratchet-train-toy/src/trainWorker.ts | 20 +- 3 files changed, 275 insertions(+), 146 deletions(-) create mode 100644 examples/ratchet-train-toy/src/tasks.ts diff --git a/crates/ratchet-web-train/src/model.rs b/crates/ratchet-web-train/src/model.rs index 8b6a43be..c6e56ff7 100644 --- a/crates/ratchet-web-train/src/model.rs +++ b/crates/ratchet-web-train/src/model.rs @@ -1,6 +1,6 @@ use anyhow::Result; use async_trait::async_trait; -use ratchet::{DType, Device, DeviceRequest, GradStore, Tensor, Var}; +use ratchet::{shape, DType, Device, DeviceRequest, GradStore, Tensor, Var}; use ratchet_datasets::batcher::IterResult2; use ratchet_datasets::{ nlp::{ @@ -330,7 +330,6 @@ pub struct Trainer { optimizer: Box + Send + Sync>, device: Device, config: TrainerConfig, - batcher: BatcherType<'static>, } #[wasm_bindgen] @@ -380,7 +379,7 @@ impl Trainer { }, }; // Initialize the GPT2 model. - let model = GPT2::new(&gpt2_config, vb) + let model = GPT2::new(&gpt2_config, vb, false) .await .map_err(|e| e.to_string())?; @@ -427,135 +426,66 @@ impl Trainer { _ => Box::new(NoopScheduler::new(base_optimizer)), // Default to no scheduler }; - // Based on the config.dataset value, choose the dataset iterator and wrap it in a Batcher. - // Supported options (case-insensitive): "two_sum" (default), "zeros", "tinystories", "sort", "add", "count", "slapjack", "mod_add" - let batcher = match cfg.dataset.to_lowercase().as_str() { - "zeros" => { - let task = ZerosTask::new(cfg.block_size); - let dataset_iter = ToyTaskIter::new(task, device.clone()); - BatcherType::ToyZeros(Batcher::new_r2(dataset_iter).batch_size(cfg.batch_size)) - } - "sort" => { - let task = SortTask::new(20, cfg.seed); - let dataset_iter = ToyTaskIter::new(task, device.clone()); - BatcherType::ToySort(Batcher::new_r2(dataset_iter).batch_size(cfg.batch_size)) - } - "add" => { - let task = AddTask::new(1000, cfg.seed); - let dataset_iter = ToyTaskIter::new(task, device.clone()); - BatcherType::ToyAdd(Batcher::new_r2(dataset_iter).batch_size(cfg.batch_size)) - } - "count" => { - let task = CountTask::new(10, 'd', cfg.seed); - let dataset_iter = ToyTaskIter::new(task, device.clone()); - BatcherType::ToyCount(Batcher::new_r2(dataset_iter).batch_size(cfg.batch_size)) - } - "slapjack" => { - let task = SlapjackTask::new(48, cfg.seed); - let dataset_iter = ToyTaskIter::new(task, device.clone()); - BatcherType::ToySlapjack(Batcher::new_r2(dataset_iter).batch_size(cfg.batch_size)) - } - "mod_add" => { - let task = ModAddTask::new(113, cfg.seed); - let dataset_iter = ToyTaskIter::new(task, device.clone()); - BatcherType::ToyModAdd(Batcher::new_r2(dataset_iter).batch_size(cfg.batch_size)) - } - // "tinystories" => { - // let dataset = TinyStoriesDataset::new(); - // let dataset_iter = TinyStoriesDatasetRandomIter::new(dataset, device.clone()); - // BatcherEnum::TinyStories(Batcher::new_r2(dataset_iter).batch_size(cfg.batch_size)) - // } - // Default (including "two_sum" and unrecognized values): use the TwoSum task. - _ => { - let task = TwoSumTask::new(5, 5, cfg.seed); - let dataset_iter = ToyTaskIter::new(task, device.clone()); - BatcherType::ToyTwoSum(Batcher::new_r2(dataset_iter).batch_size(cfg.batch_size)) - } - }; - Ok(Trainer { model, optimizer, device, config: cfg, - batcher, }) } - /// Asynchronously retrieve the next batch as a BatchHandle. - /// The returned BatchHandle wraps a Batch consisting of (input, target) tensors, - /// which reside on the device. + /// Train on a single batch of data provided directly from JavaScript #[wasm_bindgen] - pub async fn next_batch(&mut self) -> Result { - // Pull the next (input, target) tuple from the dataset iterator. - let (input, target) = match &mut self.batcher { - BatcherType::ToyTwoSum(batcher) => batcher - .next() - .ok_or_else(|| { - JsValue::from_str("No more data available in the ToyTwoSum dataset") - })? - .map_err(|e| e.to_string())?, - BatcherType::ToyZeros(batcher) => batcher - .next() - .ok_or_else(|| JsValue::from_str("No more data available in the ToyZeros dataset"))? - .map_err(|e| e.to_string())?, - BatcherType::ToySort(batcher) => batcher - .next() - .ok_or_else(|| JsValue::from_str("No more data available in the ToySort dataset"))? - .map_err(|e| e.to_string())?, - BatcherType::ToyAdd(batcher) => batcher - .next() - .ok_or_else(|| JsValue::from_str("No more data available in the ToyAdd dataset"))? - .map_err(|e| e.to_string())?, - BatcherType::ToyCount(batcher) => batcher - .next() - .ok_or_else(|| JsValue::from_str("No more data available in the ToyCount dataset"))? - .map_err(|e| e.to_string())?, - BatcherType::ToySlapjack(batcher) => batcher - .next() - .ok_or_else(|| { - JsValue::from_str("No more data available in the ToySlapjack dataset") - })? - .map_err(|e| e.to_string())?, - BatcherType::ToyModAdd(batcher) => batcher - .next() - .ok_or_else(|| { - JsValue::from_str("No more data available in the ToyModAdd dataset") - })? - .map_err(|e| e.to_string())?, - BatcherType::TinyStories(batcher) => batcher - .next() - .ok_or_else(|| { - JsValue::from_str("No more data available in the TinyStories dataset") - })? - .map_err(|e| e.to_string())?, - }; - Ok(BatchHandle { - inner: Batch { input, target }, - }) - } + pub async fn train_on_batch( + &mut self, + input: JsValue, + target: JsValue, + ) -> Result { + // Convert input and target from JS arrays to Vec> + let input: Vec> = + serde_wasm_bindgen::from_value(input).map_err(|e| JsValue::from(e.to_string()))?; + let target: Vec> = + serde_wasm_bindgen::from_value(target).map_err(|e| JsValue::from(e.to_string()))?; + + // Validate batch dimensions + if input.is_empty() || target.is_empty() { + return Err("Empty batch provided".into()); + } + let batch_size = input.len(); + let seq_len = input[0].len(); + if batch_size != target.len() + || input.iter().any(|x| x.len() != seq_len) + || target.iter().any(|x| x.len() != seq_len) + { + return Err("Inconsistent batch dimensions".into()); + } - /// Asynchronously train on the provided batch. - /// Here we assume that `batch.input` and `batch.target` are already on the correct device. - /// They are passed directly into the model; loss, backpropagation, and the optimizer step - /// are performed, and the scalar loss value is returned. - #[wasm_bindgen] - pub async fn train_step(&mut self, batch_handle: BatchHandle) -> Result { - // Extract the batch from the handle. - let batch = batch_handle.inner; + // Flatten the vectors and create tensors + let input_flat: Vec = input.into_iter().flatten().collect(); + let target_flat: Vec = target.into_iter().flatten().collect(); + + // Create tensors and move to device + let input_tensor = + Tensor::from_data(input_flat, shape![batch_size, seq_len], self.device.clone()); + + let target_tensor = Tensor::from_data( + target_flat, + shape![batch_size, seq_len], + self.device.clone(), + ); // Forward pass: compute logits from the model. let (logits, attn_masks) = self .model .schedule(GPT2Input { - x: batch.input, + x: input_tensor, index_pos: 0, }) .map_err(|e| e.to_string())?; // Flatten the logits and targets. let logits_flat = logits.flatten_to(1).map_err(|e| e.to_string())?; - let target_flat = batch.target.flatten_to(1).map_err(|e| e.to_string())?; + let target_flat = target_tensor.flatten_to(1).map_err(|e| e.to_string())?; // Compute the cross-entropy loss. let loss = cross_entropy(logits_flat, target_flat).map_err(|e| e.to_string())?; @@ -652,35 +582,36 @@ mod tests { wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); - #[wasm_bindgen_test] - async fn train_browser() -> Result<(), JsValue> { - log_init(); - let json_config = r#"{ - "vocab_size": 1024, - "n_embd": 768, - "n_layer": 6, - "n_head": 6, - "block_size": 24, - "batch_size": 1, - "dataset": "two_sum", - "optimizer": { - "lr": 1e-3 - } - }"#; - // Parse the JSON string into a TrainerConfig first to validate it - let config: TrainerConfig = - serde_json::from_str(json_config).map_err(|e| JsValue::from(e.to_string()))?; - // Convert to JsValue - let config_js = - serde_wasm_bindgen::to_value(&config).map_err(|e| JsValue::from(e.to_string()))?; - - let mut trainer = Trainer::new(config_js).await.unwrap(); - for i in 0..10 { - let batch = trainer.next_batch().await.unwrap(); - let loss = trainer.train_step(batch).await.unwrap(); - log::error!("step {}: loss: {:?}", i, loss); - } - // log::error!("config_js: {:?}", config_js); - Ok(()) - } + // #[wasm_bindgen_test] + // async fn train_browser() -> Result<(), JsValue> { + // log_init(); + // let json_config = r#"{ + // "vocab_size": 1024, + // "n_embd": 768, + // "n_layer": 6, + // "n_head": 6, + // "block_size": 24, + // "batch_size": 1, + // "dataset": "two_sum", + // "optimizer": { + // "lr": 1e-3 + // } + // }"#; + // // Parse the JSON string into a TrainerConfig first to validate it + // let config: TrainerConfig = + // serde_json::from_str(json_config).map_err(|e| JsValue::from(e.to_string()))?; + // // Convert to JsValue + // let config_js = + // serde_wasm_bindgen::to_value(&config).map_err(|e| JsValue::from(e.to_string()))?; + + // let mut trainer = Trainer::new(config_js).await.unwrap(); + // for i in 0..10 { + // let input = vec![vec![1, 2, 3, 4]]; + // let target = vec![vec![5, 6, 7, 8]]; + // let loss = trainer.train_on_batch(input, target).await.unwrap(); + // log::error!("step {}: loss: {:?}", i, loss); + // } + // // log::error!("config_js: {:?}", config_js); + // Ok(()) + // } } diff --git a/examples/ratchet-train-toy/src/tasks.ts b/examples/ratchet-train-toy/src/tasks.ts new file mode 100644 index 00000000..cace7811 --- /dev/null +++ b/examples/ratchet-train-toy/src/tasks.ts @@ -0,0 +1,186 @@ +// Type for task generation functions +export type TaskGenerator = ( + maxNum: number, + seqLen: number, + batchSize: number +) => [number[][], number[][]]; + +// Helper function to convert a sequence to tokens +function sequenceToTokens(sequence: string): number[] { + return sequence.split('').map((c) => c.charCodeAt(0)); +} + +// Helper function to pad a number to 2 digits +function pad2(num: number): string { + return num.toString().padStart(2, '0'); +} + +// Helper function to pad a number to 3 digits +function pad3(num: number): string { + return num.toString().padStart(3, '0'); +} + +export function generateTwoSumTask( + maxNum: number, + seqLen: number, + batchSize: number +): [number[][], number[][]] { + const input: number[][] = []; + const target: number[][] = []; + + for (let b = 0; b < batchSize; b++) { + const nums = Array.from({ length: seqLen }, () => Math.floor(Math.random() * maxNum)); + const i = Math.floor(Math.random() * seqLen); + const j = Math.floor(Math.random() * seqLen); + const sum = nums[i] + nums[j]; + + const sequence = `${nums.map((n) => pad2(n)).join(',')}:${pad3(sum)}=${pad2(nums[i])},${pad2(nums[j])}`; + const tokens = sequenceToTokens(sequence); + + input.push(tokens.slice(0, -1)); + target.push(tokens.slice(1)); + } + + return [input, target]; +} + +export function generateSortTask( + maxNum: number, + seqLen: number, + batchSize: number +): [number[][], number[][]] { + const input: number[][] = []; + const target: number[][] = []; + + for (let b = 0; b < batchSize; b++) { + const nums = Array.from({ length: seqLen }, () => Math.floor(Math.random() * maxNum)); + const sorted = [...nums].sort((a, b) => a - b); + + const sequence = `${nums.map((n) => pad2(n)).join(',')}:${sorted.map((n) => pad2(n)).join(',')}`; + const tokens = sequenceToTokens(sequence); + + input.push(tokens.slice(0, -1)); + target.push(tokens.slice(1)); + } + + return [input, target]; +} + +export function generateAddTask( + maxNum: number, + seqLen: number, + batchSize: number +): [number[][], number[][]] { + const input: number[][] = []; + const target: number[][] = []; + + for (let b = 0; b < batchSize; b++) { + const num1 = Math.floor(Math.random() * maxNum); + const num2 = Math.floor(Math.random() * maxNum); + const sum = num1 + num2; + + const sequence = `${pad2(num1)}+${pad2(num2)}=${pad3(sum)}`; + const tokens = sequenceToTokens(sequence); + + input.push(tokens.slice(0, -1)); + target.push(tokens.slice(1)); + } + + return [input, target]; +} + +export function generateModAddTask( + maxNum: number, + seqLen: number, + batchSize: number +): [number[][], number[][]] { + const input: number[][] = []; + const target: number[][] = []; + + for (let b = 0; b < batchSize; b++) { + const num1 = Math.floor(Math.random() * maxNum); + const num2 = Math.floor(Math.random() * maxNum); + const sum = (num1 + num2) % maxNum; + + const sequence = `${pad2(num1)}+${pad2(num2)}%${pad2(maxNum)}=${pad2(sum)}`; + const tokens = sequenceToTokens(sequence); + + input.push(tokens.slice(0, -1)); + target.push(tokens.slice(1)); + } + + return [input, target]; +} + +export function generateCountTask( + maxNum: number, + seqLen: number, + batchSize: number +): [number[][], number[][]] { + const input: number[][] = []; + const target: number[][] = []; + + for (let b = 0; b < batchSize; b++) { + const start = Math.floor(Math.random() * (maxNum - seqLen)); + const sequence = Array.from({ length: seqLen }, (_, i) => pad2(start + i)).join(','); + const tokens = sequenceToTokens(sequence); + + input.push(tokens.slice(0, -1)); + target.push(tokens.slice(1)); + } + + return [input, target]; +} + +export function generateSlapjackTask( + maxNum: number, + seqLen: number, + batchSize: number +): [number[][], number[][]] { + const input: number[][] = []; + const target: number[][] = []; + + for (let b = 0; b < batchSize; b++) { + const nums = Array.from({ length: seqLen }, () => Math.floor(Math.random() * maxNum)); + const jackPosition = Math.floor(Math.random() * seqLen); + nums[jackPosition] = maxNum - 1; // Use max number as "jack" + + const sequence = `${nums.map((n) => pad2(n)).join(',')}:${pad2(jackPosition)}`; + const tokens = sequenceToTokens(sequence); + + input.push(tokens.slice(0, -1)); + target.push(tokens.slice(1)); + } + + return [input, target]; +} + +export function generateZerosTask( + _maxNum: number, + seqLen: number, + batchSize: number +): [number[][], number[][]] { + const input: number[][] = []; + const target: number[][] = []; + + for (let b = 0; b < batchSize; b++) { + const sequence = Array(seqLen).fill('00').join(','); + const tokens = sequenceToTokens(sequence); + + input.push(tokens.slice(0, -1)); + target.push(tokens.slice(1)); + } + + return [input, target]; +} + +// Map of task names to their generator functions +export const taskGenerators: Record = { + two_sum: generateTwoSumTask, + sort: generateSortTask, + add: generateAddTask, + mod_add: generateModAddTask, + count: generateCountTask, + slapjack: generateSlapjackTask, + zeros: generateZerosTask +}; diff --git a/examples/ratchet-train-toy/src/trainWorker.ts b/examples/ratchet-train-toy/src/trainWorker.ts index babcadb2..13233669 100644 --- a/examples/ratchet-train-toy/src/trainWorker.ts +++ b/examples/ratchet-train-toy/src/trainWorker.ts @@ -1,4 +1,5 @@ import { Trainer } from '@ratchet-ml/ratchet-web-train'; +import { taskGenerators } from './tasks'; let trainer: Trainer; let sessionCounter = 0; @@ -47,14 +48,22 @@ async function initializeTrainer(config: TrainerConfig) { self.postMessage({ type: 'modelReady' }); currentSession = sessionCounter++; trainingSessions[currentSession] = true; - trainingLoop(currentSession); + trainingLoop(currentSession, config); } -async function trainingLoop(sessionId: number) { +async function trainingLoop(sessionId: number, config: TrainerConfig) { if (!trainer) { console.error('Trainer not initialized'); return; } + + // Get the appropriate task generator + const taskGenerator = taskGenerators[config.dataset]; + if (!taskGenerator) { + console.error(`Unknown dataset type: ${config.dataset}`); + return; + } + while (trainingSessions[sessionId]) { // Skip if this isn't the current session anymore if (sessionId !== currentSession) { @@ -62,8 +71,11 @@ async function trainingLoop(sessionId: number) { } try { - const batch = await trainer.next_batch(); - const result: Map = await trainer.train_step(batch); + // Generate a new batch using the selected task generator + const [input, target] = taskGenerator(100, 5, 1); // maxNum, seqLen, batchSize + + // Train on the batch + const result = await trainer.train_on_batch(input, target); const attn_masks = result.get('attn_masks') as Map; const usage_bytes = trainer.usage_bytes(); From 0c77b12acb9b1e130b985b3152f61be74ec7b6df Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Mon, 17 Feb 2025 22:24:20 -0700 Subject: [PATCH 011/590] Not clear that this is needed --- .../src/gpu/buffer_allocator/lazy_graph_executor.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/ratchet-core/src/gpu/buffer_allocator/lazy_graph_executor.rs b/crates/ratchet-core/src/gpu/buffer_allocator/lazy_graph_executor.rs index b5660a13..7b328b94 100644 --- a/crates/ratchet-core/src/gpu/buffer_allocator/lazy_graph_executor.rs +++ b/crates/ratchet-core/src/gpu/buffer_allocator/lazy_graph_executor.rs @@ -559,9 +559,9 @@ impl LazyGraphExecutor { return Ok(()); } - // On a cache miss: Clear cache because currently I don't know how to make sure - // allocations are compatible between runs. - self.cache.clear(); + // // On a cache miss: Clear cache because currently I don't know how to make sure + // // allocations are compatible between runs. + // self.cache.clear(); } #[cfg(feature = "plotting")] From 641f8e68139bd98ddd18b6b1924faabdeaa445c5 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Mon, 17 Feb 2025 22:24:36 -0700 Subject: [PATCH 012/590] Update pnpm-lock.yaml --- pnpm-lock.yaml | 2288 ++---------------------------------------------- 1 file changed, 63 insertions(+), 2225 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3f4932cc..acae9285 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,9 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +onlyBuiltDependencies: + - esbuild + importers: .: @@ -67,76 +70,6 @@ importers: specifier: ^5.3.3 version: 5.3.3 - examples/ratchet-train-toy: - dependencies: - chart.js: - specifier: ^4.4.7 - version: 4.4.7 - mathjs: - specifier: ^14.2.1 - version: 14.2.1 - devDependencies: - '@eslint/compat': - specifier: ^1.2.5 - version: 1.2.6(eslint@9.20.1) - '@eslint/js': - specifier: ^9.18.0 - version: 9.20.0 - '@sveltejs/adapter-static': - specifier: ^3.0.8 - version: 3.0.8(@sveltejs/kit@2.17.1) - '@sveltejs/kit': - specifier: ^2.16.0 - version: 2.17.1(@sveltejs/vite-plugin-svelte@5.0.3)(svelte@5.19.10)(vite@6.1.0) - '@sveltejs/vite-plugin-svelte': - specifier: ^5.0.0 - version: 5.0.3(svelte@5.19.10)(vite@6.1.0) - '@tailwindcss/vite': - specifier: ^4.0.0 - version: 4.0.6(vite@6.1.0) - eslint: - specifier: ^9.18.0 - version: 9.20.1 - eslint-config-prettier: - specifier: ^10.0.1 - version: 10.0.1(eslint@9.20.1) - eslint-plugin-svelte: - specifier: ^2.46.1 - version: 2.46.1(eslint@9.20.1)(svelte@5.19.10) - globals: - specifier: ^15.14.0 - version: 15.14.0 - prettier: - specifier: ^3.4.2 - version: 3.5.0 - prettier-plugin-svelte: - specifier: ^3.3.3 - version: 3.3.3(prettier@3.5.0)(svelte@5.19.10) - svelte: - specifier: ^5.0.0 - version: 5.19.10 - svelte-check: - specifier: ^4.0.0 - version: 4.1.4(svelte@5.19.10)(typescript@5.3.3) - tailwindcss: - specifier: ^4.0.0 - version: 4.0.6 - typescript: - specifier: ^5.0.0 - version: 5.3.3 - typescript-eslint: - specifier: ^8.20.0 - version: 8.24.0(eslint@9.20.1)(typescript@5.3.3) - vite: - specifier: ^6.1.0 - version: 6.1.0 - vite-plugin-top-level-await: - specifier: ^1.4.4 - version: 1.4.4(vite@6.1.0) - vite-plugin-wasm: - specifier: ^3.4.1 - version: 3.4.1(vite@6.1.0) - examples/ratchet-whisper: dependencies: '@ffmpeg/ffmpeg': @@ -196,21 +129,6 @@ packages: engines: {node: '>=10'} dev: true - /@ampproject/remapping@2.3.0: - resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} - engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/gen-mapping': 0.3.5 - '@jridgewell/trace-mapping': 0.3.25 - dev: true - - /@babel/runtime@7.26.7: - resolution: {integrity: sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==} - engines: {node: '>=6.9.0'} - dependencies: - regenerator-runtime: 0.14.1 - dev: false - /@bedrock-layout/use-forwarded-ref@1.6.1(react@18.2.0): resolution: {integrity: sha512-GD9A9AFLzFNjr7k6fgerSqxfwDWl+wsPS11PErOKe1zkVz0y7RGC9gzlOiX/JrgpyB3NFHWIuGtoOQqifJQQpw==} peerDependencies: @@ -228,318 +146,6 @@ packages: react: 18.2.0 dev: false - /@esbuild/aix-ppc64@0.24.2: - resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - requiresBuild: true - dev: true - optional: true - - /@esbuild/android-arm64@0.24.2: - resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: true - optional: true - - /@esbuild/android-arm@0.24.2: - resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - requiresBuild: true - dev: true - optional: true - - /@esbuild/android-x64@0.24.2: - resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - requiresBuild: true - dev: true - optional: true - - /@esbuild/darwin-arm64@0.24.2: - resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /@esbuild/darwin-x64@0.24.2: - resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /@esbuild/freebsd-arm64@0.24.2: - resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true - - /@esbuild/freebsd-x64@0.24.2: - resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-arm64@0.24.2: - resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-arm@0.24.2: - resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-ia32@0.24.2: - resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-loong64@0.24.2: - resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-mips64el@0.24.2: - resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-ppc64@0.24.2: - resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-riscv64@0.24.2: - resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-s390x@0.24.2: - resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-x64@0.24.2: - resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/netbsd-arm64@0.24.2: - resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - requiresBuild: true - dev: true - optional: true - - /@esbuild/netbsd-x64@0.24.2: - resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - requiresBuild: true - dev: true - optional: true - - /@esbuild/openbsd-arm64@0.24.2: - resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - requiresBuild: true - dev: true - optional: true - - /@esbuild/openbsd-x64@0.24.2: - resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - requiresBuild: true - dev: true - optional: true - - /@esbuild/sunos-x64@0.24.2: - resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - requiresBuild: true - dev: true - optional: true - - /@esbuild/win32-arm64@0.24.2: - resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@esbuild/win32-ia32@0.24.2: - resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@esbuild/win32-x64@0.24.2: - resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@eslint-community/eslint-utils@4.4.1(eslint@9.20.1): - resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - dependencies: - eslint: 9.20.1 - eslint-visitor-keys: 3.4.3 - dev: true - - /@eslint-community/regexpp@4.12.1: - resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - dev: true - - /@eslint/compat@1.2.6(eslint@9.20.1): - resolution: {integrity: sha512-k7HNCqApoDHM6XzT30zGoETj+D+uUcZUb+IVAJmar3u6bvHf7hhHJcWx09QHj4/a2qrKZMWU0E16tvkiAdv06Q==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^9.10.0 - peerDependenciesMeta: - eslint: - optional: true - dependencies: - eslint: 9.20.1 - dev: true - - /@eslint/config-array@0.19.2: - resolution: {integrity: sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dependencies: - '@eslint/object-schema': 2.1.6 - debug: 4.4.0 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - dev: true - - /@eslint/core@0.10.0: - resolution: {integrity: sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dependencies: - '@types/json-schema': 7.0.15 - dev: true - - /@eslint/core@0.11.0: - resolution: {integrity: sha512-DWUB2pksgNEb6Bz2fggIy1wh6fGgZP4Xyy/Mt0QZPiloKKXerbqq9D3SBQTlCRYOrcRPu4vuz+CGjwdfqxnoWA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dependencies: - '@types/json-schema': 7.0.15 - dev: true - - /@eslint/eslintrc@3.2.0: - resolution: {integrity: sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dependencies: - ajv: 6.12.6 - debug: 4.4.0 - espree: 10.3.0 - globals: 14.0.0 - ignore: 5.3.1 - import-fresh: 3.3.1 - js-yaml: 4.1.0 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - dev: true - - /@eslint/js@9.20.0: - resolution: {integrity: sha512-iZA07H9io9Wn836aVTytRaNqh00Sad+EamwOVJT12GTLw1VGMFV/4JaME+JjLtr9fiGaoWgYnS54wrfWsSs4oQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dev: true - - /@eslint/object-schema@2.1.6: - resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dev: true - - /@eslint/plugin-kit@0.2.5: - resolution: {integrity: sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dependencies: - '@eslint/core': 0.10.0 - levn: 0.4.1 - dev: true - /@ffmpeg/ffmpeg@0.12.6: resolution: {integrity: sha512-4CuXDaqrCga5qBwVtiDDR45y65OGPYZd7VzwGCGz3QLdrQH7xaLYEjU19XL4DTCL0WnTSH8752b8Atyb1SiiLw==} engines: {node: '>=18.x'} @@ -557,34 +163,6 @@ packages: engines: {node: '>=18.x'} dev: false - /@humanfs/core@0.19.1: - resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} - engines: {node: '>=18.18.0'} - dev: true - - /@humanfs/node@0.16.6: - resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} - engines: {node: '>=18.18.0'} - dependencies: - '@humanfs/core': 0.19.1 - '@humanwhocodes/retry': 0.3.1 - dev: true - - /@humanwhocodes/module-importer@1.0.1: - resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} - engines: {node: '>=12.22'} - dev: true - - /@humanwhocodes/retry@0.3.1: - resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} - engines: {node: '>=18.18'} - dev: true - - /@humanwhocodes/retry@0.4.1: - resolution: {integrity: sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==} - engines: {node: '>=18.18'} - dev: true - /@isaacs/cliui@8.0.2: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -637,10 +215,6 @@ packages: type-detect: 4.0.8 dev: true - /@kurkle/color@0.3.4: - resolution: {integrity: sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==} - dev: false - /@next/env@14.1.0: resolution: {integrity: sha512-Py8zIo+02ht82brwwhTg36iogzFqGLPXlRGKQw5s+qP/kMNc4MAyDeEwBKDijk6zTIbegEgu8Qy7C1LboslQAw==} dev: false @@ -858,723 +432,61 @@ packages: dev: true optional: true - /@polka/url@1.0.0-next.28: - resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==} - dev: true + /@swc/helpers@0.5.2: + resolution: {integrity: sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==} + dependencies: + tslib: 2.6.2 + dev: false - /@rollup/plugin-virtual@3.0.2: - resolution: {integrity: sha512-10monEYsBp3scM4/ND4LNH5Rxvh3e/cVeL3jWTgZ2SrQ+BmUoQcopVQvnaMcOnykb1VkxUFuDAN+0FnpTFRy2A==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true + /@types/node@20.11.24: + resolution: {integrity: sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==} + dependencies: + undici-types: 5.26.5 dev: true - /@rollup/rollup-android-arm-eabi@4.34.6: - resolution: {integrity: sha512-+GcCXtOQoWuC7hhX1P00LqjjIiS/iOouHXhMdiDSnq/1DGTox4SpUvO52Xm+div6+106r+TcvOeo/cxvyEyTgg==} - cpu: [arm] - os: [android] - requiresBuild: true + /@types/prop-types@15.7.11: + resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==} dev: true - optional: true - /@rollup/rollup-android-arm64@4.34.6: - resolution: {integrity: sha512-E8+2qCIjciYUnCa1AiVF1BkRgqIGW9KzJeesQqVfyRITGQN+dFuoivO0hnro1DjT74wXLRZ7QF8MIbz+luGaJA==} - cpu: [arm64] - os: [android] - requiresBuild: true + /@types/react-dom@18.2.19: + resolution: {integrity: sha512-aZvQL6uUbIJpjZk4U8JZGbau9KDeAwMfmhyWorxgBkqDIEf6ROjRozcmPIicqsUwPUjbkDfHKgGee1Lq65APcA==} + dependencies: + '@types/react': 18.2.61 dev: true - optional: true - /@rollup/rollup-darwin-arm64@4.34.6: - resolution: {integrity: sha512-z9Ib+OzqN3DZEjX7PDQMHEhtF+t6Mi2z/ueChQPLS/qUMKY7Ybn5A2ggFoKRNRh1q1T03YTQfBTQCJZiepESAg==} - cpu: [arm64] - os: [darwin] - requiresBuild: true + /@types/react@18.2.61: + resolution: {integrity: sha512-NURTN0qNnJa7O/k4XUkEW2yfygA+NxS0V5h1+kp9jPwhzZy95q3ADoGMP0+JypMhrZBTTgjKAUlTctde1zzeQA==} + dependencies: + '@types/prop-types': 15.7.11 + '@types/scheduler': 0.16.8 + csstype: 3.1.3 dev: true - optional: true - /@rollup/rollup-darwin-x64@4.34.6: - resolution: {integrity: sha512-PShKVY4u0FDAR7jskyFIYVyHEPCPnIQY8s5OcXkdU8mz3Y7eXDJPdyM/ZWjkYdR2m0izD9HHWA8sGcXn+Qrsyg==} - cpu: [x64] - os: [darwin] - requiresBuild: true + /@types/scheduler@0.16.8: + resolution: {integrity: sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==} dev: true - optional: true - /@rollup/rollup-freebsd-arm64@4.34.6: - resolution: {integrity: sha512-YSwyOqlDAdKqs0iKuqvRHLN4SrD2TiswfoLfvYXseKbL47ht1grQpq46MSiQAx6rQEN8o8URtpXARCpqabqxGQ==} - cpu: [arm64] - os: [freebsd] - requiresBuild: true + /acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} + engines: {node: '>=0.4.0'} + hasBin: true dev: true - optional: true - /@rollup/rollup-freebsd-x64@4.34.6: - resolution: {integrity: sha512-HEP4CgPAY1RxXwwL5sPFv6BBM3tVeLnshF03HMhJYCNc6kvSqBgTMmsEjb72RkZBAWIqiPUyF1JpEBv5XT9wKQ==} - cpu: [x64] - os: [freebsd] - requiresBuild: true + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} dev: true - optional: true - /@rollup/rollup-linux-arm-gnueabihf@4.34.6: - resolution: {integrity: sha512-88fSzjC5xeH9S2Vg3rPgXJULkHcLYMkh8faix8DX4h4TIAL65ekwuQMA/g2CXq8W+NJC43V6fUpYZNjaX3+IIg==} - cpu: [arm] - os: [linux] - requiresBuild: true + /ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} dev: true - optional: true - /@rollup/rollup-linux-arm-musleabihf@4.34.6: - resolution: {integrity: sha512-wM4ztnutBqYFyvNeR7Av+reWI/enK9tDOTKNF+6Kk2Q96k9bwhDDOlnCUNRPvromlVXo04riSliMBs/Z7RteEg==} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-linux-arm64-gnu@4.34.6: - resolution: {integrity: sha512-9RyprECbRa9zEjXLtvvshhw4CMrRa3K+0wcp3KME0zmBe1ILmvcVHnypZ/aIDXpRyfhSYSuN4EPdCCj5Du8FIA==} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-linux-arm64-musl@4.34.6: - resolution: {integrity: sha512-qTmklhCTyaJSB05S+iSovfo++EwnIEZxHkzv5dep4qoszUMX5Ca4WM4zAVUMbfdviLgCSQOu5oU8YoGk1s6M9Q==} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-linux-loongarch64-gnu@4.34.6: - resolution: {integrity: sha512-4Qmkaps9yqmpjY5pvpkfOerYgKNUGzQpFxV6rnS7c/JfYbDSU0y6WpbbredB5cCpLFGJEqYX40WUmxMkwhWCjw==} - cpu: [loong64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-linux-powerpc64le-gnu@4.34.6: - resolution: {integrity: sha512-Zsrtux3PuaxuBTX/zHdLaFmcofWGzaWW1scwLU3ZbW/X+hSsFbz9wDIp6XvnT7pzYRl9MezWqEqKy7ssmDEnuQ==} - cpu: [ppc64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-linux-riscv64-gnu@4.34.6: - resolution: {integrity: sha512-aK+Zp+CRM55iPrlyKiU3/zyhgzWBxLVrw2mwiQSYJRobCURb781+XstzvA8Gkjg/hbdQFuDw44aUOxVQFycrAg==} - cpu: [riscv64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-linux-s390x-gnu@4.34.6: - resolution: {integrity: sha512-WoKLVrY9ogmaYPXwTH326+ErlCIgMmsoRSx6bO+l68YgJnlOXhygDYSZe/qbUJCSiCiZAQ+tKm88NcWuUXqOzw==} - cpu: [s390x] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-linux-x64-gnu@4.34.6: - resolution: {integrity: sha512-Sht4aFvmA4ToHd2vFzwMFaQCiYm2lDFho5rPcvPBT5pCdC+GwHG6CMch4GQfmWTQ1SwRKS0dhDYb54khSrjDWw==} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-linux-x64-musl@4.34.6: - resolution: {integrity: sha512-zmmpOQh8vXc2QITsnCiODCDGXFC8LMi64+/oPpPx5qz3pqv0s6x46ps4xoycfUiVZps5PFn1gksZzo4RGTKT+A==} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-win32-arm64-msvc@4.34.6: - resolution: {integrity: sha512-3/q1qUsO/tLqGBaD4uXsB6coVGB3usxw3qyeVb59aArCgedSF66MPdgRStUd7vbZOsko/CgVaY5fo2vkvPLWiA==} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-win32-ia32-msvc@4.34.6: - resolution: {integrity: sha512-oLHxuyywc6efdKVTxvc0135zPrRdtYVjtVD5GUm55I3ODxhU/PwkQFD97z16Xzxa1Fz0AEe4W/2hzRtd+IfpOA==} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-win32-x64-msvc@4.34.6: - resolution: {integrity: sha512-0PVwmgzZ8+TZ9oGBmdZoQVXflbvuwzN/HRclujpl4N/q3i+y0lqLw8n1bXA8ru3sApDjlmONaNAuYr38y1Kr9w==} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@sveltejs/adapter-static@3.0.8(@sveltejs/kit@2.17.1): - resolution: {integrity: sha512-YaDrquRpZwfcXbnlDsSrBQNCChVOT9MGuSg+dMAyfsAa1SmiAhrA5jUYUiIMC59G92kIbY/AaQOWcBdq+lh+zg==} - peerDependencies: - '@sveltejs/kit': ^2.0.0 - dependencies: - '@sveltejs/kit': 2.17.1(@sveltejs/vite-plugin-svelte@5.0.3)(svelte@5.19.10)(vite@6.1.0) - dev: true - - /@sveltejs/kit@2.17.1(@sveltejs/vite-plugin-svelte@5.0.3)(svelte@5.19.10)(vite@6.1.0): - resolution: {integrity: sha512-CpoGSLqE2MCmcQwA2CWJvOsZ9vW+p/1H3itrFykdgajUNAEyQPbsaSn7fZb6PLHQwe+07njxje9ss0fjZoCAyw==} - engines: {node: '>=18.13'} - hasBin: true - peerDependencies: - '@sveltejs/vite-plugin-svelte': ^3.0.0 || ^4.0.0-next.1 || ^5.0.0 - svelte: ^4.0.0 || ^5.0.0-next.0 - vite: ^5.0.3 || ^6.0.0 - dependencies: - '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.19.10)(vite@6.1.0) - '@types/cookie': 0.6.0 - cookie: 0.6.0 - devalue: 5.1.1 - esm-env: 1.2.2 - import-meta-resolve: 4.1.0 - kleur: 4.1.5 - magic-string: 0.30.17 - mrmime: 2.0.0 - sade: 1.8.1 - set-cookie-parser: 2.7.1 - sirv: 3.0.0 - svelte: 5.19.10 - vite: 6.1.0 - dev: true - - /@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.0.3)(svelte@5.19.10)(vite@6.1.0): - resolution: {integrity: sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22} - peerDependencies: - '@sveltejs/vite-plugin-svelte': ^5.0.0 - svelte: ^5.0.0 - vite: ^6.0.0 - dependencies: - '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.19.10)(vite@6.1.0) - debug: 4.4.0 - svelte: 5.19.10 - vite: 6.1.0 - transitivePeerDependencies: - - supports-color - dev: true - - /@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.10)(vite@6.1.0): - resolution: {integrity: sha512-MCFS6CrQDu1yGwspm4qtli0e63vaPCehf6V7pIMP15AsWgMKrqDGCPFF/0kn4SP0ii4aySu4Pa62+fIRGFMjgw==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22} - peerDependencies: - svelte: ^5.0.0 - vite: ^6.0.0 - dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.0.3)(svelte@5.19.10)(vite@6.1.0) - debug: 4.4.0 - deepmerge: 4.3.1 - kleur: 4.1.5 - magic-string: 0.30.17 - svelte: 5.19.10 - vite: 6.1.0 - vitefu: 1.0.5(vite@6.1.0) - transitivePeerDependencies: - - supports-color - dev: true - - /@swc/core-darwin-arm64@1.10.15: - resolution: {integrity: sha512-zFdZ6/yHqMCPk7OhLFqHy/MQ1EqJhcZMpNHd1gXYT7VRU3FaqvvKETrUlG3VYl65McPC7AhMRfXPyJ0JO/jARQ==} - engines: {node: '>=10'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /@swc/core-darwin-x64@1.10.15: - resolution: {integrity: sha512-8g4yiQwbr8fxOOjKXdot0dEkE5zgE8uNZudLy/ZyAhiwiZ8pbJ8/wVrDOu6dqbX7FBXAoDnvZ7fwN1jk4C8jdA==} - engines: {node: '>=10'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /@swc/core-linux-arm-gnueabihf@1.10.15: - resolution: {integrity: sha512-rl+eVOltl2+7WXOnvmWBpMgh6aO13G5x0U0g8hjwlmD6ku3Y9iRcThpOhm7IytMEarUp5pQxItNoPq+VUGjVHg==} - engines: {node: '>=10'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@swc/core-linux-arm64-gnu@1.10.15: - resolution: {integrity: sha512-qxWEQeyAJMWJqjaN4hi58WMpPdt3Tn0biSK9CYRegQtvZWCbewr6v2agtSu5AZ2rudeH6OfCWAMDQQeSgn6PJQ==} - engines: {node: '>=10'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@swc/core-linux-arm64-musl@1.10.15: - resolution: {integrity: sha512-QcELd9/+HjZx0WCxRrKcyKGWTiQ0485kFb5w8waxcSNd0d9Lgk4EFfWWVyvIb5gIHpDQmhrgzI/yRaWQX4YSZQ==} - engines: {node: '>=10'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@swc/core-linux-x64-gnu@1.10.15: - resolution: {integrity: sha512-S1+ZEEn3+a/MiMeQqQypbwTGoBG8/sPoCvpNbk+uValyygT+jSn3U0xVr45FbukpmMB+NhBMqfedMLqKA0QnJA==} - engines: {node: '>=10'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@swc/core-linux-x64-musl@1.10.15: - resolution: {integrity: sha512-qW+H9g/2zTJ4jP7NDw4VAALY0ZlNEKzYsEoSj/HKi7k3tYEHjMzsxjfsY9I8WZCft23bBdV3RTCPoxCshaj1CQ==} - engines: {node: '>=10'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@swc/core-win32-arm64-msvc@1.10.15: - resolution: {integrity: sha512-AhRB11aA6LxjIqut+mg7qsu/7soQDmbK6MKR9nP3hgBszpqtXbRba58lr24xIbBCMr+dpo6kgEapWt+t5Po6Zg==} - engines: {node: '>=10'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@swc/core-win32-ia32-msvc@1.10.15: - resolution: {integrity: sha512-UGdh430TQwbDn6KjgvRTg1fO022sbQ4yCCHUev0+5B8uoBwi9a89qAz3emy2m56C8TXxUoihW9Y9OMfaRwPXUw==} - engines: {node: '>=10'} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@swc/core-win32-x64-msvc@1.10.15: - resolution: {integrity: sha512-XJzBCqO1m929qbJsOG7FZXQWX26TnEoMctS3QjuCoyBmkHxxQmZsy78KjMes1aomTcKHCyFYgrRGWgVmk7tT4Q==} - engines: {node: '>=10'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@swc/core@1.10.15: - resolution: {integrity: sha512-/iFeQuNaGdK7mfJbQcObhAhsMqLT7qgMYl7jX2GEIO+VDTejESpzAyKwaMeYXExN8D6e5BRHBCe7M5YlsuzjDA==} - engines: {node: '>=10'} - requiresBuild: true - peerDependencies: - '@swc/helpers': '*' - peerDependenciesMeta: - '@swc/helpers': - optional: true - dependencies: - '@swc/counter': 0.1.3 - '@swc/types': 0.1.17 - optionalDependencies: - '@swc/core-darwin-arm64': 1.10.15 - '@swc/core-darwin-x64': 1.10.15 - '@swc/core-linux-arm-gnueabihf': 1.10.15 - '@swc/core-linux-arm64-gnu': 1.10.15 - '@swc/core-linux-arm64-musl': 1.10.15 - '@swc/core-linux-x64-gnu': 1.10.15 - '@swc/core-linux-x64-musl': 1.10.15 - '@swc/core-win32-arm64-msvc': 1.10.15 - '@swc/core-win32-ia32-msvc': 1.10.15 - '@swc/core-win32-x64-msvc': 1.10.15 - dev: true - - /@swc/counter@0.1.3: - resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} - dev: true - - /@swc/helpers@0.5.2: - resolution: {integrity: sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==} - dependencies: - tslib: 2.6.2 - dev: false - - /@swc/types@0.1.17: - resolution: {integrity: sha512-V5gRru+aD8YVyCOMAjMpWR1Ui577DD5KSJsHP8RAxopAH22jFz6GZd/qxqjO6MJHQhcsjvjOFXyDhyLQUnMveQ==} - dependencies: - '@swc/counter': 0.1.3 - dev: true - - /@tailwindcss/node@4.0.6: - resolution: {integrity: sha512-jb6E0WeSq7OQbVYcIJ6LxnZTeC4HjMvbzFBMCrQff4R50HBlo/obmYNk6V2GCUXDeqiXtvtrQgcIbT+/boB03Q==} - dependencies: - enhanced-resolve: 5.18.1 - jiti: 2.4.2 - tailwindcss: 4.0.6 - dev: true - - /@tailwindcss/oxide-android-arm64@4.0.6: - resolution: {integrity: sha512-xDbym6bDPW3D2XqQqX3PjqW3CKGe1KXH7Fdkc60sX5ZLVUbzPkFeunQaoP+BuYlLc2cC1FoClrIRYnRzof9Sow==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: true - optional: true - - /@tailwindcss/oxide-darwin-arm64@4.0.6: - resolution: {integrity: sha512-1f71/ju/tvyGl5c2bDkchZHy8p8EK/tDHCxlpYJ1hGNvsYihZNurxVpZ0DefpN7cNc9RTT8DjrRoV8xXZKKRjg==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /@tailwindcss/oxide-darwin-x64@4.0.6: - resolution: {integrity: sha512-s/hg/ZPgxFIrGMb0kqyeaqZt505P891buUkSezmrDY6lxv2ixIELAlOcUVTkVh245SeaeEiUVUPiUN37cwoL2g==} - engines: {node: '>= 10'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /@tailwindcss/oxide-freebsd-x64@4.0.6: - resolution: {integrity: sha512-Z3Wo8FWZnmio8+xlcbb7JUo/hqRMSmhQw8IGIRoRJ7GmLR0C+25Wq+bEX/135xe/yEle2lFkhu9JBHd4wZYiig==} - engines: {node: '>= 10'} - cpu: [x64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true - - /@tailwindcss/oxide-linux-arm-gnueabihf@4.0.6: - resolution: {integrity: sha512-SNSwkkim1myAgmnbHs4EjXsPL7rQbVGtjcok5EaIzkHkCAVK9QBQsWeP2Jm2/JJhq4wdx8tZB9Y7psMzHYWCkA==} - engines: {node: '>= 10'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@tailwindcss/oxide-linux-arm64-gnu@4.0.6: - resolution: {integrity: sha512-tJ+mevtSDMQhKlwCCuhsFEFg058kBiSy4TkoeBG921EfrHKmexOaCyFKYhVXy4JtkaeeOcjJnCLasEeqml4i+Q==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@tailwindcss/oxide-linux-arm64-musl@4.0.6: - resolution: {integrity: sha512-IoArz1vfuTR4rALXMUXI/GWWfx2EaO4gFNtBNkDNOYhlTD4NVEwE45nbBoojYiTulajI4c2XH8UmVEVJTOJKxA==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@tailwindcss/oxide-linux-x64-gnu@4.0.6: - resolution: {integrity: sha512-QtsUfLkEAeWAC3Owx9Kg+7JdzE+k9drPhwTAXbXugYB9RZUnEWWx5x3q/au6TvUYcL+n0RBqDEO2gucZRvRFgQ==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@tailwindcss/oxide-linux-x64-musl@4.0.6: - resolution: {integrity: sha512-QthvJqIji2KlGNwLcK/PPYo7w1Wsi/8NK0wAtRGbv4eOPdZHkQ9KUk+oCoP20oPO7i2a6X1aBAFQEL7i08nNMA==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@tailwindcss/oxide-win32-arm64-msvc@4.0.6: - resolution: {integrity: sha512-+oka+dYX8jy9iP00DJ9Y100XsqvbqR5s0yfMZJuPR1H/lDVtDfsZiSix1UFBQ3X1HWxoEEl6iXNJHWd56TocVw==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@tailwindcss/oxide-win32-x64-msvc@4.0.6: - resolution: {integrity: sha512-+o+juAkik4p8Ue/0LiflQXPmVatl6Av3LEZXpBTfg4qkMIbZdhCGWFzHdt2NjoMiLOJCFDddoV6GYaimvK1Olw==} - engines: {node: '>= 10'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@tailwindcss/oxide@4.0.6: - resolution: {integrity: sha512-lVyKV2y58UE9CeKVcYykULe9QaE1dtKdxDEdrTPIdbzRgBk6bdxHNAoDqvcqXbIGXubn3VOl1O/CFF77v/EqSA==} - engines: {node: '>= 10'} - optionalDependencies: - '@tailwindcss/oxide-android-arm64': 4.0.6 - '@tailwindcss/oxide-darwin-arm64': 4.0.6 - '@tailwindcss/oxide-darwin-x64': 4.0.6 - '@tailwindcss/oxide-freebsd-x64': 4.0.6 - '@tailwindcss/oxide-linux-arm-gnueabihf': 4.0.6 - '@tailwindcss/oxide-linux-arm64-gnu': 4.0.6 - '@tailwindcss/oxide-linux-arm64-musl': 4.0.6 - '@tailwindcss/oxide-linux-x64-gnu': 4.0.6 - '@tailwindcss/oxide-linux-x64-musl': 4.0.6 - '@tailwindcss/oxide-win32-arm64-msvc': 4.0.6 - '@tailwindcss/oxide-win32-x64-msvc': 4.0.6 - dev: true - - /@tailwindcss/vite@4.0.6(vite@6.1.0): - resolution: {integrity: sha512-O25vZ/URWbZ2JHdk2o8wH7jOKqEGCsYmX3GwGmYS5DjE4X3mpf93a72Rn7VRnefldNauBzr5z2hfZptmBNtTUQ==} - peerDependencies: - vite: ^5.2.0 || ^6 - dependencies: - '@tailwindcss/node': 4.0.6 - '@tailwindcss/oxide': 4.0.6 - lightningcss: 1.29.1 - tailwindcss: 4.0.6 - vite: 6.1.0 - dev: true - - /@types/cookie@0.6.0: - resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} - dev: true - - /@types/estree@1.0.6: - resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} - dev: true - - /@types/json-schema@7.0.15: - resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - dev: true - - /@types/node@20.11.24: - resolution: {integrity: sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==} - dependencies: - undici-types: 5.26.5 - dev: true - - /@types/prop-types@15.7.11: - resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==} - dev: true - - /@types/react-dom@18.2.19: - resolution: {integrity: sha512-aZvQL6uUbIJpjZk4U8JZGbau9KDeAwMfmhyWorxgBkqDIEf6ROjRozcmPIicqsUwPUjbkDfHKgGee1Lq65APcA==} - dependencies: - '@types/react': 18.2.61 - dev: true - - /@types/react@18.2.61: - resolution: {integrity: sha512-NURTN0qNnJa7O/k4XUkEW2yfygA+NxS0V5h1+kp9jPwhzZy95q3ADoGMP0+JypMhrZBTTgjKAUlTctde1zzeQA==} - dependencies: - '@types/prop-types': 15.7.11 - '@types/scheduler': 0.16.8 - csstype: 3.1.3 - dev: true - - /@types/scheduler@0.16.8: - resolution: {integrity: sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==} - dev: true - - /@typescript-eslint/eslint-plugin@8.24.0(@typescript-eslint/parser@8.24.0)(eslint@9.20.1)(typescript@5.3.3): - resolution: {integrity: sha512-aFcXEJJCI4gUdXgoo/j9udUYIHgF23MFkg09LFz2dzEmU0+1Plk4rQWv/IYKvPHAtlkkGoB3m5e6oUp+JPsNaQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.8.0' - dependencies: - '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.24.0(eslint@9.20.1)(typescript@5.3.3) - '@typescript-eslint/scope-manager': 8.24.0 - '@typescript-eslint/type-utils': 8.24.0(eslint@9.20.1)(typescript@5.3.3) - '@typescript-eslint/utils': 8.24.0(eslint@9.20.1)(typescript@5.3.3) - '@typescript-eslint/visitor-keys': 8.24.0 - eslint: 9.20.1 - graphemer: 1.4.0 - ignore: 5.3.1 - natural-compare: 1.4.0 - ts-api-utils: 2.0.1(typescript@5.3.3) - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/parser@8.24.0(eslint@9.20.1)(typescript@5.3.3): - resolution: {integrity: sha512-MFDaO9CYiard9j9VepMNa9MTcqVvSny2N4hkY6roquzj8pdCBRENhErrteaQuu7Yjn1ppk0v1/ZF9CG3KIlrTA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.8.0' - dependencies: - '@typescript-eslint/scope-manager': 8.24.0 - '@typescript-eslint/types': 8.24.0 - '@typescript-eslint/typescript-estree': 8.24.0(typescript@5.3.3) - '@typescript-eslint/visitor-keys': 8.24.0 - debug: 4.4.0 - eslint: 9.20.1 - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/scope-manager@8.24.0: - resolution: {integrity: sha512-HZIX0UByphEtdVBKaQBgTDdn9z16l4aTUz8e8zPQnyxwHBtf5vtl1L+OhH+m1FGV9DrRmoDuYKqzVrvWDcDozw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dependencies: - '@typescript-eslint/types': 8.24.0 - '@typescript-eslint/visitor-keys': 8.24.0 - dev: true - - /@typescript-eslint/type-utils@8.24.0(eslint@9.20.1)(typescript@5.3.3): - resolution: {integrity: sha512-8fitJudrnY8aq0F1wMiPM1UUgiXQRJ5i8tFjq9kGfRajU+dbPyOuHbl0qRopLEidy0MwqgTHDt6CnSeXanNIwA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.8.0' - dependencies: - '@typescript-eslint/typescript-estree': 8.24.0(typescript@5.3.3) - '@typescript-eslint/utils': 8.24.0(eslint@9.20.1)(typescript@5.3.3) - debug: 4.4.0 - eslint: 9.20.1 - ts-api-utils: 2.0.1(typescript@5.3.3) - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/types@8.24.0: - resolution: {integrity: sha512-VacJCBTyje7HGAw7xp11q439A+zeGG0p0/p2zsZwpnMzjPB5WteaWqt4g2iysgGFafrqvyLWqq6ZPZAOCoefCw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dev: true - - /@typescript-eslint/typescript-estree@8.24.0(typescript@5.3.3): - resolution: {integrity: sha512-ITjYcP0+8kbsvT9bysygfIfb+hBj6koDsu37JZG7xrCiy3fPJyNmfVtaGsgTUSEuTzcvME5YI5uyL5LD1EV5ZQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <5.8.0' - dependencies: - '@typescript-eslint/types': 8.24.0 - '@typescript-eslint/visitor-keys': 8.24.0 - debug: 4.4.0 - fast-glob: 3.3.2 - is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.7.1 - ts-api-utils: 2.0.1(typescript@5.3.3) - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/utils@8.24.0(eslint@9.20.1)(typescript@5.3.3): - resolution: {integrity: sha512-07rLuUBElvvEb1ICnafYWr4hk8/U7X9RDCOqd9JcAMtjh/9oRmcfN4yGzbPVirgMR0+HLVHehmu19CWeh7fsmQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.8.0' - dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.20.1) - '@typescript-eslint/scope-manager': 8.24.0 - '@typescript-eslint/types': 8.24.0 - '@typescript-eslint/typescript-estree': 8.24.0(typescript@5.3.3) - eslint: 9.20.1 - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/visitor-keys@8.24.0: - resolution: {integrity: sha512-kArLq83QxGLbuHrTMoOEWO+l2MwsNS2TGISEdx8xgqpkbytB07XmlQyQdNDrCc1ecSqx0cnmhGvpX+VBwqqSkg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dependencies: - '@typescript-eslint/types': 8.24.0 - eslint-visitor-keys: 4.2.0 - dev: true - - /acorn-jsx@5.3.2(acorn@8.12.1): - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - acorn: 8.12.1 - dev: true - - /acorn-jsx@5.3.2(acorn@8.14.0): - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - acorn: 8.14.0 - dev: true - - /acorn-typescript@1.4.13(acorn@8.12.1): - resolution: {integrity: sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q==} - peerDependencies: - acorn: '>=8.9.0' - dependencies: - acorn: 8.12.1 - dev: true - - /acorn@8.12.1: - resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} - engines: {node: '>=0.4.0'} - hasBin: true - dev: true - - /acorn@8.14.0: - resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} - engines: {node: '>=0.4.0'} - hasBin: true - dev: true - - /ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - dev: true - - /ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - dev: true - - /ansi-regex@6.0.1: - resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} - engines: {node: '>=12'} - dev: true - - /ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - dependencies: - color-convert: 2.0.1 + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 dev: true /ansi-styles@6.2.1: @@ -1598,15 +510,6 @@ packages: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} dev: true - /argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - dev: true - - /aria-query@5.3.2: - resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} - engines: {node: '>= 0.4'} - dev: true - /autoprefixer@10.4.18(postcss@8.4.35): resolution: {integrity: sha512-1DKbDfsr6KUElM6wg+0zRNkB/Q7WcKYAaK+pzXn+Xqmszm/5Xa9coeNdtP88Vi+dPzZnMjhge8GIV49ZQkDa+g==} engines: {node: ^10 || ^12 || >=14} @@ -1631,11 +534,6 @@ packages: - debug dev: true - /axobject-query@4.1.0: - resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} - engines: {node: '>= 0.4'} - dev: true - /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true @@ -1706,11 +604,6 @@ packages: resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} dev: true - /callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} - dev: true - /camelcase-css@2.0.1: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} @@ -1719,21 +612,6 @@ packages: /caniuse-lite@1.0.30001591: resolution: {integrity: sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ==} - /chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - dev: true - - /chart.js@4.4.7: - resolution: {integrity: sha512-pwkcKfdzTMAU/+jNosKhNL2bHtJc/sSmYgVbuGTEDhzkrhmyihmP7vUc/5ZK9WopidMDHNe3Wm7jOd/WhuHWuw==} - engines: {pnpm: '>=8'} - dependencies: - '@kurkle/color': 0.3.4 - dev: false - /chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} @@ -1749,13 +627,6 @@ packages: fsevents: 2.3.3 dev: true - /chokidar@4.0.3: - resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} - engines: {node: '>= 14.16.0'} - dependencies: - readdirp: 4.1.1 - dev: true - /chownr@2.0.0: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} @@ -1769,11 +640,6 @@ packages: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} dev: false - /clsx@2.1.1: - resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} - engines: {node: '>=6'} - dev: true - /color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -1790,34 +656,16 @@ packages: engines: {node: '>= 6'} dev: true - /complex.js@2.4.2: - resolution: {integrity: sha512-qtx7HRhPGSCBtGiST4/WGHuW+zeaND/6Ld+db6PbrulIB1i2Ev/2UPiqcmpQNPSyfBKraC0EOvOKCB5dGZKt3g==} - dev: false - /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} dev: true - /confbox@0.1.7: - resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} - dev: true - - /cookie@0.6.0: - resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} - engines: {node: '>= 0.6'} - dev: true - - /cross-spawn@7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} - engines: {node: '>= 8'} - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - dev: true - - /cross-spawn@7.0.6: - resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + /confbox@0.1.7: + resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} + dev: true + + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} dependencies: path-key: 3.1.1 @@ -1834,50 +682,15 @@ packages: /csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - /debug@4.4.0: - resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - dependencies: - ms: 2.1.3 - dev: true - - /decimal.js@10.5.0: - resolution: {integrity: sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==} - dev: false - /decode-uri-component@0.4.1: resolution: {integrity: sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ==} engines: {node: '>=14.16'} dev: true - /deep-is@0.1.4: - resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - dev: true - - /deepmerge@4.3.1: - resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} - engines: {node: '>=0.10.0'} - dev: true - /deprecation@2.3.1: resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==} dev: true - /detect-libc@1.0.3: - resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} - engines: {node: '>=0.10'} - hasBin: true - dev: true - - /devalue@5.1.1: - resolution: {integrity: sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==} - dev: true - /didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} dev: true @@ -1902,237 +715,11 @@ packages: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} dev: true - /enhanced-resolve@5.18.1: - resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} - engines: {node: '>=10.13.0'} - dependencies: - graceful-fs: 4.2.11 - tapable: 2.2.1 - dev: true - - /esbuild@0.24.2: - resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==} - engines: {node: '>=18'} - hasBin: true - requiresBuild: true - optionalDependencies: - '@esbuild/aix-ppc64': 0.24.2 - '@esbuild/android-arm': 0.24.2 - '@esbuild/android-arm64': 0.24.2 - '@esbuild/android-x64': 0.24.2 - '@esbuild/darwin-arm64': 0.24.2 - '@esbuild/darwin-x64': 0.24.2 - '@esbuild/freebsd-arm64': 0.24.2 - '@esbuild/freebsd-x64': 0.24.2 - '@esbuild/linux-arm': 0.24.2 - '@esbuild/linux-arm64': 0.24.2 - '@esbuild/linux-ia32': 0.24.2 - '@esbuild/linux-loong64': 0.24.2 - '@esbuild/linux-mips64el': 0.24.2 - '@esbuild/linux-ppc64': 0.24.2 - '@esbuild/linux-riscv64': 0.24.2 - '@esbuild/linux-s390x': 0.24.2 - '@esbuild/linux-x64': 0.24.2 - '@esbuild/netbsd-arm64': 0.24.2 - '@esbuild/netbsd-x64': 0.24.2 - '@esbuild/openbsd-arm64': 0.24.2 - '@esbuild/openbsd-x64': 0.24.2 - '@esbuild/sunos-x64': 0.24.2 - '@esbuild/win32-arm64': 0.24.2 - '@esbuild/win32-ia32': 0.24.2 - '@esbuild/win32-x64': 0.24.2 - dev: true - /escalade@3.1.2: resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} engines: {node: '>=6'} dev: true - /escape-latex@1.2.0: - resolution: {integrity: sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==} - dev: false - - /escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - dev: true - - /eslint-compat-utils@0.5.1(eslint@9.20.1): - resolution: {integrity: sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==} - engines: {node: '>=12'} - peerDependencies: - eslint: '>=6.0.0' - dependencies: - eslint: 9.20.1 - semver: 7.7.1 - dev: true - - /eslint-config-prettier@10.0.1(eslint@9.20.1): - resolution: {integrity: sha512-lZBts941cyJyeaooiKxAtzoPHTN+GbQTJFAIdQbRhA4/8whaAraEh47Whw/ZFfrjNSnlAxqfm9i0XVAEkULjCw==} - hasBin: true - peerDependencies: - eslint: '>=7.0.0' - dependencies: - eslint: 9.20.1 - dev: true - - /eslint-plugin-svelte@2.46.1(eslint@9.20.1)(svelte@5.19.10): - resolution: {integrity: sha512-7xYr2o4NID/f9OEYMqxsEQsCsj4KaMy4q5sANaKkAb6/QeCjYFxRmDm2S3YC3A3pl1kyPZ/syOx/i7LcWYSbIw==} - engines: {node: ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^7.0.0 || ^8.0.0-0 || ^9.0.0-0 - svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 - peerDependenciesMeta: - svelte: - optional: true - dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.20.1) - '@jridgewell/sourcemap-codec': 1.5.0 - eslint: 9.20.1 - eslint-compat-utils: 0.5.1(eslint@9.20.1) - esutils: 2.0.3 - known-css-properties: 0.35.0 - postcss: 8.5.2 - postcss-load-config: 3.1.4(postcss@8.5.2) - postcss-safe-parser: 6.0.0(postcss@8.5.2) - postcss-selector-parser: 6.1.2 - semver: 7.7.1 - svelte: 5.19.10 - svelte-eslint-parser: 0.43.0(svelte@5.19.10) - transitivePeerDependencies: - - ts-node - dev: true - - /eslint-scope@7.2.2: - resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - dev: true - - /eslint-scope@8.2.0: - resolution: {integrity: sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - dev: true - - /eslint-visitor-keys@3.4.3: - resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true - - /eslint-visitor-keys@4.2.0: - resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dev: true - - /eslint@9.20.1: - resolution: {integrity: sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - hasBin: true - peerDependencies: - jiti: '*' - peerDependenciesMeta: - jiti: - optional: true - dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.20.1) - '@eslint-community/regexpp': 4.12.1 - '@eslint/config-array': 0.19.2 - '@eslint/core': 0.11.0 - '@eslint/eslintrc': 3.2.0 - '@eslint/js': 9.20.0 - '@eslint/plugin-kit': 0.2.5 - '@humanfs/node': 0.16.6 - '@humanwhocodes/module-importer': 1.0.1 - '@humanwhocodes/retry': 0.4.1 - '@types/estree': 1.0.6 - '@types/json-schema': 7.0.15 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.6 - debug: 4.4.0 - escape-string-regexp: 4.0.0 - eslint-scope: 8.2.0 - eslint-visitor-keys: 4.2.0 - espree: 10.3.0 - esquery: 1.6.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 8.0.0 - find-up: 5.0.0 - glob-parent: 6.0.2 - ignore: 5.3.1 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - json-stable-stringify-without-jsonify: 1.0.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.4 - transitivePeerDependencies: - - supports-color - dev: true - - /esm-env@1.2.2: - resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} - dev: true - - /espree@10.3.0: - resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dependencies: - acorn: 8.14.0 - acorn-jsx: 5.3.2(acorn@8.14.0) - eslint-visitor-keys: 4.2.0 - dev: true - - /espree@9.6.1: - resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - acorn: 8.12.1 - acorn-jsx: 5.3.2(acorn@8.12.1) - eslint-visitor-keys: 3.4.3 - dev: true - - /esquery@1.6.0: - resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} - engines: {node: '>=0.10'} - dependencies: - estraverse: 5.3.0 - dev: true - - /esrap@1.4.4: - resolution: {integrity: sha512-tDN6xP/r/b3WmdpWm7LybrD252hY52IokcycPnO+WHfhFF0+n5AWtcLLK7VNV6m0uYgVRhGVs8OkZwRyfC7HzQ==} - dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 - dev: true - - /esrecurse@4.3.0: - resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} - engines: {node: '>=4.0'} - dependencies: - estraverse: 5.3.0 - dev: true - - /estraverse@5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} - engines: {node: '>=4.0'} - dev: true - - /esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} - dev: true - - /fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - dev: true - /fast-glob@3.3.2: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} engines: {node: '>=8.6.0'} @@ -2144,36 +731,12 @@ packages: micromatch: 4.0.5 dev: true - /fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - dev: true - - /fast-levenshtein@2.0.6: - resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - dev: true - /fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} dependencies: reusify: 1.0.4 dev: true - /fdir@6.4.3: - resolution: {integrity: sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==} - peerDependencies: - picomatch: ^3 || ^4 - peerDependenciesMeta: - picomatch: - optional: true - dev: true - - /file-entry-cache@8.0.0: - resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} - engines: {node: '>=16.0.0'} - dependencies: - flat-cache: 4.0.1 - dev: true - /fill-range@7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} @@ -2186,30 +749,10 @@ packages: engines: {node: '>=14.16'} dev: true - /find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} - dependencies: - locate-path: 6.0.0 - path-exists: 4.0.0 - dev: true - /fix-webm-duration@1.0.5: resolution: {integrity: sha512-b6oula3OfSknx0aWoLsxvp4DVIYbwsf+UAkr6EDAK3iuMYk/OSNKzmeSI61GXK0MmFTEuzle19BPvTxMIKjkZg==} dev: false - /flat-cache@4.0.1: - resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} - engines: {node: '>=16'} - dependencies: - flatted: 3.3.2 - keyv: 4.5.4 - dev: true - - /flatted@3.3.2: - resolution: {integrity: sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==} - dev: true - /follow-redirects@1.15.6: resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} engines: {node: '>=4.0'} @@ -2232,11 +775,6 @@ packages: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} dev: true - /fraction.js@5.2.1: - resolution: {integrity: sha512-Ah6t/7YCYjrPUFUFsOsRLMXAdnYM+aQwmojD2Ayb/Ezr82SwES0vuyQ8qZ3QO8n9j7W14VJuVZZet8U3bhSdQQ==} - engines: {node: '>= 12'} - dev: false - /fs-minipass@2.1.0: resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} engines: {node: '>= 8'} @@ -2252,7 +790,6 @@ packages: 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 @@ -2298,16 +835,6 @@ packages: path-is-absolute: 1.0.1 dev: true - /globals@14.0.0: - resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} - engines: {node: '>=18'} - dev: true - - /globals@15.14.0: - resolution: {integrity: sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==} - engines: {node: '>=18'} - dev: true - /goober@2.1.14(csstype@3.1.3): resolution: {integrity: sha512-4UpC0NdGyAFqLNPnhCT2iHpza2q+RAY3GV85a/mRPdzyPQMsj0KmMMuetdIkzWRbJ+Hgau1EZztq8ImmiMGhsg==} peerDependencies: @@ -2318,15 +845,7 @@ packages: /graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - - /graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - dev: true - - /has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - dev: true + dev: false /hasown@2.0.1: resolution: {integrity: sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==} @@ -2340,23 +859,6 @@ packages: engines: {node: '>= 4'} dev: true - /import-fresh@3.3.1: - resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} - engines: {node: '>=6'} - dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - dev: true - - /import-meta-resolve@4.1.0: - resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==} - dev: true - - /imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - dev: true - /inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. @@ -2404,198 +906,33 @@ packages: engines: {node: '>=0.12.0'} dev: true - /is-reference@3.0.3: - resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} - dependencies: - '@types/estree': 1.0.6 - dev: true - /isbinaryfile@5.0.2: resolution: {integrity: sha512-GvcjojwonMjWbTkfMpnVHVqXW/wKMYDfEpY94/8zy8HFMOqb/VL6oeONq9v87q4ttVlaTLnGXnJD4B5B1OTGIg==} engines: {node: '>= 18.0.0'} dev: true /isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - dev: true - - /jackspeak@2.3.6: - resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} - engines: {node: '>=14'} - dependencies: - '@isaacs/cliui': 8.0.2 - optionalDependencies: - '@pkgjs/parseargs': 0.11.0 - dev: true - - /javascript-natural-sort@0.7.1: - resolution: {integrity: sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==} - dev: false - - /jiti@1.21.0: - resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==} - hasBin: true - dev: true - - /jiti@2.4.2: - resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} - hasBin: true - dev: true - - /js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - dev: false - - /js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} - hasBin: true - dependencies: - argparse: 2.0.1 - dev: true - - /json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - dev: true - - /json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - dev: true - - /json-stable-stringify-without-jsonify@1.0.1: - resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - dev: true - - /keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - dependencies: - json-buffer: 3.0.1 - dev: true - - /kleur@4.1.5: - resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} - engines: {node: '>=6'} - dev: true - - /known-css-properties@0.35.0: - resolution: {integrity: sha512-a/RAk2BfKk+WFGhhOCAYqSiFLc34k8Mt/6NWRI4joER0EYUzXIcFivjjnoD3+XU1DggLn/tZc3DOAgke7l8a4A==} - dev: true - - /levn@0.4.1: - resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} - engines: {node: '>= 0.8.0'} - dependencies: - prelude-ls: 1.2.1 - type-check: 0.4.0 - dev: true - - /lightningcss-darwin-arm64@1.29.1: - resolution: {integrity: sha512-HtR5XJ5A0lvCqYAoSv2QdZZyoHNttBpa5EP9aNuzBQeKGfbyH5+UipLWvVzpP4Uml5ej4BYs5I9Lco9u1fECqw==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /lightningcss-darwin-x64@1.29.1: - resolution: {integrity: sha512-k33G9IzKUpHy/J/3+9MCO4e+PzaFblsgBjSGlpAaFikeBFm8B/CkO3cKU9oI4g+fjS2KlkLM/Bza9K/aw8wsNA==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /lightningcss-freebsd-x64@1.29.1: - resolution: {integrity: sha512-0SUW22fv/8kln2LnIdOCmSuXnxgxVC276W5KLTwoehiO0hxkacBxjHOL5EtHD8BAXg2BvuhsJPmVMasvby3LiQ==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true - - /lightningcss-linux-arm-gnueabihf@1.29.1: - resolution: {integrity: sha512-sD32pFvlR0kDlqsOZmYqH/68SqUMPNj+0pucGxToXZi4XZgZmqeX/NkxNKCPsswAXU3UeYgDSpGhu05eAufjDg==} - engines: {node: '>= 12.0.0'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /lightningcss-linux-arm64-gnu@1.29.1: - resolution: {integrity: sha512-0+vClRIZ6mmJl/dxGuRsE197o1HDEeeRk6nzycSy2GofC2JsY4ifCRnvUWf/CUBQmlrvMzt6SMQNMSEu22csWQ==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /lightningcss-linux-arm64-musl@1.29.1: - resolution: {integrity: sha512-UKMFrG4rL/uHNgelBsDwJcBqVpzNJbzsKkbI3Ja5fg00sgQnHw/VrzUTEc4jhZ+AN2BvQYz/tkHu4vt1kLuJyw==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /lightningcss-linux-x64-gnu@1.29.1: - resolution: {integrity: sha512-u1S+xdODy/eEtjADqirA774y3jLcm8RPtYztwReEXoZKdzgsHYPl0s5V52Tst+GKzqjebkULT86XMSxejzfISw==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /lightningcss-linux-x64-musl@1.29.1: - resolution: {integrity: sha512-L0Tx0DtaNUTzXv0lbGCLB/c/qEADanHbu4QdcNOXLIe1i8i22rZRpbT3gpWYsCh9aSL9zFujY/WmEXIatWvXbw==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /lightningcss-win32-arm64-msvc@1.29.1: - resolution: {integrity: sha512-QoOVnkIEFfbW4xPi+dpdft/zAKmgLgsRHfJalEPYuJDOWf7cLQzYg0DEh8/sn737FaeMJxHZRc1oBreiwZCjog==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /lightningcss-win32-x64-msvc@1.29.1: - resolution: {integrity: sha512-NygcbThNBe4JElP+olyTI/doBNGJvLs3bFCRPdvuCcxZCcCZ71B858IHpdm7L1btZex0FvCmM17FK98Y9MRy1Q==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [win32] - requiresBuild: true + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true - optional: true - /lightningcss@1.29.1: - resolution: {integrity: sha512-FmGoeD4S05ewj+AkhTY+D+myDvXI6eL27FjHIjoyUkO/uw7WZD1fBVs0QxeYWa7E17CUHJaYX/RUGISCtcrG4Q==} - engines: {node: '>= 12.0.0'} + /jackspeak@2.3.6: + resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} + engines: {node: '>=14'} dependencies: - detect-libc: 1.0.3 + '@isaacs/cliui': 8.0.2 optionalDependencies: - lightningcss-darwin-arm64: 1.29.1 - lightningcss-darwin-x64: 1.29.1 - lightningcss-freebsd-x64: 1.29.1 - lightningcss-linux-arm-gnueabihf: 1.29.1 - lightningcss-linux-arm64-gnu: 1.29.1 - lightningcss-linux-arm64-musl: 1.29.1 - lightningcss-linux-x64-gnu: 1.29.1 - lightningcss-linux-x64-musl: 1.29.1 - lightningcss-win32-arm64-msvc: 1.29.1 - lightningcss-win32-x64-msvc: 1.29.1 + '@pkgjs/parseargs': 0.11.0 dev: true + /jiti@1.21.0: + resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==} + hasBin: true + dev: true + + /js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + dev: false + /lilconfig@2.1.0: resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} engines: {node: '>=10'} @@ -2610,21 +947,6 @@ packages: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} dev: true - /locate-character@3.0.0: - resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} - dev: true - - /locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} - dependencies: - p-locate: 5.0.0 - dev: true - - /lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - dev: true - /loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -2637,28 +959,6 @@ packages: engines: {node: 14 || >=16.14} dev: true - /magic-string@0.30.17: - resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} - dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 - dev: true - - /mathjs@14.2.1: - resolution: {integrity: sha512-vARWETUx75+kR2K9qBV20n6NYtGXCuQKX8Zo4+AhJI5LX+ukSM1NYebv+wLnJG8KMvEe9H01sJUyC5bMciA4Tg==} - engines: {node: '>= 18'} - hasBin: true - dependencies: - '@babel/runtime': 7.26.7 - complex.js: 2.4.2 - decimal.js: 10.5.0 - escape-latex: 1.2.0 - fraction.js: 5.2.1 - javascript-natural-sort: 0.7.1 - seedrandom: 3.0.5 - tiny-emitter: 2.1.0 - typed-function: 4.2.1 - dev: false - /merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -2685,13 +985,6 @@ packages: brace-expansion: 2.0.1 dev: true - /minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} - engines: {node: '>=16 || 14 >=14.17'} - dependencies: - brace-expansion: 2.0.1 - dev: true - /minipass@3.3.6: resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} engines: {node: '>=8'} @@ -2732,20 +1025,6 @@ packages: ufo: 1.5.3 dev: true - /mri@1.2.0: - resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} - engines: {node: '>=4'} - dev: true - - /mrmime@2.0.0: - resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==} - engines: {node: '>=10'} - dev: true - - /ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - dev: true - /mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} dependencies: @@ -2759,16 +1038,6 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - /nanoid@3.3.8: - resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - dev: true - - /natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - dev: true - /next@14.1.0(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-wlzrsbfeSU48YQBjZhDzOwhWhGsy+uQycR8bHAOt1LY1bn3zZEcDyHQOEoN3aWzQ8LHCAJ1nqrWCc9XF2+O45Q==} engines: {node: '>=18.17.0'} @@ -2838,44 +1107,6 @@ packages: wrappy: 1.0.2 dev: true - /optionator@0.9.4: - resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} - engines: {node: '>= 0.8.0'} - dependencies: - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.4.1 - prelude-ls: 1.2.1 - type-check: 0.4.0 - word-wrap: 1.2.5 - dev: true - - /p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} - dependencies: - yocto-queue: 0.1.0 - dev: true - - /p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} - dependencies: - p-limit: 3.1.0 - dev: true - - /parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} - dependencies: - callsites: 3.1.0 - dev: true - - /path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - dev: true - /path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} @@ -2905,10 +1136,6 @@ packages: /picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} - /picocolors@1.1.1: - resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - dev: true - /picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} @@ -2967,23 +1194,6 @@ packages: postcss: 8.4.35 dev: true - /postcss-load-config@3.1.4(postcss@8.5.2): - resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} - engines: {node: '>= 10'} - peerDependencies: - postcss: '>=8.0.9' - ts-node: '>=9.0.0' - peerDependenciesMeta: - postcss: - optional: true - ts-node: - optional: true - dependencies: - lilconfig: 2.1.0 - postcss: 8.5.2 - yaml: 1.10.2 - dev: true - /postcss-load-config@4.0.2(postcss@8.4.35): resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} engines: {node: '>= 14'} @@ -3011,24 +1221,6 @@ packages: postcss-selector-parser: 6.0.15 dev: true - /postcss-safe-parser@6.0.0(postcss@8.5.2): - resolution: {integrity: sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==} - engines: {node: '>=12.0'} - peerDependencies: - postcss: ^8.3.3 - dependencies: - postcss: 8.5.2 - dev: true - - /postcss-scss@4.0.9(postcss@8.5.2): - resolution: {integrity: sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==} - engines: {node: '>=12.0'} - peerDependencies: - postcss: ^8.4.29 - dependencies: - postcss: 8.5.2 - dev: true - /postcss-selector-parser@6.0.15: resolution: {integrity: sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==} engines: {node: '>=4'} @@ -3037,14 +1229,6 @@ packages: util-deprecate: 1.0.2 dev: true - /postcss-selector-parser@6.1.2: - resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} - engines: {node: '>=4'} - dependencies: - cssesc: 3.0.0 - util-deprecate: 1.0.2 - dev: true - /postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} dev: true @@ -3067,41 +1251,6 @@ packages: source-map-js: 1.0.2 dev: true - /postcss@8.5.2: - resolution: {integrity: sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==} - engines: {node: ^10 || ^12 || >=14} - dependencies: - nanoid: 3.3.8 - picocolors: 1.1.1 - source-map-js: 1.2.1 - dev: true - - /prelude-ls@1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} - engines: {node: '>= 0.8.0'} - dev: true - - /prettier-plugin-svelte@3.3.3(prettier@3.5.0)(svelte@5.19.10): - resolution: {integrity: sha512-yViK9zqQ+H2qZD1w/bH7W8i+bVfKrD8GIFjkFe4Thl6kCT9SlAsXVNmt3jCvQOCsnOhcvYgsoVlRV/Eu6x5nNw==} - peerDependencies: - prettier: ^3.0.0 - svelte: ^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0 - dependencies: - prettier: 3.5.0 - svelte: 5.19.10 - dev: true - - /prettier@3.5.0: - resolution: {integrity: sha512-quyMrVt6svPS7CjQ9gKb3GLEX/rl3BCL2oa/QkNcXv4YNVBC9olt3s+H7ukto06q7B1Qz46PbrKLO34PR6vXcA==} - engines: {node: '>=14'} - hasBin: true - dev: true - - /punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} - dev: true - /query-registry@3.0.1: resolution: {integrity: sha512-M9RxRITi2mHMVPU5zysNjctUT8bAPx6ltEXo/ir9+qmiM47Y7f0Ir3+OxUO5OjYAWdicBQRew7RtHtqUXydqlg==} engines: {node: '>=20'} @@ -3189,20 +1338,6 @@ packages: picomatch: 2.3.1 dev: true - /readdirp@4.1.1: - resolution: {integrity: sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==} - engines: {node: '>= 14.18.0'} - dev: true - - /regenerator-runtime@0.14.1: - resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} - dev: false - - /resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} - dev: true - /resolve@1.22.8: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true @@ -3225,68 +1360,18 @@ packages: glob: 7.2.3 dev: true - /rollup@4.34.6: - resolution: {integrity: sha512-wc2cBWqJgkU3Iz5oztRkQbfVkbxoz5EhnCGOrnJvnLnQ7O0WhQUYyv18qQI79O8L7DdHrrlJNeCHd4VGpnaXKQ==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - dependencies: - '@types/estree': 1.0.6 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.34.6 - '@rollup/rollup-android-arm64': 4.34.6 - '@rollup/rollup-darwin-arm64': 4.34.6 - '@rollup/rollup-darwin-x64': 4.34.6 - '@rollup/rollup-freebsd-arm64': 4.34.6 - '@rollup/rollup-freebsd-x64': 4.34.6 - '@rollup/rollup-linux-arm-gnueabihf': 4.34.6 - '@rollup/rollup-linux-arm-musleabihf': 4.34.6 - '@rollup/rollup-linux-arm64-gnu': 4.34.6 - '@rollup/rollup-linux-arm64-musl': 4.34.6 - '@rollup/rollup-linux-loongarch64-gnu': 4.34.6 - '@rollup/rollup-linux-powerpc64le-gnu': 4.34.6 - '@rollup/rollup-linux-riscv64-gnu': 4.34.6 - '@rollup/rollup-linux-s390x-gnu': 4.34.6 - '@rollup/rollup-linux-x64-gnu': 4.34.6 - '@rollup/rollup-linux-x64-musl': 4.34.6 - '@rollup/rollup-win32-arm64-msvc': 4.34.6 - '@rollup/rollup-win32-ia32-msvc': 4.34.6 - '@rollup/rollup-win32-x64-msvc': 4.34.6 - fsevents: 2.3.3 - dev: true - /run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: queue-microtask: 1.2.3 dev: true - /sade@1.8.1: - resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} - engines: {node: '>=6'} - dependencies: - mri: 1.2.0 - dev: true - /scheduler@0.23.0: resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} dependencies: loose-envify: 1.4.0 dev: false - /seedrandom@3.0.5: - resolution: {integrity: sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==} - dev: false - - /semver@7.7.1: - resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} - engines: {node: '>=10'} - hasBin: true - dev: true - - /set-cookie-parser@2.7.1: - resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} - dev: true - /shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -3304,24 +1389,10 @@ packages: engines: {node: '>=14'} dev: true - /sirv@3.0.0: - resolution: {integrity: sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg==} - engines: {node: '>=18'} - dependencies: - '@polka/url': 1.0.0-next.28 - mrmime: 2.0.0 - totalist: 3.0.1 - dev: true - /source-map-js@1.0.2: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} - /source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} - engines: {node: '>=0.10.0'} - dev: true - /split-on-first@3.0.0: resolution: {integrity: sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA==} engines: {node: '>=12'} @@ -3369,11 +1440,6 @@ packages: ansi-regex: 6.0.1 dev: true - /strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - dev: true - /styled-jsx@5.1.1(react@18.2.0): resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} engines: {node: '>= 12.0.0'} @@ -3405,74 +1471,11 @@ packages: ts-interface-checker: 0.1.13 dev: true - /supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - dependencies: - has-flag: 4.0.0 - dev: true - /supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} dev: true - /svelte-check@4.1.4(svelte@5.19.10)(typescript@5.3.3): - resolution: {integrity: sha512-v0j7yLbT29MezzaQJPEDwksybTE2Ups9rUxEXy92T06TiA0cbqcO8wAOwNUVkFW6B0hsYHA+oAX3BS8b/2oHtw==} - engines: {node: '>= 18.0.0'} - hasBin: true - peerDependencies: - svelte: ^4.0.0 || ^5.0.0-next.0 - typescript: '>=5.0.0' - dependencies: - '@jridgewell/trace-mapping': 0.3.25 - chokidar: 4.0.3 - fdir: 6.4.3 - picocolors: 1.0.0 - sade: 1.8.1 - svelte: 5.19.10 - typescript: 5.3.3 - transitivePeerDependencies: - - picomatch - dev: true - - /svelte-eslint-parser@0.43.0(svelte@5.19.10): - resolution: {integrity: sha512-GpU52uPKKcVnh8tKN5P4UZpJ/fUDndmq7wfsvoVXsyP+aY0anol7Yqo01fyrlaWGMFfm4av5DyrjlaXdLRJvGA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 - peerDependenciesMeta: - svelte: - optional: true - dependencies: - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - postcss: 8.5.2 - postcss-scss: 4.0.9(postcss@8.5.2) - svelte: 5.19.10 - dev: true - - /svelte@5.19.10: - resolution: {integrity: sha512-7lId+z36IZWzuo0N0oGOStEPi3/Wv1VQEnIzMmDaLDSlJSruKplhhVr+NaZ0Wh7ZILfOjbeU7PbTjqmQQYZF4A==} - engines: {node: '>=18'} - dependencies: - '@ampproject/remapping': 2.3.0 - '@jridgewell/sourcemap-codec': 1.5.0 - '@types/estree': 1.0.6 - acorn: 8.12.1 - acorn-typescript: 1.4.13(acorn@8.12.1) - aria-query: 5.3.2 - axobject-query: 4.1.0 - clsx: 2.1.1 - esm-env: 1.2.2 - esrap: 1.4.4 - is-reference: 3.0.3 - locate-character: 3.0.0 - magic-string: 0.30.17 - zimmerframe: 1.1.2 - dev: true - /tailwindcss@3.4.1: resolution: {integrity: sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==} engines: {node: '>=14.0.0'} @@ -3504,15 +1507,6 @@ packages: - ts-node dev: true - /tailwindcss@4.0.6: - resolution: {integrity: sha512-mysewHYJKaXgNOW6pp5xon/emCsfAMnO8WMaGKZZ35fomnR/T5gYnRg2/yRTTrtXiEl1tiVkeRt0eMO6HxEZqw==} - dev: true - - /tapable@2.2.1: - resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} - engines: {node: '>=6'} - dev: true - /tar@6.2.1: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} @@ -3538,10 +1532,6 @@ packages: any-promise: 1.3.0 dev: true - /tiny-emitter@2.1.0: - resolution: {integrity: sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==} - dev: false - /to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -3549,20 +1539,6 @@ packages: is-number: 7.0.0 dev: true - /totalist@3.0.1: - resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} - engines: {node: '>=6'} - dev: true - - /ts-api-utils@2.0.1(typescript@5.3.3): - resolution: {integrity: sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==} - engines: {node: '>=18.12'} - peerDependencies: - typescript: '>=4.8.4' - dependencies: - typescript: 5.3.3 - dev: true - /ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} dev: true @@ -3571,39 +1547,11 @@ packages: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} dev: false - /type-check@0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} - engines: {node: '>= 0.8.0'} - dependencies: - prelude-ls: 1.2.1 - dev: true - /type-detect@4.0.8: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} dev: true - /typed-function@4.2.1: - resolution: {integrity: sha512-EGjWssW7Tsk4DGfE+5yluuljS1OGYWiI1J6e8puZz9nTMM51Oug8CD5Zo4gWMsOhq5BI+1bF+rWTm4Vbj3ivRA==} - engines: {node: '>= 18'} - dev: false - - /typescript-eslint@8.24.0(eslint@9.20.1)(typescript@5.3.3): - resolution: {integrity: sha512-/lmv4366en/qbB32Vz5+kCNZEMf6xYHwh1z48suBwZvAtnXKbP+YhGe8OLE2BqC67LMqKkCNLtjejdwsdW6uOQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.8.0' - dependencies: - '@typescript-eslint/eslint-plugin': 8.24.0(@typescript-eslint/parser@8.24.0)(eslint@9.20.1)(typescript@5.3.3) - '@typescript-eslint/parser': 8.24.0(eslint@9.20.1)(typescript@5.3.3) - '@typescript-eslint/utils': 8.24.0(eslint@9.20.1)(typescript@5.3.3) - eslint: 9.20.1 - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color - dev: true - /typescript@5.3.3: resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} engines: {node: '>=14.17'} @@ -3638,12 +1586,6 @@ packages: picocolors: 1.0.0 dev: true - /uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - dependencies: - punycode: 2.3.1 - dev: true - /url-join@5.0.0: resolution: {integrity: sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -3653,96 +1595,11 @@ packages: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: true - /uuid@10.0.0: - resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} - hasBin: true - dev: true - /validate-npm-package-name@5.0.1: resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dev: true - /vite-plugin-top-level-await@1.4.4(vite@6.1.0): - resolution: {integrity: sha512-QyxQbvcMkgt+kDb12m2P8Ed35Sp6nXP+l8ptGrnHV9zgYDUpraO0CPdlqLSeBqvY2DToR52nutDG7mIHuysdiw==} - peerDependencies: - vite: '>=2.8' - dependencies: - '@rollup/plugin-virtual': 3.0.2 - '@swc/core': 1.10.15 - uuid: 10.0.0 - vite: 6.1.0 - transitivePeerDependencies: - - '@swc/helpers' - - rollup - dev: true - - /vite-plugin-wasm@3.4.1(vite@6.1.0): - resolution: {integrity: sha512-ja3nSo2UCkVeitltJGkS3pfQHAanHv/DqGatdI39ja6McgABlpsZ5hVgl6wuR8Qx5etY3T5qgDQhOWzc5RReZA==} - peerDependencies: - vite: ^2 || ^3 || ^4 || ^5 || ^6 - dependencies: - vite: 6.1.0 - dev: true - - /vite@6.1.0: - resolution: {integrity: sha512-RjjMipCKVoR4hVfPY6GQTgveinjNuyLw+qruksLDvA5ktI1150VmcMBKmQaEWJhg/j6Uaf6dNCNA0AfdzUb/hQ==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} - hasBin: true - peerDependencies: - '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 - jiti: '>=1.21.0' - less: '*' - lightningcss: ^1.21.0 - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' - terser: ^5.16.0 - tsx: ^4.8.1 - yaml: ^2.4.2 - peerDependenciesMeta: - '@types/node': - optional: true - jiti: - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - tsx: - optional: true - yaml: - optional: true - dependencies: - esbuild: 0.24.2 - postcss: 8.5.2 - rollup: 4.34.6 - optionalDependencies: - fsevents: 2.3.3 - dev: true - - /vitefu@1.0.5(vite@6.1.0): - resolution: {integrity: sha512-h4Vflt9gxODPFNGPwp4zAMZRpZR7eslzwH2c5hn5kNZ5rhnKyRJ50U+yGCdc2IRaBs8O4haIgLNGrV5CrpMsCA==} - peerDependencies: - vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 - peerDependenciesMeta: - vite: - optional: true - dependencies: - vite: 6.1.0 - dev: true - /wasm-pack@0.12.1: resolution: {integrity: sha512-dIyKWUumPFsGohdndZjDXRFaokUT/kQS+SavbbiXVAvA/eN4riX5QNdB6AhXQx37zNxluxQkuixZUgJ8adKjOg==} hasBin: true @@ -3760,11 +1617,6 @@ packages: isexe: 2.0.0 dev: true - /word-wrap@1.2.5: - resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} - engines: {node: '>=0.10.0'} - dev: true - /wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -3791,26 +1643,12 @@ packages: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: true - /yaml@1.10.2: - resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} - engines: {node: '>= 6'} - dev: true - /yaml@2.4.0: resolution: {integrity: sha512-j9iR8g+/t0lArF4V6NE/QCfT+CO7iLqrXAHZbJdo+LfjqP1vR8Fg5bSiaq6Q2lOD1AUEVrEVIgABvBFYojJVYQ==} engines: {node: '>= 14'} hasBin: true dev: true - /yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} - dev: true - - /zimmerframe@1.1.2: - resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==} - dev: true - /zod-package-json@1.0.3: resolution: {integrity: sha512-Mb6GzuRyUEl8X+6V6xzHbd4XV0au/4gOYrYP+CAfHL32uPmGswES+v2YqonZiW1NZWVA3jkssCKSU2knonm/aQ==} engines: {node: '>=20'} From 6e98a9b057c0fc4068b4422966712f6831315be8 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Mon, 17 Feb 2025 22:29:45 -0700 Subject: [PATCH 013/590] Remove more rust batch code --- crates/ratchet-web-train/src/model.rs | 100 -------------------------- 1 file changed, 100 deletions(-) diff --git a/crates/ratchet-web-train/src/model.rs b/crates/ratchet-web-train/src/model.rs index c6e56ff7..058150b7 100644 --- a/crates/ratchet-web-train/src/model.rs +++ b/crates/ratchet-web-train/src/model.rs @@ -175,106 +175,6 @@ fn string_to_activation(s: &str) -> Activation { } } -// ------------------------------------------------------------------- -// Batch and BatchHandle definitions. -// The inner Batch holds the (input, target) tensors on the current device. -// ------------------------------------------------------------------- -pub struct Batch { - pub input: Tensor, - pub target: Tensor, -} - -#[wasm_bindgen(js_name = "Batch")] -pub struct BatchHandle { - inner: Batch, -} - -#[wasm_bindgen] -impl BatchHandle { - /// Asynchronously converts the underlying batch data into a JS object. - /// This operation transfers the tensors to the CPU and then converts them into Vec. - #[wasm_bindgen] - pub async fn to_js(&self) -> Result { - // Transfer the input and target tensors to CPU. - let input_cpu = self - .inner - .input - .to(&Device::CPU) - .await - .map_err(|e| e.to_string())?; - let target_cpu = self - .inner - .target - .to(&Device::CPU) - .await - .map_err(|e| e.to_string())?; - // Convert the tensors to Vec. - let input_vec = input_cpu.to_vec::().map_err(|e| e.to_string())?; - let target_vec = target_cpu.to_vec::().map_err(|e| e.to_string())?; - // Create a JS-friendly object. - let batch_obj = serde_json::json!({ - "input": input_vec, - "target": target_vec, - }); - serde_wasm_bindgen::to_value(&batch_obj).map_err(|e| e.to_string().into()) - } -} - -// Updated to: - -enum BatcherType<'a> { - ToyTwoSum( - Batcher< - IterResult2< - ratchet_datasets::nlp::toy::ToyTaskIter, - >, - >, - ), - ToyZeros( - Batcher< - IterResult2< - ratchet_datasets::nlp::toy::ToyTaskIter, - >, - >, - ), - ToySort( - Batcher< - IterResult2< - ratchet_datasets::nlp::toy::ToyTaskIter, - >, - >, - ), - ToyAdd( - Batcher< - IterResult2< - ratchet_datasets::nlp::toy::ToyTaskIter, - >, - >, - ), - ToyCount( - Batcher< - IterResult2< - ratchet_datasets::nlp::toy::ToyTaskIter, - >, - >, - ), - ToySlapjack( - Batcher< - IterResult2< - ratchet_datasets::nlp::toy::ToyTaskIter, - >, - >, - ), - ToyModAdd( - Batcher< - IterResult2< - ratchet_datasets::nlp::toy::ToyTaskIter, - >, - >, - ), - TinyStories(Batcher>>), -} - /// An enum for optimizer configurations #[derive(Debug, Clone)] pub enum OptimizerConfigEnum { From 91883791cd0d284aab72b75fbda7b5173f1105b0 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Mon, 17 Feb 2025 22:58:13 -0700 Subject: [PATCH 014/590] Add web-visible generation api --- crates/ratchet-web-train/src/model.rs | 59 +++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/crates/ratchet-web-train/src/model.rs b/crates/ratchet-web-train/src/model.rs index 058150b7..d93b554d 100644 --- a/crates/ratchet-web-train/src/model.rs +++ b/crates/ratchet-web-train/src/model.rs @@ -1,5 +1,6 @@ use anyhow::Result; use async_trait::async_trait; +use js_sys::Function; use ratchet::{shape, DType, Device, DeviceRequest, GradStore, Tensor, Var}; use ratchet_datasets::batcher::IterResult2; use ratchet_datasets::{ @@ -14,6 +15,7 @@ use ratchet_datasets::{ }, Batcher, }; +use ratchet_models::gpt2::generate; use ratchet_models::gpt2::{Config, GPT2Input, PositionalEncoding}; use ratchet_models::gpt2::{LayerNormPosition, GPT2}; use ratchet_nn::{ @@ -430,6 +432,63 @@ impl Trainer { pub fn usage_bytes(&self) -> u64 { self.device.try_gpu().unwrap().usage_bytes() } + + /// Autoregressive generation with streaming callback. + /// `prompt` is a JS array of i32 tokens. + /// `max_tokens` is how many tokens to generate. + /// `callback` is invoked once per model call with: + /// callback(tokens_to_feed, { shape: [...], data: [...] }) + #[wasm_bindgen] + pub async fn generate( + &mut self, + prompt: JsValue, + max_tokens: usize, + callback: Function, + ) -> Result<(), JsValue> { + // Convert prompt from JsValue -> Vec + let prompt_vec: Vec = + serde_wasm_bindgen::from_value(prompt).map_err(|e| JsValue::from(e.to_string()))?; + + // Call existing GPT-2 generate + generate( + &mut self.model, + prompt_vec, + // This closure is invoked for each new pass with the tokens we just fed + // and an ndarray of logits. We serialize them to JS inside. + |tokens, logits_nd| { + // Convert the tokens to JS + let tokens_js = serde_wasm_bindgen::to_value(&tokens).unwrap_or(JsValue::NULL); + + // Turn the [1, seq_len, vocab_size] logits into a plain object + let shape = vec![ + logits_nd.shape()[0], + logits_nd.shape()[1], + logits_nd.shape()[2], + ]; + let data: Vec = logits_nd.into_iter().collect(); + + let logits_obj = js_sys::Object::new(); + let _ = js_sys::Reflect::set( + &logits_obj, + &JsValue::from_str("shape"), + &serde_wasm_bindgen::to_value(&shape).unwrap_or(JsValue::NULL), + ); + let _ = js_sys::Reflect::set( + &logits_obj, + &JsValue::from_str("data"), + &serde_wasm_bindgen::to_value(&data).unwrap_or(JsValue::NULL), + ); + + // Finally, call the JS callback with (tokens, logitsObj) + let _ = callback.call2(&JsValue::NULL, &tokens_js, &logits_obj); + }, + max_tokens, + ) + .await + .map_err(|e| JsValue::from(e.to_string()))?; + + Ok(()) + } } /// A scheduler that does nothing, just passes through to the underlying optimizer. From 8673a29f994da32b4ecaf2b197c61e27d78516bd Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Tue, 18 Feb 2025 04:17:01 -0700 Subject: [PATCH 015/590] Cache non mark_step forward passes --- .../src/gpu/buffer_allocator/lazy_graph_executor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ratchet-core/src/gpu/buffer_allocator/lazy_graph_executor.rs b/crates/ratchet-core/src/gpu/buffer_allocator/lazy_graph_executor.rs index 7b328b94..03603bab 100644 --- a/crates/ratchet-core/src/gpu/buffer_allocator/lazy_graph_executor.rs +++ b/crates/ratchet-core/src/gpu/buffer_allocator/lazy_graph_executor.rs @@ -153,7 +153,7 @@ impl LazyGraphExecutor { tensors.into_iter().map(|t| (t.id(), t.clone())).collect(), None, gpu_device, - false, + true, ) } From d44a4d21c9b71275627968070783a1e6d6ab300b Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Tue, 18 Feb 2025 04:17:25 -0700 Subject: [PATCH 016/590] Provisionally mark 256 as the end of sequence token --- crates/ratchet-models/src/gpt2/generate.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ratchet-models/src/gpt2/generate.rs b/crates/ratchet-models/src/gpt2/generate.rs index 5c3e1fbd..43b71898 100644 --- a/crates/ratchet-models/src/gpt2/generate.rs +++ b/crates/ratchet-models/src/gpt2/generate.rs @@ -27,7 +27,7 @@ pub async fn generate( // Count only the tokens that are generated (not in the original prompt) let mut generated_count = 0; - while generated_count < max_tokens { + while generated_count < max_tokens && all_tokens[all_tokens.len() - 1] != 256 { // For the first pass, feed the entire prompt. // For subsequent passes, feed only the latest generated token. let tokens_to_feed = if generated_count == 0 { From ea7ea879eff1513875500d7993101e479ac4db51 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Tue, 18 Feb 2025 04:19:51 -0700 Subject: [PATCH 017/590] Return per-token losses for visualization --- crates/ratchet-nn/src/loss.rs | 3 +-- crates/ratchet-web-train/src/model.rs | 33 +++++++++++++++++++-------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/crates/ratchet-nn/src/loss.rs b/crates/ratchet-nn/src/loss.rs index ea6e66d9..f86a27a9 100644 --- a/crates/ratchet-nn/src/loss.rs +++ b/crates/ratchet-nn/src/loss.rs @@ -14,7 +14,6 @@ pub fn nll(inp: Tensor, target: Tensor) -> anyhow::Result { dims => anyhow::bail!("the target tensor should have two dimensions ({dims:?})"), } inp.gather(target.clone().unsqueeze(1)?, 1)? - .sum_all()? .affine(-1f32 / b_sz as f32, 0.) } @@ -31,7 +30,7 @@ pub fn cross_entropy(inp: Tensor, target: Tensor) -> anyhow::Result { anyhow::bail!("cross_entropy expects an input tensor of rank 2") } let inp = log_softmax(inp, 1)?; - nll(inp, target) + nll(inp, target)?.sum_all() } #[cfg(all(test, feature = "pyo3"))] diff --git a/crates/ratchet-web-train/src/model.rs b/crates/ratchet-web-train/src/model.rs index d93b554d..e0199212 100644 --- a/crates/ratchet-web-train/src/model.rs +++ b/crates/ratchet-web-train/src/model.rs @@ -19,8 +19,9 @@ use ratchet_models::gpt2::generate; use ratchet_models::gpt2::{Config, GPT2Input, PositionalEncoding}; use ratchet_models::gpt2::{LayerNormPosition, GPT2}; use ratchet_nn::{ - clip_grad_norm, cross_entropy, Activation, AdamW, ConstantLR, CosineAnnealingLR, LRScheduler, - LRSchedulerCore, LinearLR, Module, Optimizer, ParamsAdamW, VarBuilder, VarMap, SGD, + clip_grad_norm, cross_entropy, log_softmax, nll, Activation, AdamW, ConstantLR, + CosineAnnealingLR, LRScheduler, LRSchedulerCore, LinearLR, Module, Optimizer, ParamsAdamW, + VarBuilder, VarMap, SGD, }; use serde::{Deserialize, Serialize}; use std::iter::Iterator; @@ -385,15 +386,15 @@ impl Trainer { }) .map_err(|e| e.to_string())?; - // Flatten the logits and targets. - let logits_flat = logits.flatten_to(1).map_err(|e| e.to_string())?; + // Flatten the logits and targets, compute the cross-entropy loss. + let logits_flat = logits.clone().flatten_to(1).map_err(|e| e.to_string())?; let target_flat = target_tensor.flatten_to(1).map_err(|e| e.to_string())?; - - // Compute the cross-entropy loss. - let loss = cross_entropy(logits_flat, target_flat).map_err(|e| e.to_string())?; + let inp = log_softmax(logits_flat, 1).map_err(|e| e.to_string())?; + let loss = nll(inp, target_flat).map_err(|e| e.to_string())?; + let loss_summed = loss.clone().sum_all().map_err(|e| e.to_string())?; // Backpropagate to compute gradients. - let grads = loss.backward().map_err(|e| e.to_string())?; + let grads = loss_summed.backward().map_err(|e| e.to_string())?; // Run an optimizer step (updating model parameters). self.optimizer @@ -415,13 +416,25 @@ impl Trainer { let attn_masks_shape = attn_masks_cpu.shape().to_vec(); let attn_masks_data = attn_masks_cpu.to_vec::().map_err(|e| e.to_string())?; - // Create a JS-friendly object with both loss and attention masks + // Add logits to the result: + let logits_cpu = logits.to(&Device::CPU).await.map_err(|e| e.to_string())?; + let logits_shape = logits_cpu.shape().to_vec(); + let logits_data = logits_cpu.to_vec::().map_err(|e| e.to_string())?; + + // Build a JS-friendly JSON with loss, LR, attn masks, and logits let result = serde_json::json!({ - "loss": loss_vec[0], + "loss": { + "tokens": loss_vec, + "total": loss_vec.iter().sum::(), + }, "learning_rate": self.optimizer.get_lr(), "attn_masks": { "data": attn_masks_data, "shape": attn_masks_shape, + }, + "logits": { + "data": logits_data, + "shape": logits_shape, } }); From 0776727b12fc2d5046f53be7032e17734ea5c1df Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Tue, 18 Feb 2025 04:21:05 -0700 Subject: [PATCH 018/590] Allow running generate without streaming --- crates/ratchet-web-train/src/model.rs | 148 ++++++++++++++++++-------- 1 file changed, 102 insertions(+), 46 deletions(-) diff --git a/crates/ratchet-web-train/src/model.rs b/crates/ratchet-web-train/src/model.rs index e0199212..d91a8600 100644 --- a/crates/ratchet-web-train/src/model.rs +++ b/crates/ratchet-web-train/src/model.rs @@ -446,61 +446,117 @@ impl Trainer { self.device.try_gpu().unwrap().usage_bytes() } - /// Autoregressive generation with streaming callback. - /// `prompt` is a JS array of i32 tokens. - /// `max_tokens` is how many tokens to generate. - /// `callback` is invoked once per model call with: - /// callback(tokens_to_feed, { shape: [...], data: [...] }) + /// Autoregressive generation with an optional streaming callback. + /// + /// - `prompt` is a JS array of i32 tokens. + /// - `max_tokens` is how many tokens to generate. + /// - `callback` is optional; if provided, it is invoked once per model call with: + /// `callback(tokens_to_feed, { shape: [...], data: [...] })` + /// - If `callback` is not provided (i.e., `null` or `undefined`), + /// we accumulate the final tokens and logits internally, and return them as a single + /// JS object of the form: + /// ```json + /// { + /// "tokens": [...], + /// "logits": { + /// "shape": [1, total_seq_len, vocab_size], + /// "data": [...] + /// } + /// } + /// ``` #[wasm_bindgen] pub async fn generate( &mut self, prompt: JsValue, max_tokens: usize, - callback: Function, - ) -> Result<(), JsValue> { + callback: Option, + ) -> Result { // Convert prompt from JsValue -> Vec let prompt_vec: Vec = serde_wasm_bindgen::from_value(prompt).map_err(|e| JsValue::from(e.to_string()))?; - // Call existing GPT-2 generate - generate( - &mut self.model, - prompt_vec, - // This closure is invoked for each new pass with the tokens we just fed - // and an ndarray of logits. We serialize them to JS inside. - |tokens, logits_nd| { - // Convert the tokens to JS - let tokens_js = serde_wasm_bindgen::to_value(&tokens).unwrap_or(JsValue::NULL); - - // Turn the [1, seq_len, vocab_size] logits into a plain object - let shape = vec![ - logits_nd.shape()[0], - logits_nd.shape()[1], - logits_nd.shape()[2], - ]; - let data: Vec = logits_nd.into_iter().collect(); - - let logits_obj = js_sys::Object::new(); - let _ = js_sys::Reflect::set( - &logits_obj, - &JsValue::from_str("shape"), - &serde_wasm_bindgen::to_value(&shape).unwrap_or(JsValue::NULL), - ); - let _ = js_sys::Reflect::set( - &logits_obj, - &JsValue::from_str("data"), - &serde_wasm_bindgen::to_value(&data).unwrap_or(JsValue::NULL), - ); - - // Finally, call the JS callback with (tokens, logitsObj) - let _ = callback.call2(&JsValue::NULL, &tokens_js, &logits_obj); - }, - max_tokens, - ) - .await - .map_err(|e| JsValue::from(e.to_string()))?; - - Ok(()) + // If a callback is provided, we do the streaming approach. + if let Some(callback_fn) = callback { + // Call existing GPT-2 generate with a streaming closure: + generate( + &mut self.model, + prompt_vec, + |tokens, logits_nd| { + // Convert the tokens to JS + let tokens_js = serde_wasm_bindgen::to_value(&tokens).unwrap_or(JsValue::NULL); + + // Convert the logits to a {shape, data} object + let shape = vec![ + logits_nd.shape()[0], + logits_nd.shape()[1], + logits_nd.shape()[2], + ]; + let data: Vec = logits_nd.into_iter().collect(); + + let logits_obj = js_sys::Object::new(); + let _ = js_sys::Reflect::set( + &logits_obj, + &JsValue::from_str("shape"), + &serde_wasm_bindgen::to_value(&shape).unwrap_or(JsValue::NULL), + ); + let _ = js_sys::Reflect::set( + &logits_obj, + &JsValue::from_str("data"), + &serde_wasm_bindgen::to_value(&data).unwrap_or(JsValue::NULL), + ); + + // Finally, call the JS callback with (tokens, logitsObj) + let _ = callback_fn.call2(&JsValue::NULL, &tokens_js, &logits_obj); + }, + max_tokens, + ) + .await + .map_err(|e| JsValue::from(e.to_string()))?; + + // Return undefined if we're streaming via callback + Ok(JsValue::UNDEFINED) + } else { + // No callback was provided, so we accumulate only the final tokens/logits and return them. + let mut final_tokens: Vec = Vec::new(); + let mut final_logits_data: Vec = Vec::new(); + let mut final_logits_shape: Vec = Vec::new(); + + let prompt_len = prompt_vec.len(); + + // We capture these as mut so we can overwrite them at each step with the latest pass + generate( + &mut self.model, + prompt_vec, + |tokens, logits_nd| { + // Update tokens + final_tokens.extend_from_slice(&tokens); + + // Convert the logits into shape/data only for the final pass + final_logits_shape.extend_from_slice(&[ + logits_nd.shape()[0], + logits_nd.shape()[1], + logits_nd.shape()[2], + ]); + + final_logits_data.extend(logits_nd.into_iter()); + }, + max_tokens, + ) + .await + .map_err(|e| JsValue::from(e.to_string()))?; + + // Build a JSON object with final tokens and final logits + let result = serde_json::json!({ + "tokens": final_tokens[prompt_len..].to_vec(), + "logits": { + "shape": final_logits_shape, + "data": final_logits_data, + }, + }); + + // Convert to JsValue + Ok(serde_wasm_bindgen::to_value(&result).map_err(|e| e.to_string())?) + } } } From 131b37242995f335a52630c2b9970258f4d488eb Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Tue, 18 Feb 2025 04:26:03 -0700 Subject: [PATCH 019/590] Rework task generation to be configurable --- .../ratchet-train-toy/src/routes/+page.svelte | 57 +- examples/ratchet-train-toy/src/tasks.ts | 500 +++++++++++++----- examples/ratchet-train-toy/src/trainWorker.ts | 44 +- 3 files changed, 446 insertions(+), 155 deletions(-) diff --git a/examples/ratchet-train-toy/src/routes/+page.svelte b/examples/ratchet-train-toy/src/routes/+page.svelte index 056e425b..0e8247e7 100644 --- a/examples/ratchet-train-toy/src/routes/+page.svelte +++ b/examples/ratchet-train-toy/src/routes/+page.svelte @@ -6,6 +6,7 @@ import LogSlider from '$lib/components/LogSlider.svelte'; import TickSlider from '$lib/components/TickSlider.svelte'; import ActivationPicker from '$lib/components/ActivationPicker.svelte'; + import { taskMetadata } from '../tasks'; let loss = 0; let worker: Worker; @@ -52,6 +53,7 @@ // Track last parameter values to detect actual changes let lastParams: Record | null = null; + let lastTaskParams: Record | null = null; // Add reactive statement to restart training when parameters change $: { @@ -82,13 +84,21 @@ if (lastParams === null) { // First time through, just store the parameters lastParams = { ...currentParams }; + lastTaskParams = { ...taskParameters }; } else { // Check if any parameter actually changed const hasChanges = Object.entries(currentParams).some( ([key, value]) => lastParams![key] !== value ); - if (worker && isTraining && hasChanges) { + // Check which task parameters changed + const hasTaskChanges = + Object.keys(lastTaskParams!).length > 0 && + Object.entries(taskParameters).some(([key, value]) => lastTaskParams![key] !== value); + + lastTaskParams = { ...taskParameters }; + + if (worker && isTraining && (hasChanges || hasTaskChanges)) { // Update last params before restarting lastParams = { ...currentParams }; // Restart training with new parameters @@ -117,6 +127,21 @@ let scheduler_steps = 1000; let scheduler_eta_min = 1e-4; + // Add task parameter state + let taskParameters: Record = {}; + + // Initialize task parameters with defaults + $: { + if (dataset && taskMetadata[dataset]) { + const metadata = taskMetadata[dataset]; + for (const [key, param] of Object.entries(metadata.parameters)) { + if (!(key in taskParameters)) { + taskParameters[key] = param.default; + } + } + } + } + function initializeAttentionCanvases(numLayers: number, numHeads: number) { if (typeof document === 'undefined') return; // Check if we're in the browser @@ -252,6 +277,7 @@ block_size: 24, batch_size, dataset, + task_parameters: taskParameters, activation, attention_only: attentionOnly, positional_encoding, @@ -389,7 +415,7 @@ const stepsPerSecond = 1000 / (currentTime - lastStepTime); lastStepTime = currentTime; - loss = data.loss; + loss = data.loss.total; const currentLossDataset = chart.data.datasets[chart.data.datasets.length - 1]; const currentSpeedDataset = speedChart.data.datasets[speedChart.data.datasets.length - 1]; const currentMemoryDataset = @@ -752,13 +778,9 @@ bind:value={dataset} class="w-full p-2 pr-12 border focus:outline-none focus:border-gray-400 border-gray-400 bg-white appearance-none" > - - - - - - - + {#each Object.entries(taskMetadata) as [key, meta]} + + {/each}
- + {#if dataset && taskMetadata[dataset]} +

{taskMetadata[dataset].description}

+
+ {#each Object.entries(taskMetadata[dataset].parameters) as [key, param]} +
+ +

{param.description}

+
+ {/each} +
+ {/if}
diff --git a/examples/ratchet-train-toy/src/tasks.ts b/examples/ratchet-train-toy/src/tasks.ts index cace7811..034bdb48 100644 --- a/examples/ratchet-train-toy/src/tasks.ts +++ b/examples/ratchet-train-toy/src/tasks.ts @@ -1,87 +1,171 @@ -// Type for task generation functions -export type TaskGenerator = ( - maxNum: number, - seqLen: number, - batchSize: number -) => [number[][], number[][]]; - -// Helper function to convert a sequence to tokens -function sequenceToTokens(sequence: string): number[] { - return sequence.split('').map((c) => c.charCodeAt(0)); +// Task-specific configuration types +interface TrainBatchConfig { + batchSize: number; } -// Helper function to pad a number to 2 digits -function pad2(num: number): string { - return num.toString().padStart(2, '0'); +interface NumberSequenceConfig { + seqLen: number; + maxNum: number; } -// Helper function to pad a number to 3 digits -function pad3(num: number): string { - return num.toString().padStart(3, '0'); +interface AdditionConfig { + maxNum: number; } -export function generateTwoSumTask( - maxNum: number, - seqLen: number, - batchSize: number -): [number[][], number[][]] { - const input: number[][] = []; - const target: number[][] = []; - - for (let b = 0; b < batchSize; b++) { - const nums = Array.from({ length: seqLen }, () => Math.floor(Math.random() * maxNum)); - const i = Math.floor(Math.random() * seqLen); - const j = Math.floor(Math.random() * seqLen); - const sum = nums[i] + nums[j]; - - const sequence = `${nums.map((n) => pad2(n)).join(',')}:${pad3(sum)}=${pad2(nums[i])},${pad2(nums[j])}`; - const tokens = sequenceToTokens(sequence); - - input.push(tokens.slice(0, -1)); - target.push(tokens.slice(1)); - } - - return [input, target]; +interface ModAdditionConfig { + maxNum: number; } -export function generateSortTask( - maxNum: number, - seqLen: number, - batchSize: number -): [number[][], number[][]] { - const input: number[][] = []; - const target: number[][] = []; +interface FixedLengthConfig { + seqLen: number; +} - for (let b = 0; b < batchSize; b++) { - const nums = Array.from({ length: seqLen }, () => Math.floor(Math.random() * maxNum)); - const sorted = [...nums].sort((a, b) => a - b); +// Task metadata for UI configuration +export interface TaskParameter { + name: string; + description: string; + min: number; + max: number; + default: number; +} - const sequence = `${nums.map((n) => pad2(n)).join(',')}:${sorted.map((n) => pad2(n)).join(',')}`; - const tokens = sequenceToTokens(sequence); +export interface TaskMetadata { + name: string; + description: string; + parameters: Record; +} - input.push(tokens.slice(0, -1)); - target.push(tokens.slice(1)); +export const taskMetadata: Record = { + sort: { + name: 'Sorting', + description: 'Learn to sort a sequence of numbers', + parameters: { + seqLen: { + name: 'Sequence Length', + description: 'Length of the sequence to sort', + min: 2, + max: 10, + default: 2 + }, + maxNum: { + name: 'Max Number', + description: 'Maximum value in the sequence', + min: 10, + max: 100, + default: 100 + } + } + }, + add: { + name: 'Addition', + description: 'Learn to add two numbers', + parameters: { + maxNum: { + name: 'Max Number', + description: 'Maximum value for each addend', + min: 10, + max: 100, + default: 100 + } + } + }, + mod_add: { + name: 'Modular Addition', + description: 'Learn to add two numbers with modulo', + parameters: { + maxNum: { + name: 'Modulo', + description: 'The modulo to use', + min: 10, + max: 100, + default: 100 + } + } + }, + // count: { + // name: 'Counting', + // description: 'Learn to count the occurrences of a character in a sequence', + // parameters: { + // seqLen: { + // name: 'Sequence Length', + // description: 'Length of the counting sequence', + // min: 2, + // max: 10, + // default: 5 + // }, + // maxNum: { + // name: 'Max Number', + // description: 'Maximum starting number', + // min: 10, + // max: 100, + // default: 100 + // } + // } + // }, + // slapjack: { + // name: 'Slapjack', + // description: 'Learn to find a specific number in a sequence', + // parameters: { + // seqLen: { + // name: 'Sequence Length', + // description: 'Length of the sequence', + // min: 2, + // max: 10, + // default: 5 + // } + // } + // }, + zeros: { + name: 'Zeros', + description: 'Learn to output zeros (baseline/debug task)', + parameters: { + seqLen: { + name: 'Sequence Length', + description: 'Length of the sequence', + min: 2, + max: 10, + default: 5 + } + } + }, + two_sum: { + name: 'Two Sum', + description: 'Find two numbers in a sequence that sum to a target', + parameters: { + seqLen: { + name: 'Sequence Length', + description: 'Length of the sequence', + min: 2, + max: 10, + default: 5 + }, + maxNum: { + name: 'Max Number', + description: 'Maximum value in the sequence', + min: 10, + max: 100, + default: 100 + } + } } +}; - return [input, target]; -} +// Type for single sequence generation +type SequenceGenerator = ( + config: TaskConfigMap[K] +) => [string, string]; -export function generateAddTask( - maxNum: number, - seqLen: number, - batchSize: number +// Helper function to handle batch processing and tokenization +function generateTrainBatch( + generator: SequenceGenerator, + config: TrainBatchConfig & TaskConfigMap[K] ): [number[][], number[][]] { const input: number[][] = []; const target: number[][] = []; - for (let b = 0; b < batchSize; b++) { - const num1 = Math.floor(Math.random() * maxNum); - const num2 = Math.floor(Math.random() * maxNum); - const sum = num1 + num2; - - const sequence = `${pad2(num1)}+${pad2(num2)}=${pad3(sum)}`; + for (let b = 0; b < config.batchSize; b++) { + const sequence = generator(config).join(''); const tokens = sequenceToTokens(sequence); - input.push(tokens.slice(0, -1)); target.push(tokens.slice(1)); } @@ -89,98 +173,234 @@ export function generateAddTask( return [input, target]; } -export function generateModAddTask( - maxNum: number, - seqLen: number, - batchSize: number -): [number[][], number[][]] { - const input: number[][] = []; - const target: number[][] = []; +function generateEvalExample( + generator: SequenceGenerator, + config: TaskConfigMap[K] +): [number[], number[]] { + const [sequence, completion] = generator(config); + const sequenceTokens = sequenceToTokens(sequence); + const completionTokens = sequenceToTokens(completion); + return [sequenceTokens, completionTokens]; +} - for (let b = 0; b < batchSize; b++) { - const num1 = Math.floor(Math.random() * maxNum); - const num2 = Math.floor(Math.random() * maxNum); - const sum = (num1 + num2) % maxNum; +// Helper function to convert a sequence to tokens +export function sequenceToTokens(sequence: string): number[] { + return sequence.split('').map((c) => c.charCodeAt(0)); +} - const sequence = `${pad2(num1)}+${pad2(num2)}%${pad2(maxNum)}=${pad2(sum)}`; - const tokens = sequenceToTokens(sequence); +export function tokensToString(tokens: number[]): string { + return tokens.map((t) => String.fromCharCode(t)).join(''); +} - input.push(tokens.slice(0, -1)); - target.push(tokens.slice(1)); - } +// Helper function to pad a number to 2 digits +function pad2(num: number): string { + return num.toString().padStart(2, '0'); +} - return [input, target]; +// Helper function to pad a number to 3 digits +function pad3(num: number): string { + return num.toString().padStart(3, '0'); } -export function generateCountTask( - maxNum: number, - seqLen: number, - batchSize: number -): [number[][], number[][]] { - const input: number[][] = []; - const target: number[][] = []; +// Task-specific sequence generators +function twoSumSequence(config: NumberSequenceConfig): [string, string] { + const { maxNum, seqLen } = config; + const nums = Array.from({ length: seqLen }, () => Math.floor(Math.random() * maxNum)); + const i = Math.floor(Math.random() * seqLen); + const j = Math.floor(Math.random() * seqLen); + const sum = nums[i] + nums[j]; + return [ + `${nums.map((n) => pad2(n)).join(',')}:${pad3(sum)}=`, + `${pad2(nums[i])},${pad2(nums[j])}` + ]; +} - for (let b = 0; b < batchSize; b++) { - const start = Math.floor(Math.random() * (maxNum - seqLen)); - const sequence = Array.from({ length: seqLen }, (_, i) => pad2(start + i)).join(','); - const tokens = sequenceToTokens(sequence); +function sortSequence(config: NumberSequenceConfig): [string, string] { + const { maxNum, seqLen } = config; + const nums = Array.from({ length: seqLen }, () => Math.floor(Math.random() * maxNum)); + const sorted = [...nums].sort((a, b) => a - b); + return [`${nums.map((n) => pad2(n)).join(',')}:`, `${sorted.map((n) => pad2(n)).join(',')}`]; +} - input.push(tokens.slice(0, -1)); - target.push(tokens.slice(1)); - } +function addSequence(config: AdditionConfig): [string, string] { + const { maxNum } = config; + const num1 = Math.floor(Math.random() * maxNum); + const num2 = Math.floor(Math.random() * maxNum); + const sum = num1 + num2; + return [`${pad2(num1)}+${pad2(num2)}=`, `${pad3(sum)}`]; +} - return [input, target]; +function modAddSequence(config: ModAdditionConfig): [string, string] { + const { maxNum } = config; + const num1 = Math.floor(Math.random() * maxNum); + const num2 = Math.floor(Math.random() * maxNum); + const sum = (num1 + num2) % maxNum; + return [`${pad2(num1)}+${pad2(num2)}%${pad2(maxNum)}=`, `${pad2(sum)}`]; } -export function generateSlapjackTask( - maxNum: number, - seqLen: number, - batchSize: number -): [number[][], number[][]] { - const input: number[][] = []; - const target: number[][] = []; +// function countSequence(config: NumberSequenceConfig): [string, string] { +// const { maxNum, seqLen } = config; +// const start = Math.floor(Math.random() * (maxNum - seqLen)); +// const sequence = Array.from({ length: seqLen - 1 }, (_, i) => pad2(start + i)).join(','); +// return [sequence, `,${pad2(start + seqLen - 1)}`]; +// } + +// function slapjackSequence(config: FixedLengthConfig): [string, string] { +// const { seqLen } = config; +// const maxNum = 100; // Fixed max number for consistency +// const nums = Array.from({ length: seqLen }, () => Math.floor(Math.random() * maxNum)); +// const jackPosition = Math.floor(Math.random() * seqLen); +// nums[jackPosition] = maxNum - 1; // Use max number as "jack" +// return [ +// `${nums.map((n) => pad2(n)).join(',')}`, +// `:${pad2(jackPosition)}` +// ]; +// } + +function zerosSequence(config: FixedLengthConfig): [string, string] { + const { seqLen } = config; + const sequence = Array(seqLen - 1) + .fill('0') + .join(''); + return ['', sequence]; +} - for (let b = 0; b < batchSize; b++) { - const nums = Array.from({ length: seqLen }, () => Math.floor(Math.random() * maxNum)); - const jackPosition = Math.floor(Math.random() * seqLen); - nums[jackPosition] = maxNum - 1; // Use max number as "jack" +export type TaskConfigMap = { + two_sum: NumberSequenceConfig; + sort: NumberSequenceConfig; + add: AdditionConfig; + mod_add: ModAdditionConfig; + zeros: FixedLengthConfig; +}; - const sequence = `${nums.map((n) => pad2(n)).join(',')}:${pad2(jackPosition)}`; - const tokens = sequenceToTokens(sequence); +// Update TrainBatchGenerator to be generic over task keys +export type TrainBatchGenerator = ( + config: TrainBatchConfig & TaskConfigMap[K] +) => [number[][], number[][]]; - input.push(tokens.slice(0, -1)); - target.push(tokens.slice(1)); +// Map of task names to their generator functions +export const trainBatchGenerators: { + [K in keyof TaskConfigMap]: TrainBatchGenerator; +} = { + two_sum: (config: TrainBatchConfig & NumberSequenceConfig) => { + const { seqLen, maxNum } = config; + return generateTrainBatch((c) => twoSumSequence({ ...c, seqLen, maxNum }), config); + }, + sort: (config: TrainBatchConfig & NumberSequenceConfig) => { + const { seqLen, maxNum } = config; + return generateTrainBatch((c) => sortSequence({ ...c, seqLen, maxNum }), config); + }, + add: (config: TrainBatchConfig & AdditionConfig) => { + const { maxNum } = config; + return generateTrainBatch((c) => addSequence({ ...c, maxNum }), config); + }, + mod_add: (config: TrainBatchConfig & ModAdditionConfig) => { + const { maxNum } = config; + return generateTrainBatch((c) => modAddSequence({ ...c, maxNum }), config); + }, + // count: (config: BaseTaskConfig) => { + // const { seqLen, maxNum } = config as NumberSequenceConfig; + // return generateBatch((c) => countSequence({ ...c, seqLen, maxNum }), config); + // }, + // slapjack: (config: BaseTaskConfig) => { + // const { seqLen } = config as FixedLengthConfig; + // return generateBatch((c) => slapjackSequence({ ...c, seqLen }), config); + // }, + zeros: (config: TrainBatchConfig & FixedLengthConfig) => { + const { seqLen } = config; + return generateTrainBatch((c) => zerosSequence({ ...c, seqLen }), config); } +}; - return [input, target]; -} - -export function generateZerosTask( - _maxNum: number, - seqLen: number, - batchSize: number -): [number[][], number[][]] { - const input: number[][] = []; - const target: number[][] = []; - - for (let b = 0; b < batchSize; b++) { - const sequence = Array(seqLen).fill('00').join(','); - const tokens = sequenceToTokens(sequence); +export type EvalExampleGenerator = ( + config: TaskConfigMap[K] +) => [number[], number[]]; - input.push(tokens.slice(0, -1)); - target.push(tokens.slice(1)); - } +export type EvalMetric = ( + completion: number[], + target: number[], + sequence: number[], + config: TaskConfigMap[K] +) => Array; - return [input, target]; -} +export type EvalConfig = { + metric: EvalMetric; + generator: EvalExampleGenerator; +}; -// Map of task names to their generator functions -export const taskGenerators: Record = { - two_sum: generateTwoSumTask, - sort: generateSortTask, - add: generateAddTask, - mod_add: generateModAddTask, - count: generateCountTask, - slapjack: generateSlapjackTask, - zeros: generateZerosTask +export const evalExampleGenerators: { + [K in keyof TaskConfigMap]: EvalConfig; +} = { + two_sum: (() => { + return { + generator: (config: NumberSequenceConfig) => { + const { seqLen, maxNum } = config as NumberSequenceConfig; + return generateEvalExample((c) => twoSumSequence({ ...c, seqLen, maxNum }), config); + }, + metric: (completion, _, sequence, config) => { + // Passing is binary in this case, and depends on the whole completion + const shouldPass = (() => { + const completionString = tokensToString(completion); + // Count the number of commas + const numCommas = (completionString.match(/,/g) || []).length; + if (numCommas !== 1) { + return false; + } + + // Use log to find the number length + const numberLength = Math.floor(Math.log10(config.maxNum)); + // Check that there are two numbers of the correct length, separated + // by a comma + // Match pattern of two 2-digit numbers separated by comma + const pattern = new RegExp(`^\\d{${numberLength}},\\d{${numberLength}}$`); + return pattern.test(completionString); + })(); + return completion.map(() => shouldPass); + } + }; + })(), + sort: (() => { + return { + generator: (config: NumberSequenceConfig) => { + const { seqLen, maxNum } = config as NumberSequenceConfig; + return generateEvalExample((c) => sortSequence({ ...c, seqLen, maxNum }), config); + }, + metric: (completion, target) => { + return completion.map((c, i) => c === target[i]); + } + }; + })(), + add: (() => { + return { + generator: (config: AdditionConfig) => { + const { maxNum } = config as AdditionConfig; + return generateEvalExample((c) => addSequence({ ...c, maxNum }), config); + }, + metric: (completion, target) => { + return completion.map((c, i) => c === target[i]); + } + }; + })(), + mod_add: (() => { + return { + generator: (config: ModAdditionConfig) => { + const { maxNum } = config as ModAdditionConfig; + return generateEvalExample((c) => modAddSequence({ ...c, maxNum }), config); + }, + metric: (completion, target) => { + return completion.map((c, i) => c === target[i]); + } + }; + })(), + zeros: (() => { + return { + generator: (config: FixedLengthConfig) => { + const { seqLen } = config as FixedLengthConfig; + return generateEvalExample((c) => zerosSequence({ ...c, seqLen }), config); + }, + metric: (completion, target) => { + return completion.map((c, i) => c === target[i]); + } + }; + })() }; diff --git a/examples/ratchet-train-toy/src/trainWorker.ts b/examples/ratchet-train-toy/src/trainWorker.ts index 13233669..007dc67d 100644 --- a/examples/ratchet-train-toy/src/trainWorker.ts +++ b/examples/ratchet-train-toy/src/trainWorker.ts @@ -1,5 +1,10 @@ import { Trainer } from '@ratchet-ml/ratchet-web-train'; -import { taskGenerators } from './tasks'; +import { + evalExampleGenerators, + trainBatchGenerators, + type EvalConfig, + type TaskConfigMap +} from './tasks'; let trainer: Trainer; let sessionCounter = 0; @@ -13,7 +18,8 @@ export interface TrainerConfig { n_head: number; block_size: number; batch_size: number; - dataset: string; + dataset: keyof typeof trainBatchGenerators; + task_parameters: TaskConfigMap[keyof TaskConfigMap]; activation: string; attention_only: boolean; position_encoding: string; @@ -58,12 +64,14 @@ async function trainingLoop(sessionId: number, config: TrainerConfig) { } // Get the appropriate task generator - const taskGenerator = taskGenerators[config.dataset]; + const taskGenerator = trainBatchGenerators[config.dataset]; + const evalConfig = evalExampleGenerators[config.dataset] as EvalConfig; if (!taskGenerator) { console.error(`Unknown dataset type: ${config.dataset}`); return; } + let step = 0; while (trainingSessions[sessionId]) { // Skip if this isn't the current session anymore if (sessionId !== currentSession) { @@ -72,10 +80,12 @@ async function trainingLoop(sessionId: number, config: TrainerConfig) { try { // Generate a new batch using the selected task generator - const [input, target] = taskGenerator(100, 5, 1); // maxNum, seqLen, batchSize + const [input, target] = generateTask(config); // Train on the batch const result = await trainer.train_on_batch(input, target); + const logits = result.get('logits') as Map; + const loss = result.get('loss') as Map; const attn_masks = result.get('attn_masks') as Map; const usage_bytes = trainer.usage_bytes(); @@ -83,12 +93,21 @@ async function trainingLoop(sessionId: number, config: TrainerConfig) { if (sessionId === currentSession) { self.postMessage({ type: 'step', - loss: result.get('loss'), + input: input, + target: target, + loss: { + total: loss.get('total'), + tokens: loss.get('tokens') + }, learning_rate: result.get('learning_rate'), usage_bytes, attn_masks: { data: attn_masks.get('data'), shape: attn_masks.get('shape') + }, + logits: { + data: logits.get('data'), + shape: logits.get('shape') } }); } @@ -107,6 +126,21 @@ async function trainingLoop(sessionId: number, config: TrainerConfig) { } } +function generateTask(config: TrainerConfig): [number[][], number[][]] { + const taskGenerator = trainBatchGenerators[config.dataset]; + if (!taskGenerator) { + throw new Error(`Unknown dataset: ${config.dataset}`); + } + + // Combine batch size with task-specific parameters + const taskConfig = { + batchSize: config.batch_size, + ...config.task_parameters + }; + + return taskGenerator(taskConfig); +} + self.onmessage = async (e: MessageEvent) => { if (e.data.type === 'stop') { markStopTraining(); From 7766180a8de18cee4c341194324cd5ef421ecb71 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Tue, 18 Feb 2025 04:28:46 -0700 Subject: [PATCH 020/590] Visualize train batches --- .../ratchet-train-toy/src/routes/+page.svelte | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/examples/ratchet-train-toy/src/routes/+page.svelte b/examples/ratchet-train-toy/src/routes/+page.svelte index 0e8247e7..b3045605 100644 --- a/examples/ratchet-train-toy/src/routes/+page.svelte +++ b/examples/ratchet-train-toy/src/routes/+page.svelte @@ -9,6 +9,7 @@ import { taskMetadata } from '../tasks'; let loss = 0; + let initialTotalLoss: number | null = null; let worker: Worker; let chart: Chart; let speedChart: Chart; @@ -127,6 +128,15 @@ let scheduler_steps = 1000; let scheduler_eta_min = 1e-4; + // Add new state for batch visualization + let batchHistory: Array<{ + input: string[]; + target: string[]; + losses: number[]; + initialLoss: number; + }> = []; + const MAX_HISTORY = 5; // Keep last 10 batches + // Add task parameter state let taskParameters: Record = {}; @@ -261,6 +271,9 @@ function startTraining() { currentStepCount = 0; + // Clear batch history + batchHistory = []; + // isTraining = true; // Store the current model configuration currentModelLayers = n_layer; @@ -416,6 +429,30 @@ lastStepTime = currentTime; loss = data.loss.total; + if (initialTotalLoss === null) { + initialTotalLoss = loss; + } + + // Process batch data for visualization + const inputStrings = data.input.map((x: Array) => + x.map((y: number) => String.fromCharCode(y)) + ); + const targetStrings = data.target.map((x: Array) => + x.map((y: number) => String.fromCharCode(y)) + ); + const tokenLosses = data.loss.tokens.map((x: number) => x * data.loss.tokens.length); + + // Update batch history + batchHistory = [ + { + input: inputStrings, + target: targetStrings, + losses: tokenLosses, + initialLoss: initialTotalLoss / 2 + }, + ...batchHistory.slice(0, MAX_HISTORY - 1) + ]; + const currentLossDataset = chart.data.datasets[chart.data.datasets.length - 1]; const currentSpeedDataset = speedChart.data.datasets[speedChart.data.datasets.length - 1]; const currentMemoryDataset = @@ -729,6 +766,41 @@ {/if}
{/if} + + {#if isTraining} +
+
+

Train Batches

+
Showing last {MAX_HISTORY} batches
+
+
+ {#each batchHistory as batch} +
+ {#each batch.input as input, batchIdx} +
+
+ {input[0]}{#each [...input.slice(1), batch.target[batchIdx][batch.target[batchIdx].length - 1]] as char, i} + + {char} + + {/each} +
+
+ {/each} +
+ {/each} +
+
+ {/if}
From 8e77054d8880ffe7e853c6df24e53b08a11d6560 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Tue, 18 Feb 2025 04:29:03 -0700 Subject: [PATCH 021/590] Make tick slider look less busy --- .../src/lib/components/TickSlider.svelte | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/examples/ratchet-train-toy/src/lib/components/TickSlider.svelte b/examples/ratchet-train-toy/src/lib/components/TickSlider.svelte index 02fc9868..747def43 100644 --- a/examples/ratchet-train-toy/src/lib/components/TickSlider.svelte +++ b/examples/ratchet-train-toy/src/lib/components/TickSlider.svelte @@ -13,16 +13,24 @@ const ticks = []; const range = max - min; const totalTicks = range / step; - const skipFactor = Math.ceil(totalTicks / 10); // Show at most 10 ticks + const majorTickCount = 10; // Show 10 major ticks + const minorTickCount = 40; // Show at most 20 minor ticks + + const majorStep = Math.ceil(totalTicks / majorTickCount) * step; + const minorStep = Math.ceil(totalTicks / minorTickCount) * step; - for (let i = min; i <= max; i += step) { - if ((i - min) % (step * skipFactor) === 0) { - ticks.push({ - value: i, - label: formatter(i), - type: 'major' - }); - } else if ((i - min) % step === 0) { + // Add major ticks + for (let i = min; i <= max; i += majorStep) { + ticks.push({ + value: i, + label: formatter(i), + type: 'major' + }); + } + + // Add minor ticks + for (let i = min; i <= max; i += minorStep) { + if (!ticks.some(t => Math.abs(t.value - i) < step/2)) { ticks.push({ value: i, label: '', @@ -30,7 +38,8 @@ }); } } - return ticks; + + return ticks.sort((a, b) => a.value - b.value); } const ticks = generateTicks(); From c93a3296b724875c1ca52ab0c86dd39a85a645c6 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Tue, 18 Feb 2025 04:30:19 -0700 Subject: [PATCH 022/590] Add eval + eval eample visualization --- .../ratchet-train-toy/src/routes/+page.svelte | 157 +++++++++++++++++- examples/ratchet-train-toy/src/trainWorker.ts | 69 ++++++++ 2 files changed, 225 insertions(+), 1 deletion(-) diff --git a/examples/ratchet-train-toy/src/routes/+page.svelte b/examples/ratchet-train-toy/src/routes/+page.svelte index b3045605..0ffd8283 100644 --- a/examples/ratchet-train-toy/src/routes/+page.svelte +++ b/examples/ratchet-train-toy/src/routes/+page.svelte @@ -15,10 +15,12 @@ let speedChart: Chart; let memoryChart: Chart; let lrChart: Chart; + let accuracyChart: Chart; let canvas: HTMLCanvasElement; let speedCanvas: HTMLCanvasElement; let memoryCanvas: HTMLCanvasElement; let lrCanvas: HTMLCanvasElement; + let accuracyCanvas: HTMLCanvasElement; let attentionCanvases: HTMLCanvasElement[][] = []; let showAdvanced = false; let runCount = 0; @@ -137,6 +139,16 @@ }> = []; const MAX_HISTORY = 5; // Keep last 10 batches + // Add eval history state + let evalHistory: Array<{ + sequence: string; + completion: string; + target: string; + results: Array; + logits: number[]; + }> = []; + const MAX_EVAL_HISTORY = 5; + // Add task parameter state let taskParameters: Record = {}; @@ -271,7 +283,8 @@ function startTraining() { currentStepCount = 0; - // Clear batch history + // Clear evaluation and batch histories + evalHistory = []; batchHistory = []; // isTraining = true; @@ -369,6 +382,16 @@ pointStyle: false }); + // Add dataset to accuracy chart + accuracyChart.data.datasets.push({ + label: '', // Will be set by updateAllLabels + data: [], + borderColor: colors[runCount % colors.length], + tension: 0.1, + order: -runCount, + pointStyle: false + }); + updateAllLabels(); lastStepTime = performance.now(); @@ -414,6 +437,38 @@ case 'modelReady': addMessage('Model initialized and ready'); break; + case 'evalStreaming': + // Convert sequence and completion to strings + const sequence = data.sequence.map((x: number) => String.fromCharCode(x)).join(''); + const completion = data.completion.map((x: number) => String.fromCharCode(x)).join(''); + const target = data.target.map((x: number) => String.fromCharCode(x)).join(''); + + // Update eval history - find existing entry or create new one + const existingIndex = evalHistory.findIndex((entry) => entry.sequence === sequence); + if (existingIndex !== -1) { + // Update existing entry + evalHistory[existingIndex] = { + ...evalHistory[existingIndex], + completion, + target, + results: data.evalResult, + logits: data.logits.data + }; + evalHistory = evalHistory; // Trigger reactivity + } else { + // Create new entry + evalHistory = [ + { + sequence, + completion, + target, + results: data.evalResult, + logits: data.logits.data + }, + ...evalHistory.slice(0, MAX_EVAL_HISTORY - 1) + ]; + } + break; case 'step': currentStepCount++; if (!isTraining) { @@ -464,6 +519,18 @@ currentMemoryDataset.data.push(Number(data.usage_bytes) / (1024 * 1024)); // Convert bigint to number then to MB currentLrDataset.data.push(data.learning_rate); + // Update accuracy chart only when we have valid accuracy data + if (data.accuracy !== null && data.accuracy !== undefined) { + const currentAccuracyDataset = + accuracyChart.data.datasets[accuracyChart.data.datasets.length - 1]; + // Store both the step number and accuracy value + currentAccuracyDataset.data.push({ + x: currentStepCount, + y: data.accuracy + }); + accuracyChart.update(); + } + // Update labels if this is the longest run const maxLength = Math.max(...chart.data.datasets.map((d) => d.data.length)); const labels = Array.from({ length: maxLength }, (_, i) => i.toString()); @@ -471,6 +538,7 @@ speedChart.data.labels = labels; memoryChart.data.labels = labels; lrChart.data.labels = labels; + accuracyChart.data.labels = labels; if (isNaN(loss)) { addMessage('Loss is NaN; stopped training'); @@ -534,6 +602,7 @@ speedChart.update(); memoryChart.update(); lrChart.update(); + accuracyChart.update(); break; case 'error': addMessage(`Error: ${data.error}`); @@ -680,6 +749,57 @@ } } }); + + // Initialize accuracy chart + accuracyChart = new Chart(accuracyCanvas, { + type: 'line', + data: { + labels: [], + datasets: [] + }, + options: { + responsive: true, + animation: false, + aspectRatio: 4, + scales: { + x: { + type: 'linear', + display: true, + title: { + display: true, + text: 'Steps' + } + }, + y: { + beginAtZero: true, + max: 1, + title: { + display: true, + text: 'Accuracy' + } + } + }, + plugins: { + legend: { + display: false + }, + title: { + display: true, + text: 'Validation Accuracy', + font: { + weight: 'bold' + } + } + }, + datasets: { + line: { + order: -1, + showLine: true, // Connect points with lines + spanGaps: true // Connect points across gaps + } + } + } + }); }); @@ -728,6 +848,9 @@
+
+ +
{#if isTraining} @@ -800,6 +923,38 @@ {/each} + + {#if evalHistory.length > 0} +
+
+

Eval Results

+
Showing last {MAX_EVAL_HISTORY} evaluations
+
+
+ {#each evalHistory as evalEntry} +
+
+
+ {evalEntry.sequence}{#each evalEntry.completion.split('') as char, i} + + {char} + + {/each} + → {evalEntry.target} +
+
+
+ {/each} +
+
+ {/if} {/if}
diff --git a/examples/ratchet-train-toy/src/trainWorker.ts b/examples/ratchet-train-toy/src/trainWorker.ts index 007dc67d..5828d640 100644 --- a/examples/ratchet-train-toy/src/trainWorker.ts +++ b/examples/ratchet-train-toy/src/trainWorker.ts @@ -89,12 +89,80 @@ async function trainingLoop(sessionId: number, config: TrainerConfig) { const attn_masks = result.get('attn_masks') as Map; const usage_bytes = trainer.usage_bytes(); + let winCount = null; + const EVAL_TRIAL_COUNT = 5; + + // After every 10 steps, evaluate the model + if (step % 10 === 0) { + const [streamingSequence, streamingTarget] = evalConfig.generator(config.task_parameters); + let streamingExampleCompletion: number[] | null = null; + const streamingExampleLogits: number[] = []; + let streamingExampleMetric: boolean | null = null; + + // For the first eval, we'll stream it to the frontend + await trainer.generate( + streamingSequence, + /* max_tokens= */ streamingTarget.length + 1, + (tokens: number[], logitsObj: { shape: number[]; data: number[] }) => { + // For the first call (prompt), just store logits + if (streamingExampleCompletion === null) { + streamingExampleCompletion = []; + streamingExampleLogits.push(...logitsObj.data); + return; + } + + // For each subsequent token, append it and evaluate + streamingExampleCompletion.push(...tokens); + streamingExampleLogits.push(...logitsObj.data); + + // Run the metric on the current completion + const evalResult = evalConfig.metric( + streamingExampleCompletion, + streamingTarget, + streamingSequence, + config.task_parameters + ); + + streamingExampleMetric = evalResult.every((result) => result); + + // Send the streaming eval result to the frontend + if (sessionId === currentSession) { + self.postMessage({ + type: 'evalStreaming', + sequence: streamingSequence, + completion: streamingExampleCompletion, + target: streamingTarget, + evalResult, + logits: { + data: streamingExampleLogits, + shape: logitsObj.shape + } + }); + } + } + ); + + winCount = streamingExampleMetric === true ? 1 : 0; + + // We'll do best of 5 evals, so we'll do the rest non-streaming + for (let i = 0; i < EVAL_TRIAL_COUNT - 1; i++) { + const [sequence, target] = evalConfig.generator(config.task_parameters); + const result = await trainer.generate(sequence, target.length + 1); + const tokens = result.get('tokens') as number[]; + const evalResult = evalConfig.metric(tokens, target, sequence, config.task_parameters); + if (evalResult.every((result) => result)) { + winCount++; + } + } + } + // Only send message if this is still the current session if (sessionId === currentSession) { self.postMessage({ type: 'step', input: input, target: target, + accuracy: winCount === null ? null : winCount / EVAL_TRIAL_COUNT, loss: { total: loss.get('total'), tokens: loss.get('tokens') @@ -121,6 +189,7 @@ async function trainingLoop(sessionId: number, config: TrainerConfig) { } break; } + step++; // Yield to keep the worker responsive await new Promise((resolve) => setTimeout(resolve, 0)); } From 093817f67825252fb4393c344c954549b230a1ea Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Tue, 18 Feb 2025 04:30:53 -0700 Subject: [PATCH 023/590] Add eslint rule to ignore _ vars --- examples/ratchet-train-toy/eslint.config.js | 59 +++++++++++++-------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/examples/ratchet-train-toy/eslint.config.js b/examples/ratchet-train-toy/eslint.config.js index e18311fa..85d7229a 100644 --- a/examples/ratchet-train-toy/eslint.config.js +++ b/examples/ratchet-train-toy/eslint.config.js @@ -1,34 +1,47 @@ -import prettier from "eslint-config-prettier"; +import prettier from 'eslint-config-prettier'; import js from '@eslint/js'; import { includeIgnoreFile } from '@eslint/compat'; import svelte from 'eslint-plugin-svelte'; import globals from 'globals'; import { fileURLToPath } from 'node:url'; import ts from 'typescript-eslint'; -const gitignorePath = fileURLToPath(new URL("./.gitignore", import.meta.url)); +const gitignorePath = fileURLToPath(new URL('./.gitignore', import.meta.url)); export default ts.config( - includeIgnoreFile(gitignorePath), - js.configs.recommended, - ...ts.configs.recommended, - ...svelte.configs["flat/recommended"], - prettier, - ...svelte.configs['flat/prettier'], - { - languageOptions: { - globals: { - ...globals.browser, - ...globals.node - } - } - }, - { - files: ["**/*.svelte"], + includeIgnoreFile(gitignorePath), + js.configs.recommended, + ...ts.configs.recommended, + ...svelte.configs['flat/recommended'], + prettier, + ...svelte.configs['flat/prettier'], + { + languageOptions: { + globals: { + ...globals.browser, + ...globals.node + } + } + }, + { + files: ['**/*.svelte'], - languageOptions: { - parserOptions: { - parser: ts.parser - } + languageOptions: { + parserOptions: { + parser: ts.parser + } + } + }, + { + rules: { + 'no-unused-vars': 'off', + '@typescript-eslint/no-unused-vars': [ + 'warn', + { + argsIgnorePattern: '^_', + varsIgnorePattern: '^_', + caughtErrorsIgnorePattern: '^_' + } + ] + } } - } ); From 7453296ca741a0d819b7efbc4539cf8bd9bf2925 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Tue, 18 Feb 2025 04:31:00 -0700 Subject: [PATCH 024/590] Update pnpm-lock.yaml --- pnpm-lock.yaml | 2278 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 2220 insertions(+), 58 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index acae9285..687c2abf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,9 +4,6 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false -onlyBuiltDependencies: - - esbuild - importers: .: @@ -70,6 +67,76 @@ importers: specifier: ^5.3.3 version: 5.3.3 + examples/ratchet-train-toy: + dependencies: + chart.js: + specifier: ^4.4.7 + version: 4.4.7 + mathjs: + specifier: ^14.2.1 + version: 14.2.1 + devDependencies: + '@eslint/compat': + specifier: ^1.2.5 + version: 1.2.6(eslint@9.20.1) + '@eslint/js': + specifier: ^9.18.0 + version: 9.20.0 + '@sveltejs/adapter-static': + specifier: ^3.0.8 + version: 3.0.8(@sveltejs/kit@2.17.2) + '@sveltejs/kit': + specifier: ^2.16.0 + version: 2.17.2(@sveltejs/vite-plugin-svelte@5.0.3)(svelte@5.20.1)(vite@6.1.0) + '@sveltejs/vite-plugin-svelte': + specifier: ^5.0.0 + version: 5.0.3(svelte@5.20.1)(vite@6.1.0) + '@tailwindcss/vite': + specifier: ^4.0.0 + version: 4.0.6(vite@6.1.0) + eslint: + specifier: ^9.18.0 + version: 9.20.1 + eslint-config-prettier: + specifier: ^10.0.1 + version: 10.0.1(eslint@9.20.1) + eslint-plugin-svelte: + specifier: ^2.46.1 + version: 2.46.1(eslint@9.20.1)(svelte@5.20.1) + globals: + specifier: ^15.14.0 + version: 15.15.0 + prettier: + specifier: ^3.4.2 + version: 3.5.1 + prettier-plugin-svelte: + specifier: ^3.3.3 + version: 3.3.3(prettier@3.5.1)(svelte@5.20.1) + svelte: + specifier: ^5.0.0 + version: 5.20.1 + svelte-check: + specifier: ^4.0.0 + version: 4.1.4(svelte@5.20.1)(typescript@5.3.3) + tailwindcss: + specifier: ^4.0.0 + version: 4.0.6 + typescript: + specifier: ^5.0.0 + version: 5.3.3 + typescript-eslint: + specifier: ^8.20.0 + version: 8.24.1(eslint@9.20.1)(typescript@5.3.3) + vite: + specifier: ^6.1.0 + version: 6.1.0 + vite-plugin-top-level-await: + specifier: ^1.4.4 + version: 1.5.0(vite@6.1.0) + vite-plugin-wasm: + specifier: ^3.4.1 + version: 3.4.1(vite@6.1.0) + examples/ratchet-whisper: dependencies: '@ffmpeg/ffmpeg': @@ -129,6 +196,21 @@ packages: engines: {node: '>=10'} dev: true + /@ampproject/remapping@2.3.0: + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + dev: true + + /@babel/runtime@7.26.9: + resolution: {integrity: sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==} + engines: {node: '>=6.9.0'} + dependencies: + regenerator-runtime: 0.14.1 + dev: false + /@bedrock-layout/use-forwarded-ref@1.6.1(react@18.2.0): resolution: {integrity: sha512-GD9A9AFLzFNjr7k6fgerSqxfwDWl+wsPS11PErOKe1zkVz0y7RGC9gzlOiX/JrgpyB3NFHWIuGtoOQqifJQQpw==} peerDependencies: @@ -146,6 +228,318 @@ packages: react: 18.2.0 dev: false + /@esbuild/aix-ppc64@0.24.2: + resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm64@0.24.2: + resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm@0.24.2: + resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64@0.24.2: + resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64@0.24.2: + resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64@0.24.2: + resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.24.2: + resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.24.2: + resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64@0.24.2: + resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm@0.24.2: + resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32@0.24.2: + resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64@0.24.2: + resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el@0.24.2: + resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64@0.24.2: + resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64@0.24.2: + resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.24.2: + resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64@0.24.2: + resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-arm64@0.24.2: + resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64@0.24.2: + resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-arm64@0.24.2: + resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.24.2: + resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64@0.24.2: + resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64@0.24.2: + resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32@0.24.2: + resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64@0.24.2: + resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@eslint-community/eslint-utils@4.4.1(eslint@9.20.1): + resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 9.20.1 + eslint-visitor-keys: 3.4.3 + dev: true + + /@eslint-community/regexpp@4.12.1: + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + + /@eslint/compat@1.2.6(eslint@9.20.1): + resolution: {integrity: sha512-k7HNCqApoDHM6XzT30zGoETj+D+uUcZUb+IVAJmar3u6bvHf7hhHJcWx09QHj4/a2qrKZMWU0E16tvkiAdv06Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^9.10.0 + peerDependenciesMeta: + eslint: + optional: true + dependencies: + eslint: 9.20.1 + dev: true + + /@eslint/config-array@0.19.2: + resolution: {integrity: sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dependencies: + '@eslint/object-schema': 2.1.6 + debug: 4.4.0 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@eslint/core@0.10.0: + resolution: {integrity: sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dependencies: + '@types/json-schema': 7.0.15 + dev: true + + /@eslint/core@0.11.0: + resolution: {integrity: sha512-DWUB2pksgNEb6Bz2fggIy1wh6fGgZP4Xyy/Mt0QZPiloKKXerbqq9D3SBQTlCRYOrcRPu4vuz+CGjwdfqxnoWA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dependencies: + '@types/json-schema': 7.0.15 + dev: true + + /@eslint/eslintrc@3.2.0: + resolution: {integrity: sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dependencies: + ajv: 6.12.6 + debug: 4.4.0 + espree: 10.3.0 + globals: 14.0.0 + ignore: 5.3.1 + import-fresh: 3.3.1 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@eslint/js@9.20.0: + resolution: {integrity: sha512-iZA07H9io9Wn836aVTytRaNqh00Sad+EamwOVJT12GTLw1VGMFV/4JaME+JjLtr9fiGaoWgYnS54wrfWsSs4oQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dev: true + + /@eslint/object-schema@2.1.6: + resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dev: true + + /@eslint/plugin-kit@0.2.5: + resolution: {integrity: sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dependencies: + '@eslint/core': 0.10.0 + levn: 0.4.1 + dev: true + /@ffmpeg/ffmpeg@0.12.6: resolution: {integrity: sha512-4CuXDaqrCga5qBwVtiDDR45y65OGPYZd7VzwGCGz3QLdrQH7xaLYEjU19XL4DTCL0WnTSH8752b8Atyb1SiiLw==} engines: {node: '>=18.x'} @@ -163,6 +557,34 @@ packages: engines: {node: '>=18.x'} dev: false + /@humanfs/core@0.19.1: + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + dev: true + + /@humanfs/node@0.16.6: + resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} + engines: {node: '>=18.18.0'} + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.3.1 + dev: true + + /@humanwhocodes/module-importer@1.0.1: + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + dev: true + + /@humanwhocodes/retry@0.3.1: + resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} + engines: {node: '>=18.18'} + dev: true + + /@humanwhocodes/retry@0.4.1: + resolution: {integrity: sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==} + engines: {node: '>=18.18'} + dev: true + /@isaacs/cliui@8.0.2: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -215,6 +637,10 @@ packages: type-detect: 4.0.8 dev: true + /@kurkle/color@0.3.4: + resolution: {integrity: sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==} + dev: false + /@next/env@14.1.0: resolution: {integrity: sha512-Py8zIo+02ht82brwwhTg36iogzFqGLPXlRGKQw5s+qP/kMNc4MAyDeEwBKDijk6zTIbegEgu8Qy7C1LboslQAw==} dev: false @@ -432,64 +858,726 @@ packages: dev: true optional: true - /@swc/helpers@0.5.2: - resolution: {integrity: sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==} - dependencies: - tslib: 2.6.2 - dev: false - - /@types/node@20.11.24: - resolution: {integrity: sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==} - dependencies: - undici-types: 5.26.5 + /@polka/url@1.0.0-next.28: + resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==} dev: true - /@types/prop-types@15.7.11: - resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==} + /@rollup/plugin-virtual@3.0.2: + resolution: {integrity: sha512-10monEYsBp3scM4/ND4LNH5Rxvh3e/cVeL3jWTgZ2SrQ+BmUoQcopVQvnaMcOnykb1VkxUFuDAN+0FnpTFRy2A==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true dev: true - /@types/react-dom@18.2.19: - resolution: {integrity: sha512-aZvQL6uUbIJpjZk4U8JZGbau9KDeAwMfmhyWorxgBkqDIEf6ROjRozcmPIicqsUwPUjbkDfHKgGee1Lq65APcA==} - dependencies: - '@types/react': 18.2.61 + /@rollup/rollup-android-arm-eabi@4.34.8: + resolution: {integrity: sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw==} + cpu: [arm] + os: [android] + requiresBuild: true dev: true + optional: true - /@types/react@18.2.61: - resolution: {integrity: sha512-NURTN0qNnJa7O/k4XUkEW2yfygA+NxS0V5h1+kp9jPwhzZy95q3ADoGMP0+JypMhrZBTTgjKAUlTctde1zzeQA==} - dependencies: - '@types/prop-types': 15.7.11 - '@types/scheduler': 0.16.8 - csstype: 3.1.3 + /@rollup/rollup-android-arm64@4.34.8: + resolution: {integrity: sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q==} + cpu: [arm64] + os: [android] + requiresBuild: true dev: true + optional: true - /@types/scheduler@0.16.8: - resolution: {integrity: sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==} + /@rollup/rollup-darwin-arm64@4.34.8: + resolution: {integrity: sha512-02rVdZ5tgdUNRxIUrFdcMBZQoaPMrxtwSb+/hOfBdqkatYHR3lZ2A2EGyHq2sGOd0Owk80oV3snlDASC24He3Q==} + cpu: [arm64] + os: [darwin] + requiresBuild: true dev: true + optional: true - /acorn@8.12.1: - resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} - engines: {node: '>=0.4.0'} - hasBin: true + /@rollup/rollup-darwin-x64@4.34.8: + resolution: {integrity: sha512-qIP/elwR/tq/dYRx3lgwK31jkZvMiD6qUtOycLhTzCvrjbZ3LjQnEM9rNhSGpbLXVJYQ3rq39A6Re0h9tU2ynw==} + cpu: [x64] + os: [darwin] + requiresBuild: true dev: true + optional: true - /ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} + /@rollup/rollup-freebsd-arm64@4.34.8: + resolution: {integrity: sha512-IQNVXL9iY6NniYbTaOKdrlVP3XIqazBgJOVkddzJlqnCpRi/yAeSOa8PLcECFSQochzqApIOE1GHNu3pCz+BDA==} + cpu: [arm64] + os: [freebsd] + requiresBuild: true dev: true + optional: true - /ansi-regex@6.0.1: - resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} - engines: {node: '>=12'} + /@rollup/rollup-freebsd-x64@4.34.8: + resolution: {integrity: sha512-TYXcHghgnCqYFiE3FT5QwXtOZqDj5GmaFNTNt3jNC+vh22dc/ukG2cG+pi75QO4kACohZzidsq7yKTKwq/Jq7Q==} + cpu: [x64] + os: [freebsd] + requiresBuild: true dev: true + optional: true - /ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - dependencies: - color-convert: 2.0.1 + /@rollup/rollup-linux-arm-gnueabihf@4.34.8: + resolution: {integrity: sha512-A4iphFGNkWRd+5m3VIGuqHnG3MVnqKe7Al57u9mwgbyZ2/xF9Jio72MaY7xxh+Y87VAHmGQr73qoKL9HPbXj1g==} + cpu: [arm] + os: [linux] + requiresBuild: true dev: true + optional: true - /ansi-styles@6.2.1: + /@rollup/rollup-linux-arm-musleabihf@4.34.8: + resolution: {integrity: sha512-S0lqKLfTm5u+QTxlFiAnb2J/2dgQqRy/XvziPtDd1rKZFXHTyYLoVL58M/XFwDI01AQCDIevGLbQrMAtdyanpA==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm64-gnu@4.34.8: + resolution: {integrity: sha512-jpz9YOuPiSkL4G4pqKrus0pn9aYwpImGkosRKwNi+sJSkz+WU3anZe6hi73StLOQdfXYXC7hUfsQlTnjMd3s1A==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm64-musl@4.34.8: + resolution: {integrity: sha512-KdSfaROOUJXgTVxJNAZ3KwkRc5nggDk+06P6lgi1HLv1hskgvxHUKZ4xtwHkVYJ1Rep4GNo+uEfycCRRxht7+Q==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-loongarch64-gnu@4.34.8: + resolution: {integrity: sha512-NyF4gcxwkMFRjgXBM6g2lkT58OWztZvw5KkV2K0qqSnUEqCVcqdh2jN4gQrTn/YUpAcNKyFHfoOZEer9nwo6uQ==} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-powerpc64le-gnu@4.34.8: + resolution: {integrity: sha512-LMJc999GkhGvktHU85zNTDImZVUCJ1z/MbAJTnviiWmmjyckP5aQsHtcujMjpNdMZPT2rQEDBlJfubhs3jsMfw==} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-riscv64-gnu@4.34.8: + resolution: {integrity: sha512-xAQCAHPj8nJq1PI3z8CIZzXuXCstquz7cIOL73HHdXiRcKk8Ywwqtx2wrIy23EcTn4aZ2fLJNBB8d0tQENPCmw==} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-s390x-gnu@4.34.8: + resolution: {integrity: sha512-DdePVk1NDEuc3fOe3dPPTb+rjMtuFw89gw6gVWxQFAuEqqSdDKnrwzZHrUYdac7A7dXl9Q2Vflxpme15gUWQFA==} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-x64-gnu@4.34.8: + resolution: {integrity: sha512-8y7ED8gjxITUltTUEJLQdgpbPh1sUQ0kMTmufRF/Ns5tI9TNMNlhWtmPKKHCU0SilX+3MJkZ0zERYYGIVBYHIA==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-x64-musl@4.34.8: + resolution: {integrity: sha512-SCXcP0ZpGFIe7Ge+McxY5zKxiEI5ra+GT3QRxL0pMMtxPfpyLAKleZODi1zdRHkz5/BhueUrYtYVgubqe9JBNQ==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-arm64-msvc@4.34.8: + resolution: {integrity: sha512-YHYsgzZgFJzTRbth4h7Or0m5O74Yda+hLin0irAIobkLQFRQd1qWmnoVfwmKm9TXIZVAD0nZ+GEb2ICicLyCnQ==} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-ia32-msvc@4.34.8: + resolution: {integrity: sha512-r3NRQrXkHr4uWy5TOjTpTYojR9XmF0j/RYgKCef+Ag46FWUTltm5ziticv8LdNsDMehjJ543x/+TJAek/xBA2w==} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-x64-msvc@4.34.8: + resolution: {integrity: sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g==} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@sveltejs/adapter-static@3.0.8(@sveltejs/kit@2.17.2): + resolution: {integrity: sha512-YaDrquRpZwfcXbnlDsSrBQNCChVOT9MGuSg+dMAyfsAa1SmiAhrA5jUYUiIMC59G92kIbY/AaQOWcBdq+lh+zg==} + peerDependencies: + '@sveltejs/kit': ^2.0.0 + dependencies: + '@sveltejs/kit': 2.17.2(@sveltejs/vite-plugin-svelte@5.0.3)(svelte@5.20.1)(vite@6.1.0) + dev: true + + /@sveltejs/kit@2.17.2(@sveltejs/vite-plugin-svelte@5.0.3)(svelte@5.20.1)(vite@6.1.0): + resolution: {integrity: sha512-Vypk02baf7qd3SOB1uUwUC/3Oka+srPo2J0a8YN3EfJypRshDkNx9HzNKjSmhOnGWwT+SSO06+N0mAb8iVTmTQ==} + engines: {node: '>=18.13'} + hasBin: true + peerDependencies: + '@sveltejs/vite-plugin-svelte': ^3.0.0 || ^4.0.0-next.1 || ^5.0.0 + svelte: ^4.0.0 || ^5.0.0-next.0 + vite: ^5.0.3 || ^6.0.0 + dependencies: + '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.20.1)(vite@6.1.0) + '@types/cookie': 0.6.0 + cookie: 0.6.0 + devalue: 5.1.1 + esm-env: 1.2.2 + import-meta-resolve: 4.1.0 + kleur: 4.1.5 + magic-string: 0.30.17 + mrmime: 2.0.1 + sade: 1.8.1 + set-cookie-parser: 2.7.1 + sirv: 3.0.1 + svelte: 5.20.1 + vite: 6.1.0 + dev: true + + /@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.0.3)(svelte@5.20.1)(vite@6.1.0): + resolution: {integrity: sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22} + peerDependencies: + '@sveltejs/vite-plugin-svelte': ^5.0.0 + svelte: ^5.0.0 + vite: ^6.0.0 + dependencies: + '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.20.1)(vite@6.1.0) + debug: 4.4.0 + svelte: 5.20.1 + vite: 6.1.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.20.1)(vite@6.1.0): + resolution: {integrity: sha512-MCFS6CrQDu1yGwspm4qtli0e63vaPCehf6V7pIMP15AsWgMKrqDGCPFF/0kn4SP0ii4aySu4Pa62+fIRGFMjgw==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22} + peerDependencies: + svelte: ^5.0.0 + vite: ^6.0.0 + dependencies: + '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.0.3)(svelte@5.20.1)(vite@6.1.0) + debug: 4.4.0 + deepmerge: 4.3.1 + kleur: 4.1.5 + magic-string: 0.30.17 + svelte: 5.20.1 + vite: 6.1.0 + vitefu: 1.0.5(vite@6.1.0) + transitivePeerDependencies: + - supports-color + dev: true + + /@swc/core-darwin-arm64@1.10.17: + resolution: {integrity: sha512-LSQhSjESleTc0c45BnVKRacp9Nl4zhJMlV/nmhpFCOv/CqHI5YBDX5c9bPk9jTRNHIf0QH92uTtswt8yN++TCQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@swc/core-darwin-x64@1.10.17: + resolution: {integrity: sha512-TTaZFS4jLuA3y6+D2HYv4yVGhmjkOGG6KyAwBiJEeoUaazX5MYOyQwaZBPhRGtzHZFrzi4t4jNix4kAkMajPkQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-arm-gnueabihf@1.10.17: + resolution: {integrity: sha512-8P+ESJyGnVdJi0nUcQfxkbTiB/7hnu6N3U72KbvHFBcuroherwzW4DId1XD4RTU2Cjsh1dztZoCcOLY8W9RW1Q==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-arm64-gnu@1.10.17: + resolution: {integrity: sha512-zT21jDQCe+IslzOtw+BD/9ElO/H4qU4fkkOeVQ68PcxuqYS2gwyDxWqa9IGwpzWexYM+Lzi1rAbl/1BM6nGW8Q==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-arm64-musl@1.10.17: + resolution: {integrity: sha512-C2jaW1X+93HscVcesKYgSuZ9GaKqKcQvwvD+q+4JZkaKF4Zopt/aguc6Tmn/nuavRk0WV8yVCpHXoP7lz/2akA==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-x64-gnu@1.10.17: + resolution: {integrity: sha512-vfyxqV5gddurG2NVJLemR/68s7GTe0QruozrZiDpNqr9V4VX9t3PadDKMDAvQz6jKrtiqMtshNXQTNRKAKlzFw==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-x64-musl@1.10.17: + resolution: {integrity: sha512-8M+nI5MHZGQUnXyfTLsGw85a3oQRXMsFjgMZuOEJO9ZGBIEnYVuWOxENfcP6MmlJmTOW+cJxHnMGhKY+fjcntw==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-win32-arm64-msvc@1.10.17: + resolution: {integrity: sha512-iUeIBFM6c/NwsreLFSAH395Dahc+54mSi0Kq//IrZ2Y16VlqCV7VHdOIMrdAyDoBFUvh0jKuLJPWt+jlKGtSLg==} + engines: {node: '>=10'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@swc/core-win32-ia32-msvc@1.10.17: + resolution: {integrity: sha512-lPXYFvkfYIN8HdNmG6dCnQqgA+rOSTgeAjIhGsYCEyLsYkkhF2FQw34OF6PnWawQ6hOdOE9v6Bw3T4enj3Lb6w==} + engines: {node: '>=10'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@swc/core-win32-x64-msvc@1.10.17: + resolution: {integrity: sha512-KrnkFEWpBmxSe8LixhAZXeeUwTNDVukrPeXJ1PiG+pmb5nI989I9J9IQVIgBv+JXXaK+rmiWjlcIkphaDJJEAA==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@swc/core@1.10.17: + resolution: {integrity: sha512-FXZx7jHpiwz4fTuuueWwsvN7VFLSoeS3mcxCTPUNOHs/K2ecaBO+slh5T5Xvt/KGuD2I/2T8G6Zts0maPkt2lQ==} + engines: {node: '>=10'} + requiresBuild: true + peerDependencies: + '@swc/helpers': '*' + peerDependenciesMeta: + '@swc/helpers': + optional: true + dependencies: + '@swc/counter': 0.1.3 + '@swc/types': 0.1.17 + optionalDependencies: + '@swc/core-darwin-arm64': 1.10.17 + '@swc/core-darwin-x64': 1.10.17 + '@swc/core-linux-arm-gnueabihf': 1.10.17 + '@swc/core-linux-arm64-gnu': 1.10.17 + '@swc/core-linux-arm64-musl': 1.10.17 + '@swc/core-linux-x64-gnu': 1.10.17 + '@swc/core-linux-x64-musl': 1.10.17 + '@swc/core-win32-arm64-msvc': 1.10.17 + '@swc/core-win32-ia32-msvc': 1.10.17 + '@swc/core-win32-x64-msvc': 1.10.17 + dev: true + + /@swc/counter@0.1.3: + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + dev: true + + /@swc/helpers@0.5.2: + resolution: {integrity: sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==} + dependencies: + tslib: 2.6.2 + dev: false + + /@swc/types@0.1.17: + resolution: {integrity: sha512-V5gRru+aD8YVyCOMAjMpWR1Ui577DD5KSJsHP8RAxopAH22jFz6GZd/qxqjO6MJHQhcsjvjOFXyDhyLQUnMveQ==} + dependencies: + '@swc/counter': 0.1.3 + dev: true + + /@tailwindcss/node@4.0.6: + resolution: {integrity: sha512-jb6E0WeSq7OQbVYcIJ6LxnZTeC4HjMvbzFBMCrQff4R50HBlo/obmYNk6V2GCUXDeqiXtvtrQgcIbT+/boB03Q==} + dependencies: + enhanced-resolve: 5.18.1 + jiti: 2.4.2 + tailwindcss: 4.0.6 + dev: true + + /@tailwindcss/oxide-android-arm64@4.0.6: + resolution: {integrity: sha512-xDbym6bDPW3D2XqQqX3PjqW3CKGe1KXH7Fdkc60sX5ZLVUbzPkFeunQaoP+BuYlLc2cC1FoClrIRYnRzof9Sow==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@tailwindcss/oxide-darwin-arm64@4.0.6: + resolution: {integrity: sha512-1f71/ju/tvyGl5c2bDkchZHy8p8EK/tDHCxlpYJ1hGNvsYihZNurxVpZ0DefpN7cNc9RTT8DjrRoV8xXZKKRjg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@tailwindcss/oxide-darwin-x64@4.0.6: + resolution: {integrity: sha512-s/hg/ZPgxFIrGMb0kqyeaqZt505P891buUkSezmrDY6lxv2ixIELAlOcUVTkVh245SeaeEiUVUPiUN37cwoL2g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@tailwindcss/oxide-freebsd-x64@4.0.6: + resolution: {integrity: sha512-Z3Wo8FWZnmio8+xlcbb7JUo/hqRMSmhQw8IGIRoRJ7GmLR0C+25Wq+bEX/135xe/yEle2lFkhu9JBHd4wZYiig==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@tailwindcss/oxide-linux-arm-gnueabihf@4.0.6: + resolution: {integrity: sha512-SNSwkkim1myAgmnbHs4EjXsPL7rQbVGtjcok5EaIzkHkCAVK9QBQsWeP2Jm2/JJhq4wdx8tZB9Y7psMzHYWCkA==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@tailwindcss/oxide-linux-arm64-gnu@4.0.6: + resolution: {integrity: sha512-tJ+mevtSDMQhKlwCCuhsFEFg058kBiSy4TkoeBG921EfrHKmexOaCyFKYhVXy4JtkaeeOcjJnCLasEeqml4i+Q==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@tailwindcss/oxide-linux-arm64-musl@4.0.6: + resolution: {integrity: sha512-IoArz1vfuTR4rALXMUXI/GWWfx2EaO4gFNtBNkDNOYhlTD4NVEwE45nbBoojYiTulajI4c2XH8UmVEVJTOJKxA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@tailwindcss/oxide-linux-x64-gnu@4.0.6: + resolution: {integrity: sha512-QtsUfLkEAeWAC3Owx9Kg+7JdzE+k9drPhwTAXbXugYB9RZUnEWWx5x3q/au6TvUYcL+n0RBqDEO2gucZRvRFgQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@tailwindcss/oxide-linux-x64-musl@4.0.6: + resolution: {integrity: sha512-QthvJqIji2KlGNwLcK/PPYo7w1Wsi/8NK0wAtRGbv4eOPdZHkQ9KUk+oCoP20oPO7i2a6X1aBAFQEL7i08nNMA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@tailwindcss/oxide-win32-arm64-msvc@4.0.6: + resolution: {integrity: sha512-+oka+dYX8jy9iP00DJ9Y100XsqvbqR5s0yfMZJuPR1H/lDVtDfsZiSix1UFBQ3X1HWxoEEl6iXNJHWd56TocVw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@tailwindcss/oxide-win32-x64-msvc@4.0.6: + resolution: {integrity: sha512-+o+juAkik4p8Ue/0LiflQXPmVatl6Av3LEZXpBTfg4qkMIbZdhCGWFzHdt2NjoMiLOJCFDddoV6GYaimvK1Olw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@tailwindcss/oxide@4.0.6: + resolution: {integrity: sha512-lVyKV2y58UE9CeKVcYykULe9QaE1dtKdxDEdrTPIdbzRgBk6bdxHNAoDqvcqXbIGXubn3VOl1O/CFF77v/EqSA==} + engines: {node: '>= 10'} + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.0.6 + '@tailwindcss/oxide-darwin-arm64': 4.0.6 + '@tailwindcss/oxide-darwin-x64': 4.0.6 + '@tailwindcss/oxide-freebsd-x64': 4.0.6 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.0.6 + '@tailwindcss/oxide-linux-arm64-gnu': 4.0.6 + '@tailwindcss/oxide-linux-arm64-musl': 4.0.6 + '@tailwindcss/oxide-linux-x64-gnu': 4.0.6 + '@tailwindcss/oxide-linux-x64-musl': 4.0.6 + '@tailwindcss/oxide-win32-arm64-msvc': 4.0.6 + '@tailwindcss/oxide-win32-x64-msvc': 4.0.6 + dev: true + + /@tailwindcss/vite@4.0.6(vite@6.1.0): + resolution: {integrity: sha512-O25vZ/URWbZ2JHdk2o8wH7jOKqEGCsYmX3GwGmYS5DjE4X3mpf93a72Rn7VRnefldNauBzr5z2hfZptmBNtTUQ==} + peerDependencies: + vite: ^5.2.0 || ^6 + dependencies: + '@tailwindcss/node': 4.0.6 + '@tailwindcss/oxide': 4.0.6 + lightningcss: 1.29.1 + tailwindcss: 4.0.6 + vite: 6.1.0 + dev: true + + /@types/cookie@0.6.0: + resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + dev: true + + /@types/estree@1.0.6: + resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + dev: true + + /@types/json-schema@7.0.15: + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + dev: true + + /@types/node@20.11.24: + resolution: {integrity: sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==} + dependencies: + undici-types: 5.26.5 + dev: true + + /@types/prop-types@15.7.11: + resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==} + dev: true + + /@types/react-dom@18.2.19: + resolution: {integrity: sha512-aZvQL6uUbIJpjZk4U8JZGbau9KDeAwMfmhyWorxgBkqDIEf6ROjRozcmPIicqsUwPUjbkDfHKgGee1Lq65APcA==} + dependencies: + '@types/react': 18.2.61 + dev: true + + /@types/react@18.2.61: + resolution: {integrity: sha512-NURTN0qNnJa7O/k4XUkEW2yfygA+NxS0V5h1+kp9jPwhzZy95q3ADoGMP0+JypMhrZBTTgjKAUlTctde1zzeQA==} + dependencies: + '@types/prop-types': 15.7.11 + '@types/scheduler': 0.16.8 + csstype: 3.1.3 + dev: true + + /@types/scheduler@0.16.8: + resolution: {integrity: sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==} + dev: true + + /@typescript-eslint/eslint-plugin@8.24.1(@typescript-eslint/parser@8.24.1)(eslint@9.20.1)(typescript@5.3.3): + resolution: {integrity: sha512-ll1StnKtBigWIGqvYDVuDmXJHVH4zLVot1yQ4fJtLpL7qacwkxJc1T0bptqw+miBQ/QfUbhl1TcQ4accW5KUyA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 8.24.1(eslint@9.20.1)(typescript@5.3.3) + '@typescript-eslint/scope-manager': 8.24.1 + '@typescript-eslint/type-utils': 8.24.1(eslint@9.20.1)(typescript@5.3.3) + '@typescript-eslint/utils': 8.24.1(eslint@9.20.1)(typescript@5.3.3) + '@typescript-eslint/visitor-keys': 8.24.1 + eslint: 9.20.1 + graphemer: 1.4.0 + ignore: 5.3.1 + natural-compare: 1.4.0 + ts-api-utils: 2.0.1(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser@8.24.1(eslint@9.20.1)(typescript@5.3.3): + resolution: {integrity: sha512-Tqoa05bu+t5s8CTZFaGpCH2ub3QeT9YDkXbPd3uQ4SfsLoh1/vv2GEYAioPoxCWJJNsenXlC88tRjwoHNts1oQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' + dependencies: + '@typescript-eslint/scope-manager': 8.24.1 + '@typescript-eslint/types': 8.24.1 + '@typescript-eslint/typescript-estree': 8.24.1(typescript@5.3.3) + '@typescript-eslint/visitor-keys': 8.24.1 + debug: 4.4.0 + eslint: 9.20.1 + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager@8.24.1: + resolution: {integrity: sha512-OdQr6BNBzwRjNEXMQyaGyZzgg7wzjYKfX2ZBV3E04hUCBDv3GQCHiz9RpqdUIiVrMgJGkXm3tcEh4vFSHreS2Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dependencies: + '@typescript-eslint/types': 8.24.1 + '@typescript-eslint/visitor-keys': 8.24.1 + dev: true + + /@typescript-eslint/type-utils@8.24.1(eslint@9.20.1)(typescript@5.3.3): + resolution: {integrity: sha512-/Do9fmNgCsQ+K4rCz0STI7lYB4phTtEXqqCAs3gZW0pnK7lWNkvWd5iW545GSmApm4AzmQXmSqXPO565B4WVrw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' + dependencies: + '@typescript-eslint/typescript-estree': 8.24.1(typescript@5.3.3) + '@typescript-eslint/utils': 8.24.1(eslint@9.20.1)(typescript@5.3.3) + debug: 4.4.0 + eslint: 9.20.1 + ts-api-utils: 2.0.1(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/types@8.24.1: + resolution: {integrity: sha512-9kqJ+2DkUXiuhoiYIUvIYjGcwle8pcPpdlfkemGvTObzgmYfJ5d0Qm6jwb4NBXP9W1I5tss0VIAnWFumz3mC5A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dev: true + + /@typescript-eslint/typescript-estree@8.24.1(typescript@5.3.3): + resolution: {integrity: sha512-UPyy4MJ/0RE648DSKQe9g0VDSehPINiejjA6ElqnFaFIhI6ZEiZAkUI0D5MCk0bQcTf/LVqZStvQ6K4lPn/BRg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.8.0' + dependencies: + '@typescript-eslint/types': 8.24.1 + '@typescript-eslint/visitor-keys': 8.24.1 + debug: 4.4.0 + fast-glob: 3.3.2 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.1 + ts-api-utils: 2.0.1(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils@8.24.1(eslint@9.20.1)(typescript@5.3.3): + resolution: {integrity: sha512-OOcg3PMMQx9EXspId5iktsI3eMaXVwlhC8BvNnX6B5w9a4dVgpkQZuU8Hy67TolKcl+iFWq0XX+jbDGN4xWxjQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' + dependencies: + '@eslint-community/eslint-utils': 4.4.1(eslint@9.20.1) + '@typescript-eslint/scope-manager': 8.24.1 + '@typescript-eslint/types': 8.24.1 + '@typescript-eslint/typescript-estree': 8.24.1(typescript@5.3.3) + eslint: 9.20.1 + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/visitor-keys@8.24.1: + resolution: {integrity: sha512-EwVHlp5l+2vp8CoqJm9KikPZgi3gbdZAtabKT9KPShGeOcJhsv4Zdo3oc8T8I0uKEmYoU4ItyxbptjF08enaxg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dependencies: + '@typescript-eslint/types': 8.24.1 + eslint-visitor-keys: 4.2.0 + dev: true + + /acorn-jsx@5.3.2(acorn@8.12.1): + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.12.1 + dev: true + + /acorn-jsx@5.3.2(acorn@8.14.0): + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.14.0 + dev: true + + /acorn-typescript@1.4.13(acorn@8.12.1): + resolution: {integrity: sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q==} + peerDependencies: + acorn: '>=8.9.0' + dependencies: + acorn: 8.12.1 + dev: true + + /acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /acorn@8.14.0: + resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true + + /ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + dev: true + + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: true + + /ansi-styles@6.2.1: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} dev: true @@ -510,6 +1598,15 @@ packages: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} dev: true + /argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + + /aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + dev: true + /autoprefixer@10.4.18(postcss@8.4.35): resolution: {integrity: sha512-1DKbDfsr6KUElM6wg+0zRNkB/Q7WcKYAaK+pzXn+Xqmszm/5Xa9coeNdtP88Vi+dPzZnMjhge8GIV49ZQkDa+g==} engines: {node: ^10 || ^12 || >=14} @@ -534,6 +1631,11 @@ packages: - debug dev: true + /axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + dev: true + /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true @@ -604,6 +1706,11 @@ packages: resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} dev: true + /callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + /camelcase-css@2.0.1: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} @@ -612,6 +1719,21 @@ packages: /caniuse-lite@1.0.30001591: resolution: {integrity: sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ==} + /chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /chart.js@4.4.7: + resolution: {integrity: sha512-pwkcKfdzTMAU/+jNosKhNL2bHtJc/sSmYgVbuGTEDhzkrhmyihmP7vUc/5ZK9WopidMDHNe3Wm7jOd/WhuHWuw==} + engines: {pnpm: '>=8'} + dependencies: + '@kurkle/color': 0.3.4 + dev: false + /chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} @@ -627,6 +1749,13 @@ packages: fsevents: 2.3.3 dev: true + /chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + dependencies: + readdirp: 4.1.2 + dev: true + /chownr@2.0.0: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} @@ -640,6 +1769,11 @@ packages: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} dev: false + /clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + dev: true + /color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -656,6 +1790,10 @@ packages: engines: {node: '>= 6'} dev: true + /complex.js@2.4.2: + resolution: {integrity: sha512-qtx7HRhPGSCBtGiST4/WGHuW+zeaND/6Ld+db6PbrulIB1i2Ev/2UPiqcmpQNPSyfBKraC0EOvOKCB5dGZKt3g==} + dev: false + /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} dev: true @@ -664,6 +1802,11 @@ packages: resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} dev: true + /cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} + dev: true + /cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -673,6 +1816,15 @@ packages: which: 2.0.2 dev: true + /cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + /cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} @@ -682,15 +1834,50 @@ packages: /csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + /debug@4.4.0: + resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.3 + dev: true + + /decimal.js@10.5.0: + resolution: {integrity: sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==} + dev: false + /decode-uri-component@0.4.1: resolution: {integrity: sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ==} engines: {node: '>=14.16'} dev: true + /deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + dev: true + + /deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + dev: true + /deprecation@2.3.1: resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==} dev: true + /detect-libc@1.0.3: + resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} + engines: {node: '>=0.10'} + hasBin: true + dev: true + + /devalue@5.1.1: + resolution: {integrity: sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==} + dev: true + /didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} dev: true @@ -715,11 +1902,237 @@ packages: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} dev: true + /enhanced-resolve@5.18.1: + resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} + engines: {node: '>=10.13.0'} + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.1 + dev: true + + /esbuild@0.24.2: + resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==} + engines: {node: '>=18'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/aix-ppc64': 0.24.2 + '@esbuild/android-arm': 0.24.2 + '@esbuild/android-arm64': 0.24.2 + '@esbuild/android-x64': 0.24.2 + '@esbuild/darwin-arm64': 0.24.2 + '@esbuild/darwin-x64': 0.24.2 + '@esbuild/freebsd-arm64': 0.24.2 + '@esbuild/freebsd-x64': 0.24.2 + '@esbuild/linux-arm': 0.24.2 + '@esbuild/linux-arm64': 0.24.2 + '@esbuild/linux-ia32': 0.24.2 + '@esbuild/linux-loong64': 0.24.2 + '@esbuild/linux-mips64el': 0.24.2 + '@esbuild/linux-ppc64': 0.24.2 + '@esbuild/linux-riscv64': 0.24.2 + '@esbuild/linux-s390x': 0.24.2 + '@esbuild/linux-x64': 0.24.2 + '@esbuild/netbsd-arm64': 0.24.2 + '@esbuild/netbsd-x64': 0.24.2 + '@esbuild/openbsd-arm64': 0.24.2 + '@esbuild/openbsd-x64': 0.24.2 + '@esbuild/sunos-x64': 0.24.2 + '@esbuild/win32-arm64': 0.24.2 + '@esbuild/win32-ia32': 0.24.2 + '@esbuild/win32-x64': 0.24.2 + dev: true + /escalade@3.1.2: resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} engines: {node: '>=6'} dev: true + /escape-latex@1.2.0: + resolution: {integrity: sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==} + dev: false + + /escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + dev: true + + /eslint-compat-utils@0.5.1(eslint@9.20.1): + resolution: {integrity: sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==} + engines: {node: '>=12'} + peerDependencies: + eslint: '>=6.0.0' + dependencies: + eslint: 9.20.1 + semver: 7.7.1 + dev: true + + /eslint-config-prettier@10.0.1(eslint@9.20.1): + resolution: {integrity: sha512-lZBts941cyJyeaooiKxAtzoPHTN+GbQTJFAIdQbRhA4/8whaAraEh47Whw/ZFfrjNSnlAxqfm9i0XVAEkULjCw==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + dependencies: + eslint: 9.20.1 + dev: true + + /eslint-plugin-svelte@2.46.1(eslint@9.20.1)(svelte@5.20.1): + resolution: {integrity: sha512-7xYr2o4NID/f9OEYMqxsEQsCsj4KaMy4q5sANaKkAb6/QeCjYFxRmDm2S3YC3A3pl1kyPZ/syOx/i7LcWYSbIw==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0-0 || ^9.0.0-0 + svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + svelte: + optional: true + dependencies: + '@eslint-community/eslint-utils': 4.4.1(eslint@9.20.1) + '@jridgewell/sourcemap-codec': 1.5.0 + eslint: 9.20.1 + eslint-compat-utils: 0.5.1(eslint@9.20.1) + esutils: 2.0.3 + known-css-properties: 0.35.0 + postcss: 8.5.2 + postcss-load-config: 3.1.4(postcss@8.5.2) + postcss-safe-parser: 6.0.0(postcss@8.5.2) + postcss-selector-parser: 6.1.2 + semver: 7.7.1 + svelte: 5.20.1 + svelte-eslint-parser: 0.43.0(svelte@5.20.1) + transitivePeerDependencies: + - ts-node + dev: true + + /eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + + /eslint-scope@8.2.0: + resolution: {integrity: sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + + /eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /eslint-visitor-keys@4.2.0: + resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dev: true + + /eslint@9.20.1: + resolution: {integrity: sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + dependencies: + '@eslint-community/eslint-utils': 4.4.1(eslint@9.20.1) + '@eslint-community/regexpp': 4.12.1 + '@eslint/config-array': 0.19.2 + '@eslint/core': 0.11.0 + '@eslint/eslintrc': 3.2.0 + '@eslint/js': 9.20.0 + '@eslint/plugin-kit': 0.2.5 + '@humanfs/node': 0.16.6 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.1 + '@types/estree': 1.0.6 + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.0 + escape-string-regexp: 4.0.0 + eslint-scope: 8.2.0 + eslint-visitor-keys: 4.2.0 + espree: 10.3.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.1 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + dev: true + + /esm-env@1.2.2: + resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} + dev: true + + /espree@10.3.0: + resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dependencies: + acorn: 8.14.0 + acorn-jsx: 5.3.2(acorn@8.14.0) + eslint-visitor-keys: 4.2.0 + dev: true + + /espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.12.1 + acorn-jsx: 5.3.2(acorn@8.12.1) + eslint-visitor-keys: 3.4.3 + dev: true + + /esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + /esrap@1.4.5: + resolution: {integrity: sha512-CjNMjkBWWZeHn+VX+gS8YvFwJ5+NDhg8aWZBSFJPR8qQduDNjbJodA2WcwCm7uQa5Rjqj+nZvVmceg1RbHFB9g==} + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + dev: true + + /esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + /estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + dev: true + + /esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + dev: true + + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: true + /fast-glob@3.3.2: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} engines: {node: '>=8.6.0'} @@ -731,12 +2144,36 @@ packages: micromatch: 4.0.5 dev: true + /fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + /fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + dev: true + /fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} dependencies: reusify: 1.0.4 dev: true + /fdir@6.4.3: + resolution: {integrity: sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + dev: true + + /file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + dependencies: + flat-cache: 4.0.1 + dev: true + /fill-range@7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} @@ -749,10 +2186,30 @@ packages: engines: {node: '>=14.16'} dev: true + /find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + /fix-webm-duration@1.0.5: resolution: {integrity: sha512-b6oula3OfSknx0aWoLsxvp4DVIYbwsf+UAkr6EDAK3iuMYk/OSNKzmeSI61GXK0MmFTEuzle19BPvTxMIKjkZg==} dev: false + /flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + dependencies: + flatted: 3.3.2 + keyv: 4.5.4 + dev: true + + /flatted@3.3.2: + resolution: {integrity: sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==} + dev: true + /follow-redirects@1.15.6: resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} engines: {node: '>=4.0'} @@ -775,6 +2232,11 @@ packages: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} dev: true + /fraction.js@5.2.1: + resolution: {integrity: sha512-Ah6t/7YCYjrPUFUFsOsRLMXAdnYM+aQwmojD2Ayb/Ezr82SwES0vuyQ8qZ3QO8n9j7W14VJuVZZet8U3bhSdQQ==} + engines: {node: '>= 12'} + dev: false + /fs-minipass@2.1.0: resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} engines: {node: '>= 8'} @@ -790,6 +2252,7 @@ packages: 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 @@ -835,6 +2298,16 @@ packages: path-is-absolute: 1.0.1 dev: true + /globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + dev: true + + /globals@15.15.0: + resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} + engines: {node: '>=18'} + dev: true + /goober@2.1.14(csstype@3.1.3): resolution: {integrity: sha512-4UpC0NdGyAFqLNPnhCT2iHpza2q+RAY3GV85a/mRPdzyPQMsj0KmMMuetdIkzWRbJ+Hgau1EZztq8ImmiMGhsg==} peerDependencies: @@ -845,7 +2318,15 @@ packages: /graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - dev: false + + /graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + dev: true + + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true /hasown@2.0.1: resolution: {integrity: sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==} @@ -859,6 +2340,23 @@ packages: engines: {node: '>= 4'} dev: true + /import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + /import-meta-resolve@4.1.0: + resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==} + dev: true + + /imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + dev: true + /inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. @@ -906,6 +2404,12 @@ packages: engines: {node: '>=0.12.0'} dev: true + /is-reference@3.0.3: + resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} + dependencies: + '@types/estree': 1.0.6 + dev: true + /isbinaryfile@5.0.2: resolution: {integrity: sha512-GvcjojwonMjWbTkfMpnVHVqXW/wKMYDfEpY94/8zy8HFMOqb/VL6oeONq9v87q4ttVlaTLnGXnJD4B5B1OTGIg==} engines: {node: '>= 18.0.0'} @@ -915,24 +2419,183 @@ packages: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true - /jackspeak@2.3.6: - resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} - engines: {node: '>=14'} + /jackspeak@2.3.6: + resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} + engines: {node: '>=14'} + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + dev: true + + /javascript-natural-sort@0.7.1: + resolution: {integrity: sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==} + dev: false + + /jiti@1.21.0: + resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==} + hasBin: true + dev: true + + /jiti@2.4.2: + resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} + hasBin: true + dev: true + + /js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + dev: false + + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + /json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + dev: true + + /json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + /json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + dev: true + + /keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + dependencies: + json-buffer: 3.0.1 + dev: true + + /kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + dev: true + + /known-css-properties@0.35.0: + resolution: {integrity: sha512-a/RAk2BfKk+WFGhhOCAYqSiFLc34k8Mt/6NWRI4joER0EYUzXIcFivjjnoD3+XU1DggLn/tZc3DOAgke7l8a4A==} + dev: true + + /levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /lightningcss-darwin-arm64@1.29.1: + resolution: {integrity: sha512-HtR5XJ5A0lvCqYAoSv2QdZZyoHNttBpa5EP9aNuzBQeKGfbyH5+UipLWvVzpP4Uml5ej4BYs5I9Lco9u1fECqw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /lightningcss-darwin-x64@1.29.1: + resolution: {integrity: sha512-k33G9IzKUpHy/J/3+9MCO4e+PzaFblsgBjSGlpAaFikeBFm8B/CkO3cKU9oI4g+fjS2KlkLM/Bza9K/aw8wsNA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /lightningcss-freebsd-x64@1.29.1: + resolution: {integrity: sha512-0SUW22fv/8kln2LnIdOCmSuXnxgxVC276W5KLTwoehiO0hxkacBxjHOL5EtHD8BAXg2BvuhsJPmVMasvby3LiQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /lightningcss-linux-arm-gnueabihf@1.29.1: + resolution: {integrity: sha512-sD32pFvlR0kDlqsOZmYqH/68SqUMPNj+0pucGxToXZi4XZgZmqeX/NkxNKCPsswAXU3UeYgDSpGhu05eAufjDg==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /lightningcss-linux-arm64-gnu@1.29.1: + resolution: {integrity: sha512-0+vClRIZ6mmJl/dxGuRsE197o1HDEeeRk6nzycSy2GofC2JsY4ifCRnvUWf/CUBQmlrvMzt6SMQNMSEu22csWQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /lightningcss-linux-arm64-musl@1.29.1: + resolution: {integrity: sha512-UKMFrG4rL/uHNgelBsDwJcBqVpzNJbzsKkbI3Ja5fg00sgQnHw/VrzUTEc4jhZ+AN2BvQYz/tkHu4vt1kLuJyw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /lightningcss-linux-x64-gnu@1.29.1: + resolution: {integrity: sha512-u1S+xdODy/eEtjADqirA774y3jLcm8RPtYztwReEXoZKdzgsHYPl0s5V52Tst+GKzqjebkULT86XMSxejzfISw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /lightningcss-linux-x64-musl@1.29.1: + resolution: {integrity: sha512-L0Tx0DtaNUTzXv0lbGCLB/c/qEADanHbu4QdcNOXLIe1i8i22rZRpbT3gpWYsCh9aSL9zFujY/WmEXIatWvXbw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /lightningcss-win32-arm64-msvc@1.29.1: + resolution: {integrity: sha512-QoOVnkIEFfbW4xPi+dpdft/zAKmgLgsRHfJalEPYuJDOWf7cLQzYg0DEh8/sn737FaeMJxHZRc1oBreiwZCjog==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /lightningcss-win32-x64-msvc@1.29.1: + resolution: {integrity: sha512-NygcbThNBe4JElP+olyTI/doBNGJvLs3bFCRPdvuCcxZCcCZ71B858IHpdm7L1btZex0FvCmM17FK98Y9MRy1Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /lightningcss@1.29.1: + resolution: {integrity: sha512-FmGoeD4S05ewj+AkhTY+D+myDvXI6eL27FjHIjoyUkO/uw7WZD1fBVs0QxeYWa7E17CUHJaYX/RUGISCtcrG4Q==} + engines: {node: '>= 12.0.0'} dependencies: - '@isaacs/cliui': 8.0.2 + detect-libc: 1.0.3 optionalDependencies: - '@pkgjs/parseargs': 0.11.0 + lightningcss-darwin-arm64: 1.29.1 + lightningcss-darwin-x64: 1.29.1 + lightningcss-freebsd-x64: 1.29.1 + lightningcss-linux-arm-gnueabihf: 1.29.1 + lightningcss-linux-arm64-gnu: 1.29.1 + lightningcss-linux-arm64-musl: 1.29.1 + lightningcss-linux-x64-gnu: 1.29.1 + lightningcss-linux-x64-musl: 1.29.1 + lightningcss-win32-arm64-msvc: 1.29.1 + lightningcss-win32-x64-msvc: 1.29.1 dev: true - /jiti@1.21.0: - resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==} - hasBin: true - dev: true - - /js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - dev: false - /lilconfig@2.1.0: resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} engines: {node: '>=10'} @@ -947,6 +2610,21 @@ packages: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} dev: true + /locate-character@3.0.0: + resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} + dev: true + + /locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + /loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -959,6 +2637,28 @@ packages: engines: {node: 14 || >=16.14} dev: true + /magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + dev: true + + /mathjs@14.2.1: + resolution: {integrity: sha512-vARWETUx75+kR2K9qBV20n6NYtGXCuQKX8Zo4+AhJI5LX+ukSM1NYebv+wLnJG8KMvEe9H01sJUyC5bMciA4Tg==} + engines: {node: '>= 18'} + hasBin: true + dependencies: + '@babel/runtime': 7.26.9 + complex.js: 2.4.2 + decimal.js: 10.5.0 + escape-latex: 1.2.0 + fraction.js: 5.2.1 + javascript-natural-sort: 0.7.1 + seedrandom: 3.0.5 + tiny-emitter: 2.1.0 + typed-function: 4.2.1 + dev: false + /merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -985,6 +2685,13 @@ packages: brace-expansion: 2.0.1 dev: true + /minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: true + /minipass@3.3.6: resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} engines: {node: '>=8'} @@ -1025,6 +2732,20 @@ packages: ufo: 1.5.3 dev: true + /mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + dev: true + + /mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + dev: true + + /ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + dev: true + /mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} dependencies: @@ -1038,6 +2759,16 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + /nanoid@3.3.8: + resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + + /natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: true + /next@14.1.0(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-wlzrsbfeSU48YQBjZhDzOwhWhGsy+uQycR8bHAOt1LY1bn3zZEcDyHQOEoN3aWzQ8LHCAJ1nqrWCc9XF2+O45Q==} engines: {node: '>=18.17.0'} @@ -1107,6 +2838,44 @@ packages: wrappy: 1.0.2 dev: true + /optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + dev: true + + /p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + + /path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + /path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} @@ -1136,6 +2905,10 @@ packages: /picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + /picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + dev: true + /picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} @@ -1194,6 +2967,23 @@ packages: postcss: 8.4.35 dev: true + /postcss-load-config@3.1.4(postcss@8.5.2): + resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} + engines: {node: '>= 10'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + dependencies: + lilconfig: 2.1.0 + postcss: 8.5.2 + yaml: 1.10.2 + dev: true + /postcss-load-config@4.0.2(postcss@8.4.35): resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} engines: {node: '>= 14'} @@ -1221,6 +3011,24 @@ packages: postcss-selector-parser: 6.0.15 dev: true + /postcss-safe-parser@6.0.0(postcss@8.5.2): + resolution: {integrity: sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.3.3 + dependencies: + postcss: 8.5.2 + dev: true + + /postcss-scss@4.0.9(postcss@8.5.2): + resolution: {integrity: sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.4.29 + dependencies: + postcss: 8.5.2 + dev: true + /postcss-selector-parser@6.0.15: resolution: {integrity: sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==} engines: {node: '>=4'} @@ -1229,6 +3037,14 @@ packages: util-deprecate: 1.0.2 dev: true + /postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + dev: true + /postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} dev: true @@ -1251,6 +3067,41 @@ packages: source-map-js: 1.0.2 dev: true + /postcss@8.5.2: + resolution: {integrity: sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.8 + picocolors: 1.1.1 + source-map-js: 1.2.1 + dev: true + + /prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + dev: true + + /prettier-plugin-svelte@3.3.3(prettier@3.5.1)(svelte@5.20.1): + resolution: {integrity: sha512-yViK9zqQ+H2qZD1w/bH7W8i+bVfKrD8GIFjkFe4Thl6kCT9SlAsXVNmt3jCvQOCsnOhcvYgsoVlRV/Eu6x5nNw==} + peerDependencies: + prettier: ^3.0.0 + svelte: ^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0 + dependencies: + prettier: 3.5.1 + svelte: 5.20.1 + dev: true + + /prettier@3.5.1: + resolution: {integrity: sha512-hPpFQvHwL3Qv5AdRvBFMhnKo4tYxp0ReXiPn2bxkiohEX6mBeBwEpBSQTkD458RaaDKQMYSp4hX4UtfUTA5wDw==} + engines: {node: '>=14'} + hasBin: true + dev: true + + /punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + dev: true + /query-registry@3.0.1: resolution: {integrity: sha512-M9RxRITi2mHMVPU5zysNjctUT8bAPx6ltEXo/ir9+qmiM47Y7f0Ir3+OxUO5OjYAWdicBQRew7RtHtqUXydqlg==} engines: {node: '>=20'} @@ -1338,6 +3189,20 @@ packages: picomatch: 2.3.1 dev: true + /readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + dev: true + + /regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + dev: false + + /resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + /resolve@1.22.8: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true @@ -1360,18 +3225,68 @@ packages: glob: 7.2.3 dev: true + /rollup@4.34.8: + resolution: {integrity: sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + dependencies: + '@types/estree': 1.0.6 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.34.8 + '@rollup/rollup-android-arm64': 4.34.8 + '@rollup/rollup-darwin-arm64': 4.34.8 + '@rollup/rollup-darwin-x64': 4.34.8 + '@rollup/rollup-freebsd-arm64': 4.34.8 + '@rollup/rollup-freebsd-x64': 4.34.8 + '@rollup/rollup-linux-arm-gnueabihf': 4.34.8 + '@rollup/rollup-linux-arm-musleabihf': 4.34.8 + '@rollup/rollup-linux-arm64-gnu': 4.34.8 + '@rollup/rollup-linux-arm64-musl': 4.34.8 + '@rollup/rollup-linux-loongarch64-gnu': 4.34.8 + '@rollup/rollup-linux-powerpc64le-gnu': 4.34.8 + '@rollup/rollup-linux-riscv64-gnu': 4.34.8 + '@rollup/rollup-linux-s390x-gnu': 4.34.8 + '@rollup/rollup-linux-x64-gnu': 4.34.8 + '@rollup/rollup-linux-x64-musl': 4.34.8 + '@rollup/rollup-win32-arm64-msvc': 4.34.8 + '@rollup/rollup-win32-ia32-msvc': 4.34.8 + '@rollup/rollup-win32-x64-msvc': 4.34.8 + fsevents: 2.3.3 + dev: true + /run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: queue-microtask: 1.2.3 dev: true + /sade@1.8.1: + resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} + engines: {node: '>=6'} + dependencies: + mri: 1.2.0 + dev: true + /scheduler@0.23.0: resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} dependencies: loose-envify: 1.4.0 dev: false + /seedrandom@3.0.5: + resolution: {integrity: sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==} + dev: false + + /semver@7.7.1: + resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} + engines: {node: '>=10'} + hasBin: true + dev: true + + /set-cookie-parser@2.7.1: + resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} + dev: true + /shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -1389,10 +3304,24 @@ packages: engines: {node: '>=14'} dev: true + /sirv@3.0.1: + resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==} + engines: {node: '>=18'} + dependencies: + '@polka/url': 1.0.0-next.28 + mrmime: 2.0.1 + totalist: 3.0.1 + dev: true + /source-map-js@1.0.2: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} + /source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + dev: true + /split-on-first@3.0.0: resolution: {integrity: sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA==} engines: {node: '>=12'} @@ -1440,6 +3369,11 @@ packages: ansi-regex: 6.0.1 dev: true + /strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + dev: true + /styled-jsx@5.1.1(react@18.2.0): resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} engines: {node: '>= 12.0.0'} @@ -1471,11 +3405,74 @@ packages: ts-interface-checker: 0.1.13 dev: true + /supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + /supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} dev: true + /svelte-check@4.1.4(svelte@5.20.1)(typescript@5.3.3): + resolution: {integrity: sha512-v0j7yLbT29MezzaQJPEDwksybTE2Ups9rUxEXy92T06TiA0cbqcO8wAOwNUVkFW6B0hsYHA+oAX3BS8b/2oHtw==} + engines: {node: '>= 18.0.0'} + hasBin: true + peerDependencies: + svelte: ^4.0.0 || ^5.0.0-next.0 + typescript: '>=5.0.0' + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + chokidar: 4.0.3 + fdir: 6.4.3 + picocolors: 1.0.0 + sade: 1.8.1 + svelte: 5.20.1 + typescript: 5.3.3 + transitivePeerDependencies: + - picomatch + dev: true + + /svelte-eslint-parser@0.43.0(svelte@5.20.1): + resolution: {integrity: sha512-GpU52uPKKcVnh8tKN5P4UZpJ/fUDndmq7wfsvoVXsyP+aY0anol7Yqo01fyrlaWGMFfm4av5DyrjlaXdLRJvGA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + svelte: + optional: true + dependencies: + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + postcss: 8.5.2 + postcss-scss: 4.0.9(postcss@8.5.2) + svelte: 5.20.1 + dev: true + + /svelte@5.20.1: + resolution: {integrity: sha512-aCARru2WTdzJl55Ws8SK27+kvQwd8tijl4kY7NoDUXUHtTHhxMa8Lf6QNZKmU7cuPu3jjFloDO1j5HgYJNIIWg==} + engines: {node: '>=18'} + dependencies: + '@ampproject/remapping': 2.3.0 + '@jridgewell/sourcemap-codec': 1.5.0 + '@types/estree': 1.0.6 + acorn: 8.12.1 + acorn-typescript: 1.4.13(acorn@8.12.1) + aria-query: 5.3.2 + axobject-query: 4.1.0 + clsx: 2.1.1 + esm-env: 1.2.2 + esrap: 1.4.5 + is-reference: 3.0.3 + locate-character: 3.0.0 + magic-string: 0.30.17 + zimmerframe: 1.1.2 + dev: true + /tailwindcss@3.4.1: resolution: {integrity: sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==} engines: {node: '>=14.0.0'} @@ -1507,6 +3504,15 @@ packages: - ts-node dev: true + /tailwindcss@4.0.6: + resolution: {integrity: sha512-mysewHYJKaXgNOW6pp5xon/emCsfAMnO8WMaGKZZ35fomnR/T5gYnRg2/yRTTrtXiEl1tiVkeRt0eMO6HxEZqw==} + dev: true + + /tapable@2.2.1: + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} + dev: true + /tar@6.2.1: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} @@ -1532,6 +3538,10 @@ packages: any-promise: 1.3.0 dev: true + /tiny-emitter@2.1.0: + resolution: {integrity: sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==} + dev: false + /to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -1539,6 +3549,20 @@ packages: is-number: 7.0.0 dev: true + /totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + dev: true + + /ts-api-utils@2.0.1(typescript@5.3.3): + resolution: {integrity: sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + dependencies: + typescript: 5.3.3 + dev: true + /ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} dev: true @@ -1547,11 +3571,39 @@ packages: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} dev: false + /type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + dev: true + /type-detect@4.0.8: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} dev: true + /typed-function@4.2.1: + resolution: {integrity: sha512-EGjWssW7Tsk4DGfE+5yluuljS1OGYWiI1J6e8puZz9nTMM51Oug8CD5Zo4gWMsOhq5BI+1bF+rWTm4Vbj3ivRA==} + engines: {node: '>= 18'} + dev: false + + /typescript-eslint@8.24.1(eslint@9.20.1)(typescript@5.3.3): + resolution: {integrity: sha512-cw3rEdzDqBs70TIcb0Gdzbt6h11BSs2pS0yaq7hDWDBtCCSei1pPSUXE9qUdQ/Wm9NgFg8mKtMt1b8fTHIl1jA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' + dependencies: + '@typescript-eslint/eslint-plugin': 8.24.1(@typescript-eslint/parser@8.24.1)(eslint@9.20.1)(typescript@5.3.3) + '@typescript-eslint/parser': 8.24.1(eslint@9.20.1)(typescript@5.3.3) + '@typescript-eslint/utils': 8.24.1(eslint@9.20.1)(typescript@5.3.3) + eslint: 9.20.1 + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + /typescript@5.3.3: resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} engines: {node: '>=14.17'} @@ -1586,6 +3638,12 @@ packages: picocolors: 1.0.0 dev: true + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.3.1 + dev: true + /url-join@5.0.0: resolution: {integrity: sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -1595,11 +3653,96 @@ packages: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: true + /uuid@10.0.0: + resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} + hasBin: true + dev: true + /validate-npm-package-name@5.0.1: resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dev: true + /vite-plugin-top-level-await@1.5.0(vite@6.1.0): + resolution: {integrity: sha512-r/DtuvHrSqUVk23XpG2cl8gjt1aATMG5cjExXL1BUTcSNab6CzkcPua9BPEc9fuTP5UpwClCxUe3+dNGL0yrgQ==} + peerDependencies: + vite: '>=2.8' + dependencies: + '@rollup/plugin-virtual': 3.0.2 + '@swc/core': 1.10.17 + uuid: 10.0.0 + vite: 6.1.0 + transitivePeerDependencies: + - '@swc/helpers' + - rollup + dev: true + + /vite-plugin-wasm@3.4.1(vite@6.1.0): + resolution: {integrity: sha512-ja3nSo2UCkVeitltJGkS3pfQHAanHv/DqGatdI39ja6McgABlpsZ5hVgl6wuR8Qx5etY3T5qgDQhOWzc5RReZA==} + peerDependencies: + vite: ^2 || ^3 || ^4 || ^5 || ^6 + dependencies: + vite: 6.1.0 + dev: true + + /vite@6.1.0: + resolution: {integrity: sha512-RjjMipCKVoR4hVfPY6GQTgveinjNuyLw+qruksLDvA5ktI1150VmcMBKmQaEWJhg/j6Uaf6dNCNA0AfdzUb/hQ==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + dependencies: + esbuild: 0.24.2 + postcss: 8.5.2 + rollup: 4.34.8 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /vitefu@1.0.5(vite@6.1.0): + resolution: {integrity: sha512-h4Vflt9gxODPFNGPwp4zAMZRpZR7eslzwH2c5hn5kNZ5rhnKyRJ50U+yGCdc2IRaBs8O4haIgLNGrV5CrpMsCA==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 + peerDependenciesMeta: + vite: + optional: true + dependencies: + vite: 6.1.0 + dev: true + /wasm-pack@0.12.1: resolution: {integrity: sha512-dIyKWUumPFsGohdndZjDXRFaokUT/kQS+SavbbiXVAvA/eN4riX5QNdB6AhXQx37zNxluxQkuixZUgJ8adKjOg==} hasBin: true @@ -1617,6 +3760,11 @@ packages: isexe: 2.0.0 dev: true + /word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + dev: true + /wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -1643,12 +3791,26 @@ packages: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: true + /yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + dev: true + /yaml@2.4.0: resolution: {integrity: sha512-j9iR8g+/t0lArF4V6NE/QCfT+CO7iLqrXAHZbJdo+LfjqP1vR8Fg5bSiaq6Q2lOD1AUEVrEVIgABvBFYojJVYQ==} engines: {node: '>= 14'} hasBin: true dev: true + /yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true + + /zimmerframe@1.1.2: + resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==} + dev: true + /zod-package-json@1.0.3: resolution: {integrity: sha512-Mb6GzuRyUEl8X+6V6xzHbd4XV0au/4gOYrYP+CAfHL32uPmGswES+v2YqonZiW1NZWVA3jkssCKSU2knonm/aQ==} engines: {node: '>=20'} From d2202d37c3867acd3885a9600a3fa066e284270c Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Tue, 18 Feb 2025 04:31:23 -0700 Subject: [PATCH 025/590] Formatting, I can only assume --- examples/ratchet-train-toy/src/routes/+page.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/ratchet-train-toy/src/routes/+page.svelte b/examples/ratchet-train-toy/src/routes/+page.svelte index 0ffd8283..93638459 100644 --- a/examples/ratchet-train-toy/src/routes/+page.svelte +++ b/examples/ratchet-train-toy/src/routes/+page.svelte @@ -630,7 +630,7 @@ plugins: { legend: { reverse: true - }, + } // title: { // display: false, // // text: 'Training Loss', From e96428a1044271ed7e37b7857ec6559e04342576 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Tue, 18 Feb 2025 04:48:14 -0700 Subject: [PATCH 026/590] Turn val loss ticker into val accuracy --- examples/ratchet-train-toy/src/routes/+page.svelte | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/ratchet-train-toy/src/routes/+page.svelte b/examples/ratchet-train-toy/src/routes/+page.svelte index 93638459..388ba100 100644 --- a/examples/ratchet-train-toy/src/routes/+page.svelte +++ b/examples/ratchet-train-toy/src/routes/+page.svelte @@ -9,6 +9,7 @@ import { taskMetadata } from '../tasks'; let loss = 0; + let evalAccuracy = 0; let initialTotalLoss: number | null = null; let worker: Worker; let chart: Chart; @@ -523,6 +524,7 @@ if (data.accuracy !== null && data.accuracy !== undefined) { const currentAccuracyDataset = accuracyChart.data.datasets[accuracyChart.data.datasets.length - 1]; + evalAccuracy = data.accuracy; // Store both the step number and accuracy value currentAccuracyDataset.data.push({ x: currentStepCount, @@ -841,9 +843,9 @@ >
-
+

train loss: {loss.toFixed(4)}

-

val loss: -.----

+

val accuracy: {evalAccuracy.toFixed(4)}

From 4beb612d4c0e7e58d4daf3d42c490c6edb7c2ef0 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Tue, 18 Feb 2025 04:48:25 -0700 Subject: [PATCH 027/590] Fix alignment of links on mobile --- examples/ratchet-train-toy/src/routes/+page.svelte | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/ratchet-train-toy/src/routes/+page.svelte b/examples/ratchet-train-toy/src/routes/+page.svelte index 388ba100..6f9b537d 100644 --- a/examples/ratchet-train-toy/src/routes/+page.svelte +++ b/examples/ratchet-train-toy/src/routes/+page.svelte @@ -835,10 +835,10 @@

toy transformer

- From b3f6b15c9d536582f7ed18fdb9b5c70e837ff45e Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Tue, 18 Feb 2025 04:48:38 -0700 Subject: [PATCH 028/590] Move sidebar to the left in a responsive way --- examples/ratchet-train-toy/src/routes/+page.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/ratchet-train-toy/src/routes/+page.svelte b/examples/ratchet-train-toy/src/routes/+page.svelte index 6f9b537d..f4b7c1c7 100644 --- a/examples/ratchet-train-toy/src/routes/+page.svelte +++ b/examples/ratchet-train-toy/src/routes/+page.svelte @@ -981,7 +981,7 @@
-
+
{}] {}\n", - Self::format_timestamp(segment.start, false, "."), - Self::format_timestamp(segment.stop, false, "."), - fragment_text.trim().replace("-->", "->") - ) - } - - pub fn as_oai(&self, tokenizer: &WhisperTokenizer) -> String { - self.segments - .iter() - .map(|segment| self.format_single(segment, tokenizer)) - .collect::() - } - - fn format_timestamp(num: f64, always_include_hours: bool, decimal_marker: &str) -> String { - assert!(num >= 0.0, "non-negative timestamp expected"); - let milliseconds: i64 = (num * 1000.0) as i64; - - let hours = div_floor(milliseconds, 3_600_000); - let minutes = div_floor(milliseconds % 3_600_000, 60_000); - let seconds = div_floor(milliseconds % 60_000, 1000); - let milliseconds = milliseconds % 1000; - - let hours_marker = if always_include_hours || hours != 0 { - format!("{:02}:", hours) - } else { - String::new() - }; - - format!( - "{}{:02}:{:02}{}{:03}", - hours_marker, minutes, seconds, decimal_marker, milliseconds - ) - } -} - -#[derive(Debug, Serialize, Deserialize, derive_new::new)] -pub struct Segment { - pub start: f64, - pub stop: f64, - pub tokens: Vec, - pub last: bool, -} - -impl Segment { - pub fn from_tokens( - tokenizer: &WhisperTokenizer, - sliced_tokens: &[i32], - offset: f64, - last: bool, - ) -> Self { - let input_stride = N_FRAMES / N_AUDIO_CTX; // mel frames per output token: 2 - let time_precision: f64 = input_stride as f64 * (HOP_LENGTH as f64) / (SAMPLE_RATE as f64); // time per output token: 0.02 (seconds) - - let start_timestamp_pos = sliced_tokens[0] - tokenizer.timestamp_begin(); - let end_timestamp_pos = - sliced_tokens[sliced_tokens.len() - 1] - tokenizer.timestamp_begin(); - - let segment_tokens = sliced_tokens.iter().map(|x| *x as u32).collect::>(); - - let st = offset + (start_timestamp_pos as f64 * time_precision); - let et = offset + (end_timestamp_pos as f64 * time_precision); - let st = (st * 100.).round() / 100.; - let et = (et * 100.).round() / 100.; - Segment::new(st, et, segment_tokens, last) - } -} - -#[cfg_attr( - target_arch = "wasm32", - wasm_bindgen(getter_with_clone, js_name = Segment), - derive(serde::Serialize, serde::Deserialize) -)] -#[derive(Debug, derive_new::new)] -pub struct StreamedSegment { - pub start: f64, - pub stop: f64, - pub text: String, - pub last: bool, -} - -impl std::fmt::Display for StreamedSegment { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!( - f, - "[{} --> {}] {}", - TranscriptionResult::format_timestamp(self.start, false, "."), - TranscriptionResult::format_timestamp(self.stop, false, "."), - self.text.trim().replace("-->", "->") - ) - } -} - -#[cfg_attr(target_arch = "wasm32", wasm_bindgen)] -impl StreamedSegment { - pub fn start(&self) -> f64 { - self.start - } - - pub fn stop(&self) -> f64 { - self.stop - } - - pub fn text(&self) -> String { - self.text.clone() - } - - pub fn last(&self) -> bool { - self.last - } - - pub(crate) fn from_tokens( - tokenizer: &WhisperTokenizer, - sliced_tokens: &[i32], - offset: f64, - last: bool, - ) -> Self { - let segment = Segment::from_tokens(tokenizer, sliced_tokens, offset, last); - let segment_tokens = segment - .tokens - .into_iter() - .filter(|t| *t < tokenizer.timestamp_begin() as _) - .collect::>(); - let segment_text = tokenizer.decode(segment_tokens.as_slice(), true).unwrap(); - StreamedSegment::new(segment.start, segment.stop, segment_text, last) - } -} diff --git a/crates/ratchet-models/tests/whisper.rs b/crates/ratchet-models/tests/whisper.rs deleted file mode 100644 index f34ced21..00000000 --- a/crates/ratchet-models/tests/whisper.rs +++ /dev/null @@ -1,218 +0,0 @@ -#![cfg(target_arch = "wasm32")] -wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); - -use ndarray::{s, Axis}; -use ndarray_stats::QuantileExt; -use ratchet::{shape, Device, DeviceRequest, Tensor}; -use ratchet_hub::{ApiBuilder, RepoType}; -use ratchet_loader::gguf::gguf; -use ratchet_models::whisper::{Config, Whisper, WhisperDecoder, WhisperEncoder}; -use ratchet_nn::Module; -use std::path::PathBuf; -use wasm_bindgen::prelude::*; -use wasm_bindgen_test::*; - -fn log_init() { - console_error_panic_hook::set_once(); - log::set_max_level(log::LevelFilter::Off); - console_log::init_with_level(log::Level::Debug).unwrap(); -} - -/* -#[wasm_bindgen_test] -async fn distil_large_v3_encoder() -> Result<(), JsValue> { - log_init(); - let model_repo = - ApiBuilder::from_hf("FL33TW00D-HF/distil-whisper-large-v3", RepoType::Model).build(); - let model_data = model_repo.get("distil-large-v3_q8_0.gguf").await?; - let config_data = model_repo.get("config.json").await?; - - let ground_repo = ApiBuilder::from_hf("FL33TW00D-HF/ratchet-util", RepoType::Dataset).build(); - let input_npy = ground_repo.get("distil_large_v3_q80_mm0_input.npy").await?; - let ground_npy = ground_repo.get("distil_large_v3_q80_mm0_hs.npy").await?; - - let mut reader = std::io::BufReader::new(std::io::Cursor::new(model_data.to_vec())); - let header = gguf::Header::read(&mut reader).unwrap(); - let config: Config = serde_json::from_slice(&config_data.to_vec()).unwrap(); - - let device = Device::request_device(DeviceRequest::GPU).await.unwrap(); - - let input_data = &input_npy.to_vec(); - let input = Tensor::from_npy_bytes::(input_data, &device).unwrap(); - let ground = Tensor::from_npy_bytes::(&ground_npy.to_vec(), &Device::CPU).unwrap(); - - let encoder = WhisperEncoder::load(&header, &config, &mut reader, &device).unwrap(); - let result = encoder - .schedule(input) - .unwrap() - .full() - .unwrap() - .resolve() - .unwrap(); - let ours = result.to(&Device::CPU).await.unwrap(); - ground.all_close(&ours, 1e-3, 1e-3).unwrap(); - Ok(()) -}*/ - -/* -#[wasm_bindgen_test] -async fn distil_large_v3_decoder() -> Result<(), JsValue> { - log_init(); - let model_repo = - ApiBuilder::from_hf("FL33TW00D-HF/distil-whisper-large-v3", RepoType::Model).build(); - let model_data = model_repo.get("distil-large-v3_q8_0.gguf").await?; - let config_data = model_repo.get("config.json").await?; - - let ground_repo = ApiBuilder::from_hf("FL33TW00D-HF/ratchet-util", RepoType::Dataset).build(); - let hs_data = ground_repo.get("distil_large_v3_q80_mm0_hs.npy").await?; - - let mut reader = std::io::BufReader::new(std::io::Cursor::new(model_data.to_vec())); - let header = gguf::Header::read(&mut reader).unwrap(); - let config: Config = serde_json::from_slice(&config_data.to_vec()).unwrap(); - - let device = Device::request_device(DeviceRequest::GPU).await.unwrap(); - let audio_ctx = Tensor::from_npy_bytes::(&hs_data.to_vec(), &device) - .unwrap() - .half() - .unwrap() - .resolve() - .unwrap(); - log::debug!("Audio Context: {:?}", audio_ctx); - let mut decoder = WhisperDecoder::load(&header, &config, &mut reader, &device).unwrap(); - - let mut tokens = vec![50258, 50259, 50360]; - let mut all_tokens = tokens.clone(); - let mut all_logits = vec![]; - while tokens[tokens.len() - 1] != 50257 { - let token_t = Tensor::from_data(tokens.clone(), shape![1, tokens.len()], device.clone()); - let result = decoder - .schedule([audio_ctx.clone(), token_t]) - .unwrap() - .resolve_debug() - .unwrap(); - - let our_logits = result.to(&Device::CPU).await.unwrap(); - all_logits.push(our_logits.clone()); - let nd_logits = our_logits.to_ndarray_view::(); - log::info!("Logits: {:?}", nd_logits); - - let sliced = nd_logits.slice(s![.., -1.., ..51866]).remove_axis(Axis(1)); - decoder.cache_mut().update(tokens.len()); - - tokens = sliced - .map_axis(Axis(1), |row| row.argmax_skipnan().unwrap()) - .iter() - .map(|&x| x as i32) - .collect::>(); - println!("Token: {:?}", tokens); - panic!(); - all_tokens.extend(tokens.clone()); - } - - let ground_tokens = vec![ - 50258, 50259, 50360, 50365, 639, 307, 264, 4532, 3479, 587, 11, 15578, 264, 881, 2062, 847, - 11, 34674, 5932, 30340, 295, 3123, 4397, 608, 1652, 13, 50517, 50530, 6947, 472, 575, - 12023, 4365, 11, 20899, 11, 10445, 11, 18356, 11, 4225, 4782, 11, 50624, 50626, 1804, 4651, - 3123, 4397, 34922, 8963, 862, 6352, 13, 50695, 50701, 821, 311, 257, 3804, 5214, 11, 2610, - 5214, 11, 6383, 11, 2643, 5214, 11, 293, 544, 13, 50797, 50807, 10246, 8963, 2436, 2965, - 281, 747, 604, 1081, 13, 50875, 50881, 400, 456, 366, 867, 34674, 862, 365, 11, 293, 1184, - 472, 1487, 365, 1080, 1065, 2121, 11377, 11, 51002, 51007, 4532, 3479, 5864, 293, 1019, 11, - 5456, 4122, 300, 30686, 25038, 1286, 13, 51120, 51135, 30062, 264, 6582, 29814, 412, 264, - 10155, 11, 1849, 1426, 11, 587, 11, 264, 3874, 34544, 412, 264, 7267, 3096, 13, 51243, - 51246, 18463, 428, 1032, 412, 264, 1032, 5675, 13, 51287, 51290, 30062, 264, 16629, 7283, - 13, 51320, 51328, 400, 613, 862, 6352, 3318, 1214, 281, 1254, 257, 3123, 4397, 34922, 8963, - 1081, 6352, 11, 370, 27985, 11, 51504, 51504, 370, 6239, 11, 370, 44078, 1688, 356, 9942, - 11, 291, 603, 528, 281, 8963, 552, 439, 13, 51623, 51635, 25642, 12089, 1652, 366, 1027, - 14759, 490, 7336, 836, 65, 13, 51743, 51743, 440, 4356, 436, 366, 11, 264, 1101, 436, 366, - 13, 51834, - ]; - assert_eq!(all_tokens, ground_tokens); - Ok(()) -}*/ - -/* -#[wasm_bindgen_test] -async fn tiny_encoder() -> Result<(), JsValue> { - log_init(); - let model_repo = ApiBuilder::from_hf("FL33TW00D-HF/whisper-tiny", RepoType::Model).build(); - let model_data = model_repo.get("tiny_f32.gguf").await?; - let config_data = model_repo.get("config.json").await?; - - let ground_repo = ApiBuilder::from_hf("FL33TW00D-HF/ratchet-util", RepoType::Dataset).build(); - let input_npy = ground_repo.get("jfk_tiny_encoder_input.npy").await?; - let ground_npy = ground_repo.get("jfk_tiny_encoder_hs.npy").await?; - - let mut reader = std::io::BufReader::new(std::io::Cursor::new(model_data.to_vec())); - let header = gguf::Header::read(&mut reader).unwrap(); - let config: Config = serde_json::from_slice(&config_data.to_vec()).unwrap(); - - let device = Device::request_device(DeviceRequest::GPU).await.unwrap(); - - let input_data = &input_npy.to_vec(); - let input = Tensor::from_npy_bytes::(input_data, &device).unwrap(); - let ground = Tensor::from_npy_bytes::(&ground_npy.to_vec(), &Device::CPU).unwrap(); - - let encoder = WhisperEncoder::load(&header, &config, &mut reader, &device).unwrap(); - let result = encoder.schedule(input).unwrap().resolve().unwrap(); - let ours = result.to(&Device::CPU).await.unwrap(); - ground.all_close(&ours, 1e-3, 1e-3).unwrap(); - Ok(()) -} -*/ - -#[wasm_bindgen_test] -async fn tiny_decoder() -> Result<(), JsValue> { - log_init(); - let model_repo = ApiBuilder::from_hf("FL33TW00D-HF/whisper-tiny", RepoType::Model).build(); - let model_data = model_repo.get("tiny_f32.gguf").await?; - let config_data = model_repo.get("config.json").await?; - - let ground_repo = ApiBuilder::from_hf("FL33TW00D-HF/ratchet-util", RepoType::Dataset).build(); - let hs_data = ground_repo.get("jfk_tiny_encoder_hs.npy").await?; - - let mut reader = std::io::BufReader::new(std::io::Cursor::new(model_data.to_vec())); - let header = gguf::Header::read(&mut reader).unwrap(); - let config: Config = serde_json::from_slice(&config_data.to_vec()).unwrap(); - - let device = Device::request_device(DeviceRequest::GPU).await.unwrap(); - - let audio_ctx = Tensor::from_npy_bytes::(&hs_data.to_vec(), &device).unwrap(); - let mut decoder = WhisperDecoder::load(&header, &config, &mut reader, &device).unwrap(); - - let mut tokens = vec![50258, 50259, 50359]; - let mut all_tokens = tokens.clone(); - let mut all_logits = vec![]; - let vocab_size = 51865; - while tokens[tokens.len() - 1] != 50257 { - let token_t = Tensor::from_data(tokens.clone(), shape![1, tokens.len()], device.clone()); - let result = decoder - .schedule([audio_ctx.clone(), token_t]) - .unwrap() - .resolve() - .unwrap(); - - let our_logits = result.to(&Device::CPU).await.unwrap(); - all_logits.push(our_logits.clone()); - let nd_logits = our_logits.to_ndarray_view::(); - log::debug!("ND LOGITS: {:?}", nd_logits); - let sliced = nd_logits - .slice(s![.., -1.., ..vocab_size]) - .remove_axis(Axis(1)); - decoder.cache_mut().update(tokens.len()); - - tokens = sliced - .map_axis(Axis(1), |row| row.argmax_skipnan().unwrap()) - .iter() - .map(|&x| x as i32) - .collect::>(); - println!("Token: {:?}", tokens); - all_tokens.extend(tokens.clone()); - } - - let ground_tokens = vec![ - 50258, 50259, 50359, 50363, 400, 370, 452, 7177, 6280, 1029, 406, 437, 428, 1941, 393, 360, - 337, 291, 1029, 437, 291, 393, 360, 337, 428, 1941, 13, 50257, - ]; - assert_eq!(all_tokens, ground_tokens); - Ok(()) -} diff --git a/crates/ratchet-web/src/model.rs b/crates/ratchet-web/src/model.rs index e08101f3..e0df45da 100644 --- a/crates/ratchet-web/src/model.rs +++ b/crates/ratchet-web/src/model.rs @@ -8,14 +8,12 @@ use ratchet_models::phi2; use ratchet_models::phi2::Phi2; use ratchet_models::phi3::{self, Phi3}; use ratchet_models::registry::{AvailableModels, PhiVariants, Quantization}; -use ratchet_models::whisper::{transcribe::transcribe, transcript::StreamedSegment, Whisper}; use ratchet_models::TensorMap; use tokenizers::Tokenizer; use wasm_bindgen::prelude::*; #[derive(Debug)] pub enum WebModel { - Whisper(Whisper), Phi2(Phi2), Phi3(Phi3), Moondream(Moondream), @@ -24,25 +22,6 @@ pub enum WebModel { impl WebModel { pub async fn run(&mut self, input: JsValue) -> Result { match self { - WebModel::Whisper(model) => { - let input: WhisperInputs = serde_wasm_bindgen::from_value(input)?; - let options = serde_wasm_bindgen::from_value(input.decode_options)?; - - let callback = if !input.callback.is_null() { - let rs_callback = |decoded: StreamedSegment| { - let js_decoded = serde_wasm_bindgen::to_value(&decoded).unwrap(); - let _ = input.callback.call1(&JsValue::NULL, &js_decoded); - }; - Some(rs_callback) - } else { - None - }; - - let result = transcribe(model, input.audio, options, callback) - .await - .unwrap(); - serde_wasm_bindgen::to_value(&result).map_err(|e| e.into()) - } WebModel::Phi2(model) => { let input: PhiInputs = serde_wasm_bindgen::from_value(input)?; let rs_callback = |output: String| { @@ -104,10 +83,6 @@ impl WebModel { ) -> Result { let header = serde_wasm_bindgen::from_value::
(model_record.header).unwrap(); match model_record.model { - AvailableModels::Whisper(variant) => { - let model = Whisper::from_web(header, tensor_map, variant).await?; - Ok(WebModel::Whisper(model)) - } AvailableModels::Phi(variant) => match variant { PhiVariants::Phi2 => { let model = Phi2::from_web(header, tensor_map).await?; @@ -127,15 +102,6 @@ impl WebModel { } } -#[derive(serde::Serialize, serde::Deserialize)] -pub struct WhisperInputs { - pub audio: Vec, - #[serde(with = "serde_wasm_bindgen::preserve")] - pub decode_options: JsValue, - #[serde(with = "serde_wasm_bindgen::preserve")] - pub callback: js_sys::Function, -} - #[derive(serde::Serialize, serde::Deserialize)] pub struct PhiInputs { pub prompt: String, @@ -285,124 +251,4 @@ impl Model { Ok(()) } -} - -#[cfg(all(test, target_arch = "wasm32"))] -mod tests { - use super::*; - use ratchet_hub::{ApiBuilder, RepoType}; - use ratchet_models::registry::PhiVariants; - use ratchet_models::registry::WhisperVariants; - use ratchet_models::whisper::options::DecodingOptionsBuilder; - use tokenizers::Tokenizer; - use wasm_bindgen_test::*; - - wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); - - fn log_init() { - console_error_panic_hook::set_once(); - let logger = fern::Dispatch::new() - .format(|out, message, record| { - out.finish(format_args!( - "{}[{}][{}] {}", - chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S]"), - record.target(), - record.level(), - message - )) - }) - .level_for("tokenizers", log::LevelFilter::Off) - .level(log::LevelFilter::Warn) - .chain(fern::Output::call(console_log::log)) - .apply(); - match logger { - Ok(_) => log::info!("Logging initialized."), - Err(error) => eprintln!("Error initializing logging: {:?}", error), - } - } - - fn load_sample(bytes: &[u8]) -> Vec { - let mut reader = hound::WavReader::new(std::io::Cursor::new(bytes)).unwrap(); - reader - .samples::() - .map(|x| x.unwrap() as f32 / 32768.0) - .collect::>() - } - - #[wasm_bindgen_test] - async fn whisper_browser() -> Result<(), JsValue> { - log_init(); - let download_cb: Closure = Closure::new(|p| { - log::info!("Provided closure got progress: {}", p); - }); - let js_cb: &js_sys::Function = download_cb.as_ref().unchecked_ref(); - - let mut model = Model::load( - AvailableModels::Whisper(WhisperVariants::Base), - Quantization::F16, - js_cb, - ) - .await - .unwrap(); - - let data_repo = ApiBuilder::from_hf("FL33TW00D-HF/ratchet-util", RepoType::Dataset).build(); - let audio_bytes = data_repo.get("mm0.wav").await?; - let sample = load_sample(&audio_bytes.to_vec()); - - let decode_options = DecodingOptionsBuilder::default().build(); - - let cb: Closure = Closure::new(|s| { - log::info!("GENERATED SEGMENT: {:?}", s); - }); - let js_cb: &js_sys::Function = cb.as_ref().unchecked_ref(); - - let input = WhisperInputs { - audio: sample, - decode_options, - callback: js_cb.clone(), - }; - let input = serde_wasm_bindgen::to_value(&input).unwrap(); - let result = model.run(input).await.unwrap(); - log::warn!("Result: {:?}", result); - Ok(()) - } - - #[wasm_bindgen_test] - async fn whisper_browser_custom() -> Result<(), JsValue> { - log_init(); - let download_cb: Closure = Closure::new(|p| { - log::info!("Provided closure got progress: {}", p); - }); - let js_cb: &js_sys::Function = download_cb.as_ref().unchecked_ref(); - - let mut model = Model::load_custom( - "https://huggingface.co/FL33TW00D-HF/whisper-base/resolve/main".to_string(), - AvailableModels::Whisper(WhisperVariants::Base), - Quantization::F16, - js_cb, - ) - .await - .unwrap(); - - let data_repo = ApiBuilder::from_hf("FL33TW00D-HF/ratchet-util", RepoType::Dataset).build(); - let audio_bytes = data_repo.get("mm0.wav").await?; - let sample = load_sample(&audio_bytes.to_vec()); - - let decode_options = DecodingOptionsBuilder::default().build(); - - let cb: Closure = Closure::new(|s| { - log::info!("GENERATED SEGMENT: {:?}", s); - }); - let js_cb: &js_sys::Function = cb.as_ref().unchecked_ref(); - - let input = WhisperInputs { - audio: sample, - decode_options, - callback: js_cb.clone(), - }; - let input = serde_wasm_bindgen::to_value(&input).unwrap(); - let result = model.run(input).await.unwrap(); - log::warn!("Result: {:?}", result); - Ok(()) - } -} +} \ No newline at end of file diff --git a/examples/ratchet-whisper/.gitignore b/examples/ratchet-whisper/.gitignore deleted file mode 100644 index fd3dbb57..00000000 --- a/examples/ratchet-whisper/.gitignore +++ /dev/null @@ -1,36 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js -.yarn/install-state.gz - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# local env files -.env*.local - -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts diff --git a/examples/ratchet-whisper/README.md b/examples/ratchet-whisper/README.md deleted file mode 100644 index c4033664..00000000 --- a/examples/ratchet-whisper/README.md +++ /dev/null @@ -1,36 +0,0 @@ -This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). - -## Getting Started - -First, run the development server: - -```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev -``` - -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. - -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. - -This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. - -## Learn More - -To learn more about Next.js, take a look at the following resources: - -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. - -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! - -## Deploy on Vercel - -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. - -Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/examples/ratchet-whisper/next.config.mjs b/examples/ratchet-whisper/next.config.mjs deleted file mode 100644 index 15eff7c7..00000000 --- a/examples/ratchet-whisper/next.config.mjs +++ /dev/null @@ -1,6 +0,0 @@ -/** @type {import('next').NextConfig} */ -const nextConfig = { - output: 'export' -}; - -export default nextConfig; diff --git a/examples/ratchet-whisper/package.json b/examples/ratchet-whisper/package.json deleted file mode 100644 index 52597369..00000000 --- a/examples/ratchet-whisper/package.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "ratchet-whisper", - "version": "0.1.0", - "private": true, - "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "lint": "next lint" - }, - "dependencies": { - "@ffmpeg/ffmpeg": "0.12.6", - "@ffmpeg/util": "^0.12.1", - "@ratchet-ml/ratchet-web": "link:../../target/pkg/ratchet-web", - "fix-webm-duration": "^1.0.5", - "next": "14.1.0", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-hot-toast": "^2.4.1", - "react-responsive-modal": "^6.4.2" - }, - "devDependencies": { - "@types/node": "^20.11.24", - "@types/react": "^18.2.61", - "@types/react-dom": "^18.2.19", - "autoprefixer": "^10.4.18", - "postcss": "^8.4.35", - "tailwindcss": "^3.4.1", - "typescript": "^5.3.3" - } -} diff --git a/examples/ratchet-whisper/postcss.config.js b/examples/ratchet-whisper/postcss.config.js deleted file mode 100644 index 33ad091d..00000000 --- a/examples/ratchet-whisper/postcss.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -} diff --git a/examples/ratchet-whisper/src/app/audio.ts b/examples/ratchet-whisper/src/app/audio.ts deleted file mode 100644 index 0ece4b79..00000000 --- a/examples/ratchet-whisper/src/app/audio.ts +++ /dev/null @@ -1,88 +0,0 @@ -import fixWebmDuration from "fix-webm-duration"; - -export interface Recording { - blob: Blob; - buffer: ArrayBuffer; -} - -export class MicRecorder { - private currentStart: number | null = null; - private currentStream: MediaStream | null = null; - private inner: MediaRecorder | null = null; - private audioChunks: Blob[] = []; - private static readonly supportedMimes = [ - "audio/webm", // Chrome - "audio/ogg", // Firefox - ]; - - private constructor(recorder: MediaRecorder) { - this.inner = recorder; - } - - public static async start(): Promise { - if (!navigator.mediaDevices) { - throw new Error("Media device not available"); - } - - const stream = await navigator.mediaDevices.getUserMedia({ - audio: true, - }); - let mimeType = MicRecorder.supportedMimes.find((mime: string) => - MediaRecorder.isTypeSupported(mime) - ); - const inner = new MediaRecorder(stream, { - mimeType - }); - const recorder = new MicRecorder(inner); - recorder.currentStream = stream; - - inner.addEventListener("dataavailable", (event) => { - recorder.audioChunks.push(event.data); - }); - inner.start(); - recorder.currentStart = Date.now(); - return recorder; - } - - public isRecording(): boolean { - return this.inner !== null && this.inner.state === "recording"; - } - - public async stop(): Promise { - if (!this.inner) { - throw new Error("Please start the recorder first"); - } - - const promise: Promise = new Promise( - (resolve) => { - this.inner!.addEventListener("stop", async () => { - const duration = Date.now() - this.currentStart!; - let blob = new Blob(this.audioChunks, { - type: this.inner!.mimeType, - }); - - if (this.inner!.mimeType.includes("webm")) { - blob = await fixWebmDuration(blob, duration, { - logger: false, - }); - } - - const buffer = await blob.arrayBuffer(); - - resolve({ - blob, - buffer, - }); - }); - this.inner!.stop(); - this.currentStream!.getTracks().forEach((track) => - track.stop() - ); - } - ); - return promise; - } -} - -export default MicRecorder; - diff --git a/examples/ratchet-whisper/src/app/components/WebGPUModal.tsx b/examples/ratchet-whisper/src/app/components/WebGPUModal.tsx deleted file mode 100644 index 91ff1134..00000000 --- a/examples/ratchet-whisper/src/app/components/WebGPUModal.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import React, { useState, useEffect } from "react"; -import Modal from "react-responsive-modal"; - -const WebGPUModal = () => { - const [hasWebGPU, setHasWebGPU] = useState(false); - const [isModalOpen, setIsModalOpen] = useState(false); - - useEffect(() => { - //@ts-ignore - if (!navigator.gpu) { - setIsModalOpen(true); - return; - } - setHasWebGPU(true); - }, []); - - const handleModalClose = () => { - setIsModalOpen(false); - }; - - const closeIcon = ( - - - - - - - - - - - - - - - - - - ); - - return ( - <> - {!hasWebGPU ? ( - -
-
-

- Uh oh! It looks like your browser doesn't - support WebGPU. Please try again in supported browser (Chrome 121+). -

-
-
-
- ) : ( - <> - )} - - ); -}; - -export default WebGPUModal; - diff --git a/examples/ratchet-whisper/src/app/components/configModal.tsx b/examples/ratchet-whisper/src/app/components/configModal.tsx deleted file mode 100644 index 0674fcf6..00000000 --- a/examples/ratchet-whisper/src/app/components/configModal.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React, { useState, useEffect } from "react"; -import Modal from "react-responsive-modal"; -import LanguageDropdown from "./languageDropdown"; -import SuppressComponent from "./suppressSelector"; -import TaskComponent from "./taskSelector"; -import { Task } from "@ratchet-ml/ratchet-web"; - -interface ConfigModalProps { - isModalOpen: boolean; - setIsModalOpen: React.Dispatch>; - configOptions: ConfigOptions; - setConfigOptions: React.Dispatch>; -} - -export interface ConfigOptions { - language: string | null; - task: Task; - suppress_non_speech: boolean; -} - -const ConfigModal = (props: ConfigModalProps) => { - const handleModalClose = () => { - props.setIsModalOpen(false); - }; - - return ( - <> - -
-
- - - -
-
-
- - ); -}; - -export default ConfigModal; - diff --git a/examples/ratchet-whisper/src/app/components/languageDropdown.tsx b/examples/ratchet-whisper/src/app/components/languageDropdown.tsx deleted file mode 100644 index 9d67359d..00000000 --- a/examples/ratchet-whisper/src/app/components/languageDropdown.tsx +++ /dev/null @@ -1,191 +0,0 @@ -import React, { useState } from "react"; -import { ConfigOptions } from "./configModal"; -import { DecodingOptionsBuilder } from "@ratchet-ml/ratchet-web"; - -const AvailableLanguages = { - en: "English", - zh: "Chinese", - de: "German", - es: "Spanish", - ru: "Russian", - ko: "Korean", - fr: "French", - ja: "Japanese", - pt: "Portuguese", - tr: "Turkish", - pl: "Polish", - ca: "Catalan", - nl: "Dutch", - ar: "Arabic", - sv: "Swedish", - it: "Italian", - id: "Indonesian", - hi: "Hindi", - fi: "Finnish", - vi: "Vietnamese", - he: "Hebrew", - uk: "Ukrainian", - el: "Greek", - ms: "Malay", - cs: "Czech", - ro: "Romanian", - da: "Danish", - hu: "Hungarian", - ta: "Tamil", - no: "Norwegian", - th: "Thai", - ur: "Urdu", - hr: "Croatian", - bg: "Bulgarian", - lt: "Lithuanian", - la: "Latin", - mi: "Maori", - ml: "Malayalam", - cy: "Welsh", - sk: "Slovak", - te: "Telugu", - fa: "Persian", - lv: "Latvian", - bn: "Bengali", - sr: "Serbian", - az: "Azerbaijani", - sl: "Slovenian", - kn: "Kannada", - et: "Estonian", - mk: "Macedonian", - br: "Breton", - eu: "Basque", - is: "Icelandic", - hy: "Armenian", - ne: "Nepali", - mn: "Mongolian", - bs: "Bosnian", - kk: "Kazakh", - sq: "Albanian", - sw: "Swahili", - gl: "Galician", - mr: "Marathi", - pa: "Punjabi", - si: "Sinhala", - km: "Khmer", - sn: "Shona", - yo: "Yoruba", - so: "Somali", - af: "Afrikaans", - oc: "Occitan", - ka: "Georgian", - be: "Belarusian", - tg: "Tajik", - sd: "Sindhi", - gu: "Gujarati", - am: "Amharic", - yi: "Yiddish", - lo: "Lao", - uz: "Uzbek", - fo: "Faroese", - ht: "Haitian creole", - ps: "Pashto", - tk: "Turkmen", - nn: "Nynorsk", - mt: "Maltese", - sa: "Sanskrit", - lb: "Luxembourgish", - my: "Myanmar", - bo: "Tibetan", - tl: "Tagalog", - mg: "Malagasy", - as: "Assamese", - tt: "Tatar", - haw: "Hawaiian", - ln: "Lingala", - ha: "Hausa", - ba: "Bashkir", - jw: "Javanese", - su: "Sundanese", - yue: "Cantonese", -}; - -interface LanguageDropdownProps { - configOptions: ConfigOptions; - setConfigOptions: React.Dispatch>; -} - -const LanguageDropdown = (props: LanguageDropdownProps) => { - const [open, setOpen] = useState(false); - const [selectedLanguage, setSelectedLanguage] = useState( - props.configOptions.language - ); - - const toggleOpen = () => setOpen((prev) => !prev); - - const selectLanguage = (lang: string) => { - props.setConfigOptions((prev: ConfigOptions) => ({ - ...prev, - language: lang, - })); - setSelectedLanguage(lang); - setOpen(false); - }; - - return ( -
-
- - -
- - {open && ( -
-
- {Object.entries(AvailableLanguages).map( - ([lang, name]) => ( - selectLanguage(lang)} - > - {name} - - ) - )} -
-
- )} -
- ); -}; - -export default LanguageDropdown; - diff --git a/examples/ratchet-whisper/src/app/components/micButton.tsx b/examples/ratchet-whisper/src/app/components/micButton.tsx deleted file mode 100644 index 4b7d4d0a..00000000 --- a/examples/ratchet-whisper/src/app/components/micButton.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { useState } from "react"; -import MicRecorder from "../audio"; - -const SAMPLE_RATE = 16000; - -interface MicButtonProps { - setBlobUrl: (blobUrl: string) => void; - setAudioData: (audioData: Float32Array) => void; - setAudioMetadata: (audioMetadata: AudioMetadata) => void; -} - -export interface AudioMetadata { - file: File; - fromMic: boolean; -} - -const MicButton = (props: MicButtonProps) => { - const [mic, setMic] = useState(null); - const [isRecording, setIsRecording] = useState(false); - - const handleRecord = async () => { - setMic(await MicRecorder.start()); - }; - - const handleStop = async () => { - if (!mic) { - return; - } - let recording = await mic.stop(); - let ctx = new AudioContext({ sampleRate: SAMPLE_RATE }); - let resampled = await ctx.decodeAudioData(recording.buffer); - let ch0 = resampled.getChannelData(0); - props.setAudioData(new Float32Array(ch0.buffer)); - - let blob = recording.blob; - props.setAudioMetadata({ - file: new File([blob], "recording.wav"), - fromMic: true, - }); - props.setBlobUrl(URL.createObjectURL(blob)); - setMic(null); - }; - - const handleClick = async () => { - if (isRecording) { - await handleStop(); - } else { - await handleRecord(); - } - setIsRecording(!isRecording); - }; - - return ( -
- -
- ); -}; - -export default MicButton; diff --git a/examples/ratchet-whisper/src/app/components/modelSelector.tsx b/examples/ratchet-whisper/src/app/components/modelSelector.tsx deleted file mode 100644 index b6584ffd..00000000 --- a/examples/ratchet-whisper/src/app/components/modelSelector.tsx +++ /dev/null @@ -1,117 +0,0 @@ -import { useState } from "react"; -import { AvailableModels, Whisper } from "@ratchet-ml/ratchet-web"; - -interface ModelSelectorProps { - selectedModel: AvailableModels | null; - setSelectedModel: (model: AvailableModels) => void; - loaded: boolean; - progress: number; -} - -const UNITS = [ - "byte", - "kilobyte", - "megabyte", - "gigabyte", -]; -const BYTES_PER_KB = 1000; - -export function humanFileSize(sizeBytes: number | bigint): string { - let size = Math.abs(Number(sizeBytes)); - - let u = 0; - while (size >= BYTES_PER_KB && u < UNITS.length - 1) { - size /= BYTES_PER_KB; - ++u; - } - - return new Intl.NumberFormat([], { - style: "unit", - unit: UNITS[u], - unitDisplay: "short", - maximumFractionDigits: 1, - }).format(size); -} - -export function availableModelToString(model: AvailableModels): string { - if ("Whisper" in model) { - return model.Whisper; - } else if ("Llama" in model) { - return model.Llama; - } - return ""; -} - -const ModelSelector = (props: ModelSelectorProps) => { - const { selectedModel, setSelectedModel, loaded, progress } = props; - const [dropdownOpen, setDropdownOpen] = useState(false); - - const whisper = ["tiny", "base", "small", "medium", "large_v2", "large_v3", "distil_large_v3"] as const; - type WhisperIter = typeof whisper[number]; - - const modelNames = [ - ...whisper, - ]; - - const displayModels = () => { - return modelNames.map((model, idx) => ( -
  • - { - const isWhisper = whisper.includes(model as WhisperIter); - if (isWhisper) { - setSelectedModel({ Whisper: model as Whisper }); - } - setDropdownOpen(false); - }} - > - {model} - -
  • - )); - }; - - return ( - <> - {progress > 0 && !loaded && ( -
    - -
    - )} -
    - -
      - {displayModels()} -
    -
    - - ); -}; - -export default ModelSelector; - diff --git a/examples/ratchet-whisper/src/app/components/progressBar.tsx b/examples/ratchet-whisper/src/app/components/progressBar.tsx deleted file mode 100644 index 31fdd199..00000000 --- a/examples/ratchet-whisper/src/app/components/progressBar.tsx +++ /dev/null @@ -1,19 +0,0 @@ -const ProgressBar = ({ progress }: any) => { - return ( - <> - {progress > 0 && progress < 100 && ( -
    -
    -
    -
    -
    - )} - - ); -}; - -export default ProgressBar; - diff --git a/examples/ratchet-whisper/src/app/components/suppressSelector.tsx b/examples/ratchet-whisper/src/app/components/suppressSelector.tsx deleted file mode 100644 index d030be46..00000000 --- a/examples/ratchet-whisper/src/app/components/suppressSelector.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import React, { useState } from "react"; -import { ConfigOptions } from "./configModal"; - -interface SuppressComponentProps { - configOptions: ConfigOptions; - setConfigOptions: React.Dispatch>; -} - -const SuppressComponent = (props: SuppressComponentProps) => { - const [checkedState, setCheckedState] = useState({ - suppress_non_speech: props.configOptions.suppress_non_speech - }); - - const handleOnChange = (event: React.ChangeEvent) => { - setCheckedState({ - ...checkedState, - [event.target.name]: event.target.checked - }); - - props.setConfigOptions({ - ...props.configOptions, - suppress_non_speech: event.target.checked - }); - }; - - return ( -
    - -
    -
    - - -
    -
    -
    - ); -}; - -export default SuppressComponent; - diff --git a/examples/ratchet-whisper/src/app/components/taskSelector.tsx b/examples/ratchet-whisper/src/app/components/taskSelector.tsx deleted file mode 100644 index ff5afd4e..00000000 --- a/examples/ratchet-whisper/src/app/components/taskSelector.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import React, { useState } from "react"; -import { ConfigOptions } from "./configModal"; -import { Task } from "@ratchet-ml/ratchet-web"; - -interface TaskComponentProps { - configOptions: ConfigOptions; - setConfigOptions: React.Dispatch>; -} - -const TaskComponent = (props: TaskComponentProps) => { - let state = { - translate: props.configOptions.task === Task.Translate, - transcribe: props.configOptions.task === Task.Transcribe, - }; - - const [checkedState, setCheckedState] = useState(state); - - const handleOnChange = (event: React.ChangeEvent) => { - setCheckedState({ - ...checkedState, - [event.target.name]: event.target.checked, - }); - if (event.target.name === "translate") - setCheckedState({ - translate: event.target.checked, - transcribe: !event.target.checked, - }); - if (event.target.name === "transcribe") - setCheckedState({ - translate: !event.target.checked, - transcribe: event.target.checked, - }); - props.setConfigOptions((prev: ConfigOptions) => ({ - ...prev, - task: - event.target.name === "translate" - ? Task.Translate - : Task.Transcribe, - })); - }; - - return ( -
    - -
    -
    - - -
    - -
    - - -
    -
    -
    - ); -}; - -export default TaskComponent; diff --git a/examples/ratchet-whisper/src/app/favicon.ico b/examples/ratchet-whisper/src/app/favicon.ico deleted file mode 100644 index 718d6fea4835ec2d246af9800eddb7ffb276240c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25931 zcmeHv30#a{`}aL_*G&7qml|y<+KVaDM2m#dVr!KsA!#An?kSQM(q<_dDNCpjEux83 zLb9Z^XxbDl(w>%i@8hT6>)&Gu{h#Oeyszu?xtw#Zb1mO{pgX9699l+Qppw7jXaYf~-84xW z)w4x8?=youko|}Vr~(D$UXIbiXABHh`p1?nn8Po~fxRJv}|0e(BPs|G`(TT%kKVJAdg5*Z|x0leQq0 zkdUBvb#>9F()jo|T~kx@OM8$9wzs~t2l;K=woNssA3l6|sx2r3+kdfVW@e^8e*E}v zA1y5{bRi+3Z`uD3{F7LgFJDdvm;nJilkzDku>BwXH(8ItVCXk*-lSJnR?-2UN%hJ){&rlvg`CDTj z)Bzo!3v7Ou#83zEDEFcKt(f1E0~=rqeEbTnMvWR#{+9pg%7G8y>u1OVRUSoox-ovF z2Ydma(;=YuBY(eI|04{hXzZD6_f(v~H;C~y5=DhAC{MMS>2fm~1H_t2$56pc$NH8( z5bH|<)71dV-_oCHIrzrT`2s-5w_+2CM0$95I6X8p^r!gHp+j_gd;9O<1~CEQQGS8) zS9Qh3#p&JM-G8rHekNmKVewU;pJRcTAog68KYo^dRo}(M>36U4Us zfgYWSiHZL3;lpWT=zNAW>Dh#mB!_@Lg%$ms8N-;aPqMn+C2HqZgz&9~Eu z4|Kp<`$q)Uw1R?y(~S>ePdonHxpV1#eSP1B;Ogo+-Pk}6#0GsZZ5!||ev2MGdh}_m z{DeR7?0-1^zVs&`AV6Vt;r3`I`OI_wgs*w=eO%_#7Kepl{B@xiyCANc(l zzIyd4y|c6PXWq9-|KM8(zIk8LPk(>a)zyFWjhT!$HJ$qX1vo@d25W<fvZQ2zUz5WRc(UnFMKHwe1| zWmlB1qdbiA(C0jmnV<}GfbKtmcu^2*P^O?MBLZKt|As~ge8&AAO~2K@zbXelK|4T<{|y4`raF{=72kC2Kn(L4YyenWgrPiv z@^mr$t{#X5VuIMeL!7Ab6_kG$&#&5p*Z{+?5U|TZ`B!7llpVmp@skYz&n^8QfPJzL z0G6K_OJM9x+Wu2gfN45phANGt{7=C>i34CV{Xqlx(fWpeAoj^N0Biu`w+MVcCUyU* zDZuzO0>4Z6fbu^T_arWW5n!E45vX8N=bxTVeFoep_G#VmNlQzAI_KTIc{6>c+04vr zx@W}zE5JNSU>!THJ{J=cqjz+4{L4A{Ob9$ZJ*S1?Ggg3klFp!+Y1@K+pK1DqI|_gq z5ZDXVpge8-cs!o|;K73#YXZ3AShj50wBvuq3NTOZ`M&qtjj#GOFfgExjg8Gn8>Vq5 z`85n+9|!iLCZF5$HJ$Iu($dm?8~-ofu}tEc+-pyke=3!im#6pk_Wo8IA|fJwD&~~F zc16osQ)EBo58U7XDuMexaPRjU@h8tXe%S{fA0NH3vGJFhuyyO!Uyl2^&EOpX{9As0 zWj+P>{@}jxH)8|r;2HdupP!vie{sJ28b&bo!8`D^x}TE$%zXNb^X1p@0PJ86`dZyj z%ce7*{^oo+6%&~I!8hQy-vQ7E)0t0ybH4l%KltWOo~8cO`T=157JqL(oq_rC%ea&4 z2NcTJe-HgFjNg-gZ$6!Y`SMHrlj}Etf7?r!zQTPPSv}{so2e>Fjs1{gzk~LGeesX%r(Lh6rbhSo_n)@@G-FTQy93;l#E)hgP@d_SGvyCp0~o(Y;Ee8{ zdVUDbHm5`2taPUOY^MAGOw*>=s7=Gst=D+p+2yON!0%Hk` zz5mAhyT4lS*T3LS^WSxUy86q&GnoHxzQ6vm8)VS}_zuqG?+3td68_x;etQAdu@sc6 zQJ&5|4(I?~3d-QOAODHpZ=hlSg(lBZ!JZWCtHHSj`0Wh93-Uk)_S%zsJ~aD>{`A0~ z9{AG(e|q3g5B%wYKRxiL2Y$8(4w6bzchKuloQW#e&S3n+P- z8!ds-%f;TJ1>)v)##>gd{PdS2Oc3VaR`fr=`O8QIO(6(N!A?pr5C#6fc~Ge@N%Vvu zaoAX2&(a6eWy_q&UwOhU)|P3J0Qc%OdhzW=F4D|pt0E4osw;%<%Dn58hAWD^XnZD= z>9~H(3bmLtxpF?a7su6J7M*x1By7YSUbxGi)Ot0P77`}P3{)&5Un{KD?`-e?r21!4vTTnN(4Y6Lin?UkSM z`MXCTC1@4A4~mvz%Rh2&EwY))LeoT=*`tMoqcEXI>TZU9WTP#l?uFv+@Dn~b(>xh2 z;>B?;Tz2SR&KVb>vGiBSB`@U7VIWFSo=LDSb9F{GF^DbmWAfpms8Sx9OX4CnBJca3 zlj9(x!dIjN?OG1X4l*imJNvRCk}F%!?SOfiOq5y^mZW)jFL@a|r-@d#f7 z2gmU8L3IZq0ynIws=}~m^#@&C%J6QFo~Mo4V`>v7MI-_!EBMMtb%_M&kvAaN)@ZVw z+`toz&WG#HkWDjnZE!6nk{e-oFdL^$YnbOCN}JC&{$#$O27@|Tn-skXr)2ml2~O!5 zX+gYoxhoc7qoU?C^3~&!U?kRFtnSEecWuH0B0OvLodgUAi}8p1 zrO6RSXHH}DMc$&|?D004DiOVMHV8kXCP@7NKB zgaZq^^O<7PoKEp72kby@W0Z!Y*Ay{&vfg#C&gG@YVR9g?FEocMUi1gSN$+V+ayF45{a zuDZDTN}mS|;BO%gEf}pjBfN2-gIrU#G5~cucA;dokXW89%>AyXJJI z9X4UlIWA|ZYHgbI z5?oFk@A=Ik7lrEQPDH!H+b`7_Y~aDb_qa=B2^Y&Ow41cU=4WDd40dp5(QS-WMN-=Y z9g;6_-JdNU;|6cPwf$ak*aJIcwL@1n$#l~zi{c{EW?T;DaW*E8DYq?Umtz{nJ&w-M zEMyTDrC&9K$d|kZe2#ws6)L=7K+{ zQw{XnV6UC$6-rW0emqm8wJoeZK)wJIcV?dST}Z;G0Arq{dVDu0&4kd%N!3F1*;*pW zR&qUiFzK=@44#QGw7k1`3t_d8&*kBV->O##t|tonFc2YWrL7_eqg+=+k;!F-`^b8> z#KWCE8%u4k@EprxqiV$VmmtiWxDLgnGu$Vs<8rppV5EajBXL4nyyZM$SWVm!wnCj-B!Wjqj5-5dNXukI2$$|Bu3Lrw}z65Lc=1G z^-#WuQOj$hwNGG?*CM_TO8Bg-1+qc>J7k5c51U8g?ZU5n?HYor;~JIjoWH-G>AoUP ztrWWLbRNqIjW#RT*WqZgPJXU7C)VaW5}MiijYbABmzoru6EmQ*N8cVK7a3|aOB#O& zBl8JY2WKfmj;h#Q!pN%9o@VNLv{OUL?rixHwOZuvX7{IJ{(EdPpuVFoQqIOa7giLVkBOKL@^smUA!tZ1CKRK}#SSM)iQHk)*R~?M!qkCruaS!#oIL1c z?J;U~&FfH#*98^G?i}pA{ z9Jg36t4=%6mhY(quYq*vSxptes9qy|7xSlH?G=S@>u>Ebe;|LVhs~@+06N<4CViBk zUiY$thvX;>Tby6z9Y1edAMQaiH zm^r3v#$Q#2T=X>bsY#D%s!bhs^M9PMAcHbCc0FMHV{u-dwlL;a1eJ63v5U*?Q_8JO zT#50!RD619#j_Uf))0ooADz~*9&lN!bBDRUgE>Vud-i5ck%vT=r^yD*^?Mp@Q^v+V zG#-?gKlr}Eeqifb{|So?HM&g91P8|av8hQoCmQXkd?7wIJwb z_^v8bbg`SAn{I*4bH$u(RZ6*xUhuA~hc=8czK8SHEKTzSxgbwi~9(OqJB&gwb^l4+m`k*Q;_?>Y-APi1{k zAHQ)P)G)f|AyjSgcCFps)Fh6Bca*Xznq36!pV6Az&m{O8$wGFD? zY&O*3*J0;_EqM#jh6^gMQKpXV?#1?>$ml1xvh8nSN>-?H=V;nJIwB07YX$e6vLxH( zqYwQ>qxwR(i4f)DLd)-$P>T-no_c!LsN@)8`e;W@)-Hj0>nJ-}Kla4-ZdPJzI&Mce zv)V_j;(3ERN3_@I$N<^|4Lf`B;8n+bX@bHbcZTopEmDI*Jfl)-pFDvo6svPRoo@(x z);_{lY<;);XzT`dBFpRmGrr}z5u1=pC^S-{ce6iXQlLGcItwJ^mZx{m$&DA_oEZ)B{_bYPq-HA zcH8WGoBG(aBU_j)vEy+_71T34@4dmSg!|M8Vf92Zj6WH7Q7t#OHQqWgFE3ARt+%!T z?oLovLVlnf?2c7pTc)~cc^($_8nyKwsN`RA-23ed3sdj(ys%pjjM+9JrctL;dy8a( z@en&CQmnV(()bu|Y%G1-4a(6x{aLytn$T-;(&{QIJB9vMox11U-1HpD@d(QkaJdEb zG{)+6Dos_L+O3NpWo^=gR?evp|CqEG?L&Ut#D*KLaRFOgOEK(Kq1@!EGcTfo+%A&I z=dLbB+d$u{sh?u)xP{PF8L%;YPPW53+@{>5W=Jt#wQpN;0_HYdw1{ksf_XhO4#2F= zyPx6Lx2<92L-;L5PD`zn6zwIH`Jk($?Qw({erA$^bC;q33hv!d!>%wRhj# zal^hk+WGNg;rJtb-EB(?czvOM=H7dl=vblBwAv>}%1@{}mnpUznfq1cE^sgsL0*4I zJ##!*B?=vI_OEVis5o+_IwMIRrpQyT_Sq~ZU%oY7c5JMIADzpD!Upz9h@iWg_>>~j zOLS;wp^i$-E?4<_cp?RiS%Rd?i;f*mOz=~(&3lo<=@(nR!_Rqiprh@weZlL!t#NCc zO!QTcInq|%#>OVgobj{~ixEUec`E25zJ~*DofsQdzIa@5^nOXj2T;8O`l--(QyU^$t?TGY^7#&FQ+2SS3B#qK*k3`ye?8jUYSajE5iBbJls75CCc(m3dk{t?- zopcER9{Z?TC)mk~gpi^kbbu>b-+a{m#8-y2^p$ka4n60w;Sc2}HMf<8JUvhCL0B&Btk)T`ctE$*qNW8L$`7!r^9T+>=<=2qaq-;ll2{`{Rg zc5a0ZUI$oG&j-qVOuKa=*v4aY#IsoM+1|c4Z)<}lEDvy;5huB@1RJPquU2U*U-;gu z=En2m+qjBzR#DEJDO`WU)hdd{Vj%^0V*KoyZ|5lzV87&g_j~NCjwv0uQVqXOb*QrQ zy|Qn`hxx(58c70$E;L(X0uZZ72M1!6oeg)(cdKO ze0gDaTz+ohR-#d)NbAH4x{I(21yjwvBQfmpLu$)|m{XolbgF!pmsqJ#D}(ylp6uC> z{bqtcI#hT#HW=wl7>p!38sKsJ`r8}lt-q%Keqy%u(xk=yiIJiUw6|5IvkS+#?JTBl z8H5(Q?l#wzazujH!8o>1xtn8#_w+397*_cy8!pQGP%K(Ga3pAjsaTbbXJlQF_+m+-UpUUent@xM zg%jqLUExj~o^vQ3Gl*>wh=_gOr2*|U64_iXb+-111aH}$TjeajM+I20xw(((>fej-@CIz4S1pi$(#}P7`4({6QS2CaQS4NPENDp>sAqD z$bH4KGzXGffkJ7R>V>)>tC)uax{UsN*dbeNC*v}#8Y#OWYwL4t$ePR?VTyIs!wea+ z5Urmc)X|^`MG~*dS6pGSbU+gPJoq*^a=_>$n4|P^w$sMBBy@f*Z^Jg6?n5?oId6f{ z$LW4M|4m502z0t7g<#Bx%X;9<=)smFolV&(V^(7Cv2-sxbxopQ!)*#ZRhTBpx1)Fc zNm1T%bONzv6@#|dz(w02AH8OXe>kQ#1FMCzO}2J_mST)+ExmBr9cva-@?;wnmWMOk z{3_~EX_xadgJGv&H@zK_8{(x84`}+c?oSBX*Ge3VdfTt&F}yCpFP?CpW+BE^cWY0^ zb&uBN!Ja3UzYHK-CTyA5=L zEMW{l3Usky#ly=7px648W31UNV@K)&Ub&zP1c7%)`{);I4b0Q<)B}3;NMG2JH=X$U zfIW4)4n9ZM`-yRj67I)YSLDK)qfUJ_ij}a#aZN~9EXrh8eZY2&=uY%2N0UFF7<~%M zsB8=erOWZ>Ct_#^tHZ|*q`H;A)5;ycw*IcmVxi8_0Xk}aJA^ath+E;xg!x+As(M#0=)3!NJR6H&9+zd#iP(m0PIW8$ z1Y^VX`>jm`W!=WpF*{ioM?C9`yOR>@0q=u7o>BP-eSHqCgMDj!2anwH?s%i2p+Q7D zzszIf5XJpE)IG4;d_(La-xenmF(tgAxK`Y4sQ}BSJEPs6N_U2vI{8=0C_F?@7<(G; zo$~G=8p+076G;`}>{MQ>t>7cm=zGtfbdDXm6||jUU|?X?CaE?(<6bKDYKeHlz}DA8 zXT={X=yp_R;HfJ9h%?eWvQ!dRgz&Su*JfNt!Wu>|XfU&68iRikRrHRW|ZxzRR^`eIGt zIeiDgVS>IeExKVRWW8-=A=yA`}`)ZkWBrZD`hpWIxBGkh&f#ijr449~m`j6{4jiJ*C!oVA8ZC?$1RM#K(_b zL9TW)kN*Y4%^-qPpMP7d4)o?Nk#>aoYHT(*g)qmRUb?**F@pnNiy6Fv9rEiUqD(^O zzyS?nBrX63BTRYduaG(0VVG2yJRe%o&rVrLjbxTaAFTd8s;<<@Qs>u(<193R8>}2_ zuwp{7;H2a*X7_jryzriZXMg?bTuegABb^87@SsKkr2)0Gyiax8KQWstw^v#ix45EVrcEhr>!NMhprl$InQMzjSFH54x5k9qHc`@9uKQzvL4ihcq{^B zPrVR=o_ic%Y>6&rMN)hTZsI7I<3&`#(nl+3y3ys9A~&^=4?PL&nd8)`OfG#n zwAMN$1&>K++c{^|7<4P=2y(B{jJsQ0a#U;HTo4ZmWZYvI{+s;Td{Yzem%0*k#)vjpB zia;J&>}ICate44SFYY3vEelqStQWFihx%^vQ@Do(sOy7yR2@WNv7Y9I^yL=nZr3mb zXKV5t@=?-Sk|b{XMhA7ZGB@2hqsx}4xwCW!in#C zI@}scZlr3-NFJ@NFaJlhyfcw{k^vvtGl`N9xSo**rDW4S}i zM9{fMPWo%4wYDG~BZ18BD+}h|GQKc-g^{++3MY>}W_uq7jGHx{mwE9fZiPCoxN$+7 zrODGGJrOkcPQUB(FD5aoS4g~7#6NR^ma7-!>mHuJfY5kTe6PpNNKC9GGRiu^L31uG z$7v`*JknQHsYB!Tm_W{a32TM099djW%5e+j0Ve_ct}IM>XLF1Ap+YvcrLV=|CKo6S zb+9Nl3_YdKP6%Cxy@6TxZ>;4&nTneadr z_ES90ydCev)LV!dN=#(*f}|ZORFdvkYBni^aLbUk>BajeWIOcmHP#8S)*2U~QKI%S zyrLmtPqb&TphJ;>yAxri#;{uyk`JJqODDw%(Z=2`1uc}br^V%>j!gS)D*q*f_-qf8&D;W1dJgQMlaH5er zN2U<%Smb7==vE}dDI8K7cKz!vs^73o9f>2sgiTzWcwY|BMYHH5%Vn7#kiw&eItCqa zIkR2~Q}>X=Ar8W|^Ms41Fm8o6IB2_j60eOeBB1Br!boW7JnoeX6Gs)?7rW0^5psc- zjS16yb>dFn>KPOF;imD}e!enuIniFzv}n$m2#gCCv4jM#ArwlzZ$7@9&XkFxZ4n!V zj3dyiwW4Ki2QG{@i>yuZXQizw_OkZI^-3otXC{!(lUpJF33gI60ak;Uqitp74|B6I zgg{b=Iz}WkhCGj1M=hu4#Aw173YxIVbISaoc z-nLZC*6Tgivd5V`K%GxhBsp@SUU60-rfc$=wb>zdJzXS&-5(NRRodFk;Kxk!S(O(a0e7oY=E( zAyS;Ow?6Q&XA+cnkCb{28_1N8H#?J!*$MmIwLq^*T_9-z^&UE@A(z9oGYtFy6EZef LrJugUA?W`A8`#=m diff --git a/examples/ratchet-whisper/src/app/globals.css b/examples/ratchet-whisper/src/app/globals.css deleted file mode 100644 index f3990aba..00000000 --- a/examples/ratchet-whisper/src/app/globals.css +++ /dev/null @@ -1,344 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -html, -body { - max-width: 100vw; - overflow-x: hidden; -} - -.loader { - animation: spin 1s linear infinite; - height: 10px; - width: 10px; - margin: -5px; - scale: 0.5; -} - -@keyframes spin { - 0% { - box-shadow: - 0px -30px #000, - 10px -30px #000, - 20px -20px #000, - 30px -10px #000, - 30px 0px #000, - 30px 10px #000, - 20px 20px #000, - 10px 30px #000, - 0px 30px transparent, - -10px 30px transparent, - -20px 20px transparent, - -30px 10px transparent, - -30px 0px transparent, - -30px -10px transparent, - -20px -20px transparent, - -10px -30px transparent; - } - 6.25% { - box-shadow: - 0px -30px transparent, - 10px -30px #000, - 20px -20px #000, - 30px -10px #000, - 30px 0px #000, - 30px 10px #000, - 20px 20px #000, - 10px 30px #000, - 0px 30px #000, - -10px 30px transparent, - -20px 20px transparent, - -30px 10px transparent, - -30px 0px transparent, - -30px -10px transparent, - -20px -20px transparent, - -10px -30px transparent; - } - 12.5% { - box-shadow: - 0px -30px transparent, - 10px -30px transparent, - 20px -20px #000, - 30px -10px #000, - 30px 0px #000, - 30px 10px #000, - 20px 20px #000, - 10px 30px #000, - 0px 30px #000, - -10px 30px #000, - -20px 20px transparent, - -30px 10px transparent, - -30px 0px transparent, - -30px -10px transparent, - -20px -20px transparent, - -10px -30px transparent; - } - 18.75% { - box-shadow: - 0px -30px transparent, - 10px -30px transparent, - 20px -20px transparent, - 30px -10px #000, - 30px 0px #000, - 30px 10px #000, - 20px 20px #000, - 10px 30px #000, - 0px 30px #000, - -10px 30px #000, - -20px 20px #000, - -30px 10px transparent, - -30px 0px transparent, - -30px -10px transparent, - -20px -20px transparent, - -10px -30px transparent; - } - 25% { - box-shadow: - 0px -30px transparent, - 10px -30px transparent, - 20px -20px transparent, - 30px -10px transparent, - 30px 0px #000, - 30px 10px #000, - 20px 20px #000, - 10px 30px #000, - 0px 30px #000, - -10px 30px #000, - -20px 20px #000, - -30px 10px #000, - -30px 0px transparent, - -30px -10px transparent, - -20px -20px transparent, - -10px -30px transparent; - } - 31.25% { - box-shadow: - 0px -30px transparent, - 10px -30px transparent, - 20px -20px transparent, - 30px -10px transparent, - 30px 0px transparent, - 30px 10px #000, - 20px 20px #000, - 10px 30px #000, - 0px 30px #000, - -10px 30px #000, - -20px 20px #000, - -30px 10px #000, - -30px 0px #000, - -30px -10px transparent, - -20px -20px transparent, - -10px -30px transparent; - } - 37.5% { - box-shadow: - 0px -30px transparent, - 10px -30px transparent, - 20px -20px transparent, - 30px -10px transparent, - 30px 0px transparent, - 30px 10px transparent, - 20px 20px #000, - 10px 30px #000, - 0px 30px #000, - -10px 30px #000, - -20px 20px #000, - -30px 10px #000, - -30px 0px #000, - -30px -10px #000, - -20px -20px transparent, - -10px -30px transparent; - } - 43.75% { - box-shadow: - 0px -30px transparent, - 10px -30px transparent, - 20px -20px transparent, - 30px -10px transparent, - 30px 0px transparent, - 30px 10px transparent, - 20px 20px transparent, - 10px 30px #000, - 0px 30px #000, - -10px 30px #000, - -20px 20px #000, - -30px 10px #000, - -30px 0px #000, - -30px -10px #000, - -20px -20px #000, - -10px -30px transparent; - } - 50% { - box-shadow: - 0px -30px transparent, - 10px -30px transparent, - 20px -20px transparent, - 30px -10px transparent, - 30px 0px transparent, - 30px 10px transparent, - 20px 20px transparent, - 10px 30px transparent, - 0px 30px #000, - -10px 30px #000, - -20px 20px #000, - -30px 10px #000, - -30px 0px #000, - -30px -10px #000, - -20px -20px #000, - -10px -30px #000; - } - 56.25% { - box-shadow: - 0px -30px #000, - 10px -30px transparent, - 20px -20px transparent, - 30px -10px transparent, - 30px 0px transparent, - 30px 10px transparent, - 20px 20px transparent, - 10px 30px transparent, - 0px 30px transparent, - -10px 30px #000, - -20px 20px #000, - -30px 10px #000, - -30px 0px #000, - -30px -10px #000, - -20px -20px #000, - -10px -30px #000; - } - 62.5% { - box-shadow: - 0px -30px #000, - 10px -30px #000, - 20px -20px transparent, - 30px -10px transparent, - 30px 0px transparent, - 30px 10px transparent, - 20px 20px transparent, - 10px 30px transparent, - 0px 30px transparent, - -10px 30px transparent, - -20px 20px #000, - -30px 10px #000, - -30px 0px #000, - -30px -10px #000, - -20px -20px #000, - -10px -30px #000; - } - 68.75% { - box-shadow: - 0px -30px #000, - 10px -30px #000, - 20px -20px #000, - 30px -10px transparent, - 30px 0px transparent, - 30px 10px transparent, - 20px 20px transparent, - 10px 30px transparent, - 0px 30px transparent, - -10px 30px transparent, - -20px 20px transparent, - -30px 10px #000, - -30px 0px #000, - -30px -10px #000, - -20px -20px #000, - -10px -30px #000; - } - 75% { - box-shadow: - 0px -30px #000, - 10px -30px #000, - 20px -20px #000, - 30px -10px #000, - 30px 0px transparent, - 30px 10px transparent, - 20px 20px transparent, - 10px 30px transparent, - 0px 30px transparent, - -10px 30px transparent, - -20px 20px transparent, - -30px 10px transparent, - -30px 0px #000, - -30px -10px #000, - -20px -20px #000, - -10px -30px #000; - } - 81.25% { - box-shadow: - 0px -30px #000, - 10px -30px #000, - 20px -20px #000, - 30px -10px #000, - 30px 0px #000, - 30px 10px transparent, - 20px 20px transparent, - 10px 30px transparent, - 0px 30px transparent, - -10px 30px transparent, - -20px 20px transparent, - -30px 10px transparent, - -30px 0px transparent, - -30px -10px #000, - -20px -20px #000, - -10px -30px #000; - } - 87.5% { - box-shadow: - 0px -30px #000, - 10px -30px #000, - 20px -20px #000, - 30px -10px #000, - 30px 0px #000, - 30px 10px #000, - 20px 20px transparent, - 10px 30px transparent, - 0px 30px transparent, - -10px 30px transparent, - -20px 20px transparent, - -30px 10px transparent, - -30px 0px transparent, - -30px -10px transparent, - -20px -20px #000, - -10px -30px #000; - } - 93.75% { - box-shadow: - 0px -30px #000, - 10px -30px #000, - 20px -20px #000, - 30px -10px #000, - 30px 0px #000, - 30px 10px #000, - 20px 20px #000, - 10px 30px transparent, - 0px 30px transparent, - -10px 30px transparent, - -20px 20px transparent, - -30px 10px transparent, - -30px 0px transparent, - -30px -10px transparent, - -20px -20px transparent, - -10px -30px #000; - } - 100% { - box-shadow: - 0px -30px #000, - 10px -30px #000, - 20px -20px #000, - 30px -10px #000, - 30px 0px #000, - 30px 10px #000, - 20px 20px #000, - 10px 30px #000, - 0px 30px transparent, - -10px 30px transparent, - -20px 20px transparent, - -30px 10px transparent, - -30px 0px transparent, - -30px -10px transparent, - -20px -20px transparent, - -10px -30px transparent; - } -} - diff --git a/examples/ratchet-whisper/src/app/layout.tsx b/examples/ratchet-whisper/src/app/layout.tsx deleted file mode 100644 index 27026da1..00000000 --- a/examples/ratchet-whisper/src/app/layout.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import type { Metadata } from "next"; -import { Inter } from "next/font/google"; -import "./globals.css"; -import { Toaster } from "react-hot-toast"; -import "react-responsive-modal/styles.css"; - -const inter = Inter({ subsets: ["latin"] }); - -export const metadata: Metadata = { - title: "Whisper by Ratchet", - description: "Simple demo of Whisper.", -}; - -export default function RootLayout({ - children, -}: Readonly<{ - children: React.ReactNode; -}>) { - return ( - - -
    - -
    {children}
    -
    - - - ); -} diff --git a/examples/ratchet-whisper/src/app/page.module.css b/examples/ratchet-whisper/src/app/page.module.css deleted file mode 100644 index c13f8527..00000000 --- a/examples/ratchet-whisper/src/app/page.module.css +++ /dev/null @@ -1,236 +0,0 @@ -.main { - display: flex; - flex-direction: column; - justify-content: space-between; - align-items: center; - padding: 6rem; - min-height: 100vh; -} - -.description { - display: inherit; - justify-content: inherit; - align-items: inherit; - font-size: 0.85rem; - max-width: var(--max-width); - width: 100%; - z-index: 2; - font-family: var(--font-mono); -} - -.description a { - display: flex; - justify-content: center; - align-items: center; - gap: 0.5rem; -} - -.description p { - position: relative; - margin: 0; - padding: 1rem; - background-color: rgba(var(--callout-rgb), 0.5); - border: 1px solid rgba(var(--callout-border-rgb), 0.3); - border-radius: var(--border-radius); -} - -.code { - font-weight: 700; - font-family: var(--font-mono); -} - -.grid { - display: grid; - grid-template-columns: repeat(4, minmax(25%, auto)); - max-width: 100%; - width: var(--max-width); -} - -.card { - padding: 1rem 1.2rem; - border-radius: var(--border-radius); - background: rgba(var(--card-rgb), 0); - border: 1px solid rgba(var(--card-border-rgb), 0); - transition: background 200ms, border 200ms; -} - -.card span { - display: inline-block; - transition: transform 200ms; -} - -.card h2 { - font-weight: 600; - margin-bottom: 0.7rem; -} - -.card p { - margin: 0; - opacity: 0.6; - font-size: 0.9rem; - line-height: 1.5; - max-width: 30ch; - text-wrap: balance; -} - -.center { - display: flex; - justify-content: center; - align-items: center; - position: relative; - padding: 4rem 0; -} - -.center::before { - background: var(--secondary-glow); - border-radius: 50%; - width: 480px; - height: 360px; - margin-left: -400px; -} - -.center::after { - background: var(--primary-glow); - width: 240px; - height: 180px; - z-index: -1; -} - -.center::before, -.center::after { - content: ""; - left: 50%; - position: absolute; - filter: blur(45px); - transform: translateZ(0); -} - -.logo { - position: relative; -} -/* Enable hover only on non-touch devices */ -@media (hover: hover) and (pointer: fine) { - .card:hover { - background: rgba(var(--card-rgb), 0.1); - border: 1px solid rgba(var(--card-border-rgb), 0.15); - } - - .card:hover span { - transform: translateX(4px); - } -} - -@media (prefers-reduced-motion) { - .card:hover span { - transform: none; - } -} - -/* Mobile */ -@media (max-width: 700px) { - .content { - padding: 4rem; - } - - .grid { - grid-template-columns: 1fr; - margin-bottom: 120px; - max-width: 320px; - text-align: center; - } - - .card { - padding: 1rem 2.5rem; - } - - .card h2 { - margin-bottom: 0.5rem; - } - - .center { - padding: 8rem 0 6rem; - } - - .center::before { - transform: none; - height: 300px; - } - - .description { - font-size: 0.8rem; - } - - .description a { - padding: 1rem; - } - - .description p, - .description div { - display: flex; - justify-content: center; - position: fixed; - width: 100%; - } - - .description p { - align-items: center; - inset: 0 0 auto; - padding: 2rem 1rem 1.4rem; - border-radius: 0; - border: none; - border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25); - background: linear-gradient( - to bottom, - rgba(var(--background-start-rgb), 1), - rgba(var(--callout-rgb), 0.5) - ); - background-clip: padding-box; - backdrop-filter: blur(24px); - } - - .description div { - align-items: flex-end; - pointer-events: none; - inset: auto 0 0; - padding: 2rem; - height: 200px; - background: linear-gradient( - to bottom, - transparent 0%, - rgb(var(--background-end-rgb)) 40% - ); - z-index: 1; - } -} - -/* Tablet and Smaller Desktop */ -@media (min-width: 701px) and (max-width: 1120px) { - .grid { - grid-template-columns: repeat(2, 50%); - } -} - -@media (prefers-color-scheme: dark) { - .vercelLogo { - filter: invert(1); - } - - .logo { - filter: invert(1) drop-shadow(0 0 0.3rem #ffffff70); - } -} - -@keyframes rotate { - from { - transform: rotate(360deg); - } - to { - transform: rotate(0deg); - } -} - -.buttonsContainer { - display: flex; - justify-content: center; /* Center buttons horizontally */ - gap: 10px; /* Add some space between the buttons */ -} diff --git a/examples/ratchet-whisper/src/app/page.tsx b/examples/ratchet-whisper/src/app/page.tsx deleted file mode 100644 index 1dd31ef9..00000000 --- a/examples/ratchet-whisper/src/app/page.tsx +++ /dev/null @@ -1,254 +0,0 @@ -'use client' -import { FFmpeg } from '@ffmpeg/ffmpeg' -import { toBlobURL } from '@ffmpeg/util'; -import { useEffect, useRef, useState } from "react"; -import { Model, DecodingOptionsBuilder, default as init, Task, AvailableModels, Quantization, Segment } from "@ratchet-ml/ratchet-web"; -import ConfigModal, { ConfigOptions } from './components/configModal'; -import ModelSelector from './components/modelSelector'; -import ProgressBar from './components/progressBar'; -import MicButton, { AudioMetadata } from './components/micButton'; -import toast from "react-hot-toast"; -import WebGPUModal from './components/WebGPUModal'; - -export default function Home() { - const [selectedModel, setSelectedModel] = useState({ Whisper: "tiny" }); - const [loadedModel, setLoadedModel] = useState(null); - const [model, setModel] = useState(null); - - const ffmpegRef = useRef(new FFmpeg()); - const [ffmpegLoaded, setFFmpegLoaded] = useState(false); - const [blobURL, setBlobURL] = useState(); - const [audioData, setAudioData] = useState(null); - const [audioMetadata, setAudioMetadata] = useState(null); - const [segments, setSegments] = useState([]); - const [isConfigOpen, setIsConfigOpen] = useState(false); - const [configOptions, setConfigOptions] = useState({ - language: null, - task: Task.Transcribe, - suppress_non_speech: true, - }); - const [generating, setGenerating] = useState(false); - const [progress, setProgress] = useState(0); - - - useEffect(() => { - (async () => { - await init(); - })(); - }, []) - - async function loadModel() { - setModel(await Model.load(selectedModel, Quantization.Q8_0, (p: number) => setProgress(p))); - setLoadedModel(selectedModel); - setProgress(0); - } - - const loadFFMPEG = async () => { - console.log("Loading FFmpeg"); - const baseURL = 'https://unpkg.com/@ratchet-ml/ffmpeg-core@0.0.12/dist/umd'; - const ffmpeg = ffmpegRef.current; - ffmpeg.on('log', ({ message }) => { - console.log(message); - }); - await ffmpeg.load({ - coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'), - wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm'), - }); - setFFmpegLoaded(true); - } - - function pcm16ToIntFloat32(pcmData: Uint8Array) { - let int16Array = new Int16Array(pcmData); - let float32Array = new Float32Array(int16Array.length); - for (let i = 0; i < int16Array.length; i++) { - float32Array[i] = int16Array[i] / 32768.0; - } - return float32Array; - } - - const transcode = async (audioData: Uint8Array) => { - if (!ffmpegLoaded) { - await loadFFMPEG(); - } - const ffmpeg = ffmpegRef.current; - await ffmpeg.writeFile('input', audioData); - - const cmd = [ - "-nostdin", - "-threads", "0", - "-i", "input", - "-f", "s16le", - "-ac", "1", - "-acodec", "pcm_s16le", - "-loglevel", "debug", - "-ar", "16000", - "output.pcm" - ]; - - await ffmpeg.exec(cmd); - const data = (await ffmpeg.readFile('output.pcm')) as any; - setBlobURL(URL.createObjectURL(new Blob([data.buffer], { type: 'audio/wav' }))); - return data.buffer; - }; - - - async function runModel() { - if (!model || !audioData || generating) { - return - } - setGenerating(true); - setSegments([]); - let builder = new DecodingOptionsBuilder(); - let options = builder - .setLanguage(configOptions.language ? configOptions.language : "en") - .setTask(configOptions.task) - .setSuppressBlank(configOptions.suppress_non_speech) - .build(); - console.log("Options: ", options); - let callback = (segment: Segment) => { - if (segment.last) { - setGenerating(false); - return; - } - setSegments((currentSegments) => [...currentSegments, segment]); - }; - let result = await model.run({ audio: audioData, decode_options: options, callback: callback }); - console.log("Result: ", result); - console.log("Processing time: ", result.processing_time); - } - - const handleAudioFile = () => async (event: any) => { - const file = event.target.files[0]; - if (!file) { - return; - } - const reader = new FileReader(); - reader.onload = async () => { - let audioBytes = new Uint8Array(reader.result as ArrayBuffer); - let transcoded = await toast.promise( - transcode(audioBytes), - { - loading: 'Transcoding audio...', - success: Transcoded successfully!, - error: Failed to transcode., - } - ); - setAudioData(pcm16ToIntFloat32(transcoded)); - setAudioMetadata({ - file: file, - fromMic: false, - }); - setBlobURL(URL.createObjectURL(file)); - }; - reader.readAsArrayBuffer(file); - }; - - - return ( - <> - -

    Whisper + Ratchet

    -
    -
    - - {progress > 0 && progress < 100 ? : <>} - {loadedModel != selectedModel ? : - <>} -
    -
    - - - -
    - -
    - {blobURL && ( -
    - - -
    - )} - -
    - - -
    -
    -
    -
    - {segments && - segments.map( - (segment: Segment) => { - return ( -
    -
    - {segment.start} - {" -> "} - {segment.stop} -
    -
    - {segment.text} -
    -
    - ); - } - )} -
    -
    -
    - - - ); -} diff --git a/examples/ratchet-whisper/tailwind.config.js b/examples/ratchet-whisper/tailwind.config.js deleted file mode 100644 index f03d9221..00000000 --- a/examples/ratchet-whisper/tailwind.config.js +++ /dev/null @@ -1,10 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -module.exports = { - content: [ - "./src/**/*.{js,ts,jsx,tsx,mdx}", - ], - theme: { - extend: {}, - }, - plugins: [], -} diff --git a/examples/ratchet-whisper/tsconfig.json b/examples/ratchet-whisper/tsconfig.json deleted file mode 100644 index 7b285893..00000000 --- a/examples/ratchet-whisper/tsconfig.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "compilerOptions": { - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "strict": true, - "noEmit": true, - "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "bundler", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "preserve", - "incremental": true, - "plugins": [ - { - "name": "next" - } - ], - "paths": { - "@/*": ["./src/*"] - } - }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"] -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 687c2abf..e62ae23b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -137,58 +137,6 @@ importers: specifier: ^3.4.1 version: 3.4.1(vite@6.1.0) - examples/ratchet-whisper: - dependencies: - '@ffmpeg/ffmpeg': - specifier: 0.12.6 - version: 0.12.6 - '@ffmpeg/util': - specifier: ^0.12.1 - version: 0.12.1 - '@ratchet-ml/ratchet-web': - specifier: link:../../target/pkg/ratchet-web - version: link:../../target/pkg/ratchet-web - fix-webm-duration: - specifier: ^1.0.5 - version: 1.0.5 - next: - specifier: 14.1.0 - version: 14.1.0(react-dom@18.2.0)(react@18.2.0) - react: - specifier: ^18.2.0 - version: 18.2.0 - react-dom: - specifier: ^18.2.0 - version: 18.2.0(react@18.2.0) - react-hot-toast: - specifier: ^2.4.1 - version: 2.4.1(csstype@3.1.3)(react-dom@18.2.0)(react@18.2.0) - react-responsive-modal: - specifier: ^6.4.2 - version: 6.4.2(react-dom@18.2.0)(react@18.2.0) - devDependencies: - '@types/node': - specifier: ^20.11.24 - version: 20.11.24 - '@types/react': - specifier: ^18.2.61 - version: 18.2.61 - '@types/react-dom': - specifier: ^18.2.19 - version: 18.2.19 - autoprefixer: - specifier: ^10.4.18 - version: 10.4.18(postcss@8.4.35) - postcss: - specifier: ^8.4.35 - version: 8.4.35 - tailwindcss: - specifier: ^3.4.1 - version: 3.4.1 - typescript: - specifier: ^5.3.3 - version: 5.3.3 - packages: /@alloc/quick-lru@5.2.0: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index aceecd09..8d71655d 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,3 +1,2 @@ packages: - - "examples/ratchet-whisper" - "examples/ratchet-phi" diff --git a/requirements.txt b/requirements.txt index aa835c00..ef00ccb9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,5 +3,4 @@ numpy==1.24.3 torch==2.3.0 requests==2.26.0 mlx==0.9.1; sys_platform == 'darwin' -git+https://github.com/FL33TW00D/whisper.git@feature/reference#egg=openai-whisper gguf==0.6.0 From 3ab9f62296663da72899ba05cdda25acf7197f9c Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 9 Mar 2025 00:30:05 -0700 Subject: [PATCH 116/590] Update pnpm-lock.yaml --- pnpm-lock.yaml | 2288 ++---------------------------------------------- 1 file changed, 63 insertions(+), 2225 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e62ae23b..d3f58d4b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,9 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +onlyBuiltDependencies: + - esbuild + importers: .: @@ -67,76 +70,6 @@ importers: specifier: ^5.3.3 version: 5.3.3 - examples/ratchet-train-toy: - dependencies: - chart.js: - specifier: ^4.4.7 - version: 4.4.7 - mathjs: - specifier: ^14.2.1 - version: 14.2.1 - devDependencies: - '@eslint/compat': - specifier: ^1.2.5 - version: 1.2.6(eslint@9.20.1) - '@eslint/js': - specifier: ^9.18.0 - version: 9.20.0 - '@sveltejs/adapter-static': - specifier: ^3.0.8 - version: 3.0.8(@sveltejs/kit@2.17.2) - '@sveltejs/kit': - specifier: ^2.16.0 - version: 2.17.2(@sveltejs/vite-plugin-svelte@5.0.3)(svelte@5.20.1)(vite@6.1.0) - '@sveltejs/vite-plugin-svelte': - specifier: ^5.0.0 - version: 5.0.3(svelte@5.20.1)(vite@6.1.0) - '@tailwindcss/vite': - specifier: ^4.0.0 - version: 4.0.6(vite@6.1.0) - eslint: - specifier: ^9.18.0 - version: 9.20.1 - eslint-config-prettier: - specifier: ^10.0.1 - version: 10.0.1(eslint@9.20.1) - eslint-plugin-svelte: - specifier: ^2.46.1 - version: 2.46.1(eslint@9.20.1)(svelte@5.20.1) - globals: - specifier: ^15.14.0 - version: 15.15.0 - prettier: - specifier: ^3.4.2 - version: 3.5.1 - prettier-plugin-svelte: - specifier: ^3.3.3 - version: 3.3.3(prettier@3.5.1)(svelte@5.20.1) - svelte: - specifier: ^5.0.0 - version: 5.20.1 - svelte-check: - specifier: ^4.0.0 - version: 4.1.4(svelte@5.20.1)(typescript@5.3.3) - tailwindcss: - specifier: ^4.0.0 - version: 4.0.6 - typescript: - specifier: ^5.0.0 - version: 5.3.3 - typescript-eslint: - specifier: ^8.20.0 - version: 8.24.1(eslint@9.20.1)(typescript@5.3.3) - vite: - specifier: ^6.1.0 - version: 6.1.0 - vite-plugin-top-level-await: - specifier: ^1.4.4 - version: 1.5.0(vite@6.1.0) - vite-plugin-wasm: - specifier: ^3.4.1 - version: 3.4.1(vite@6.1.0) - packages: /@alloc/quick-lru@5.2.0: @@ -144,21 +77,6 @@ packages: engines: {node: '>=10'} dev: true - /@ampproject/remapping@2.3.0: - resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} - engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/gen-mapping': 0.3.5 - '@jridgewell/trace-mapping': 0.3.25 - dev: true - - /@babel/runtime@7.26.9: - resolution: {integrity: sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==} - engines: {node: '>=6.9.0'} - dependencies: - regenerator-runtime: 0.14.1 - dev: false - /@bedrock-layout/use-forwarded-ref@1.6.1(react@18.2.0): resolution: {integrity: sha512-GD9A9AFLzFNjr7k6fgerSqxfwDWl+wsPS11PErOKe1zkVz0y7RGC9gzlOiX/JrgpyB3NFHWIuGtoOQqifJQQpw==} peerDependencies: @@ -176,318 +94,6 @@ packages: react: 18.2.0 dev: false - /@esbuild/aix-ppc64@0.24.2: - resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - requiresBuild: true - dev: true - optional: true - - /@esbuild/android-arm64@0.24.2: - resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: true - optional: true - - /@esbuild/android-arm@0.24.2: - resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - requiresBuild: true - dev: true - optional: true - - /@esbuild/android-x64@0.24.2: - resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - requiresBuild: true - dev: true - optional: true - - /@esbuild/darwin-arm64@0.24.2: - resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /@esbuild/darwin-x64@0.24.2: - resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /@esbuild/freebsd-arm64@0.24.2: - resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true - - /@esbuild/freebsd-x64@0.24.2: - resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-arm64@0.24.2: - resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-arm@0.24.2: - resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-ia32@0.24.2: - resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-loong64@0.24.2: - resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-mips64el@0.24.2: - resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-ppc64@0.24.2: - resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-riscv64@0.24.2: - resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-s390x@0.24.2: - resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-x64@0.24.2: - resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/netbsd-arm64@0.24.2: - resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - requiresBuild: true - dev: true - optional: true - - /@esbuild/netbsd-x64@0.24.2: - resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - requiresBuild: true - dev: true - optional: true - - /@esbuild/openbsd-arm64@0.24.2: - resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - requiresBuild: true - dev: true - optional: true - - /@esbuild/openbsd-x64@0.24.2: - resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - requiresBuild: true - dev: true - optional: true - - /@esbuild/sunos-x64@0.24.2: - resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - requiresBuild: true - dev: true - optional: true - - /@esbuild/win32-arm64@0.24.2: - resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@esbuild/win32-ia32@0.24.2: - resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@esbuild/win32-x64@0.24.2: - resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@eslint-community/eslint-utils@4.4.1(eslint@9.20.1): - resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - dependencies: - eslint: 9.20.1 - eslint-visitor-keys: 3.4.3 - dev: true - - /@eslint-community/regexpp@4.12.1: - resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - dev: true - - /@eslint/compat@1.2.6(eslint@9.20.1): - resolution: {integrity: sha512-k7HNCqApoDHM6XzT30zGoETj+D+uUcZUb+IVAJmar3u6bvHf7hhHJcWx09QHj4/a2qrKZMWU0E16tvkiAdv06Q==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^9.10.0 - peerDependenciesMeta: - eslint: - optional: true - dependencies: - eslint: 9.20.1 - dev: true - - /@eslint/config-array@0.19.2: - resolution: {integrity: sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dependencies: - '@eslint/object-schema': 2.1.6 - debug: 4.4.0 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - dev: true - - /@eslint/core@0.10.0: - resolution: {integrity: sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dependencies: - '@types/json-schema': 7.0.15 - dev: true - - /@eslint/core@0.11.0: - resolution: {integrity: sha512-DWUB2pksgNEb6Bz2fggIy1wh6fGgZP4Xyy/Mt0QZPiloKKXerbqq9D3SBQTlCRYOrcRPu4vuz+CGjwdfqxnoWA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dependencies: - '@types/json-schema': 7.0.15 - dev: true - - /@eslint/eslintrc@3.2.0: - resolution: {integrity: sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dependencies: - ajv: 6.12.6 - debug: 4.4.0 - espree: 10.3.0 - globals: 14.0.0 - ignore: 5.3.1 - import-fresh: 3.3.1 - js-yaml: 4.1.0 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - dev: true - - /@eslint/js@9.20.0: - resolution: {integrity: sha512-iZA07H9io9Wn836aVTytRaNqh00Sad+EamwOVJT12GTLw1VGMFV/4JaME+JjLtr9fiGaoWgYnS54wrfWsSs4oQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dev: true - - /@eslint/object-schema@2.1.6: - resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dev: true - - /@eslint/plugin-kit@0.2.5: - resolution: {integrity: sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dependencies: - '@eslint/core': 0.10.0 - levn: 0.4.1 - dev: true - /@ffmpeg/ffmpeg@0.12.6: resolution: {integrity: sha512-4CuXDaqrCga5qBwVtiDDR45y65OGPYZd7VzwGCGz3QLdrQH7xaLYEjU19XL4DTCL0WnTSH8752b8Atyb1SiiLw==} engines: {node: '>=18.x'} @@ -505,34 +111,6 @@ packages: engines: {node: '>=18.x'} dev: false - /@humanfs/core@0.19.1: - resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} - engines: {node: '>=18.18.0'} - dev: true - - /@humanfs/node@0.16.6: - resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} - engines: {node: '>=18.18.0'} - dependencies: - '@humanfs/core': 0.19.1 - '@humanwhocodes/retry': 0.3.1 - dev: true - - /@humanwhocodes/module-importer@1.0.1: - resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} - engines: {node: '>=12.22'} - dev: true - - /@humanwhocodes/retry@0.3.1: - resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} - engines: {node: '>=18.18'} - dev: true - - /@humanwhocodes/retry@0.4.1: - resolution: {integrity: sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==} - engines: {node: '>=18.18'} - dev: true - /@isaacs/cliui@8.0.2: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -585,10 +163,6 @@ packages: type-detect: 4.0.8 dev: true - /@kurkle/color@0.3.4: - resolution: {integrity: sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==} - dev: false - /@next/env@14.1.0: resolution: {integrity: sha512-Py8zIo+02ht82brwwhTg36iogzFqGLPXlRGKQw5s+qP/kMNc4MAyDeEwBKDijk6zTIbegEgu8Qy7C1LboslQAw==} dev: false @@ -806,723 +380,61 @@ packages: dev: true optional: true - /@polka/url@1.0.0-next.28: - resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==} - dev: true + /@swc/helpers@0.5.2: + resolution: {integrity: sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==} + dependencies: + tslib: 2.6.2 + dev: false - /@rollup/plugin-virtual@3.0.2: - resolution: {integrity: sha512-10monEYsBp3scM4/ND4LNH5Rxvh3e/cVeL3jWTgZ2SrQ+BmUoQcopVQvnaMcOnykb1VkxUFuDAN+0FnpTFRy2A==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true + /@types/node@20.11.24: + resolution: {integrity: sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==} + dependencies: + undici-types: 5.26.5 dev: true - /@rollup/rollup-android-arm-eabi@4.34.8: - resolution: {integrity: sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw==} - cpu: [arm] - os: [android] - requiresBuild: true + /@types/prop-types@15.7.11: + resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==} dev: true - optional: true - /@rollup/rollup-android-arm64@4.34.8: - resolution: {integrity: sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q==} - cpu: [arm64] - os: [android] - requiresBuild: true + /@types/react-dom@18.2.19: + resolution: {integrity: sha512-aZvQL6uUbIJpjZk4U8JZGbau9KDeAwMfmhyWorxgBkqDIEf6ROjRozcmPIicqsUwPUjbkDfHKgGee1Lq65APcA==} + dependencies: + '@types/react': 18.2.61 dev: true - optional: true - /@rollup/rollup-darwin-arm64@4.34.8: - resolution: {integrity: sha512-02rVdZ5tgdUNRxIUrFdcMBZQoaPMrxtwSb+/hOfBdqkatYHR3lZ2A2EGyHq2sGOd0Owk80oV3snlDASC24He3Q==} - cpu: [arm64] - os: [darwin] - requiresBuild: true + /@types/react@18.2.61: + resolution: {integrity: sha512-NURTN0qNnJa7O/k4XUkEW2yfygA+NxS0V5h1+kp9jPwhzZy95q3ADoGMP0+JypMhrZBTTgjKAUlTctde1zzeQA==} + dependencies: + '@types/prop-types': 15.7.11 + '@types/scheduler': 0.16.8 + csstype: 3.1.3 dev: true - optional: true - /@rollup/rollup-darwin-x64@4.34.8: - resolution: {integrity: sha512-qIP/elwR/tq/dYRx3lgwK31jkZvMiD6qUtOycLhTzCvrjbZ3LjQnEM9rNhSGpbLXVJYQ3rq39A6Re0h9tU2ynw==} - cpu: [x64] - os: [darwin] - requiresBuild: true + /@types/scheduler@0.16.8: + resolution: {integrity: sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==} dev: true - optional: true - /@rollup/rollup-freebsd-arm64@4.34.8: - resolution: {integrity: sha512-IQNVXL9iY6NniYbTaOKdrlVP3XIqazBgJOVkddzJlqnCpRi/yAeSOa8PLcECFSQochzqApIOE1GHNu3pCz+BDA==} - cpu: [arm64] - os: [freebsd] - requiresBuild: true + /acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} + engines: {node: '>=0.4.0'} + hasBin: true dev: true - optional: true - /@rollup/rollup-freebsd-x64@4.34.8: - resolution: {integrity: sha512-TYXcHghgnCqYFiE3FT5QwXtOZqDj5GmaFNTNt3jNC+vh22dc/ukG2cG+pi75QO4kACohZzidsq7yKTKwq/Jq7Q==} - cpu: [x64] - os: [freebsd] - requiresBuild: true + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} dev: true - optional: true - /@rollup/rollup-linux-arm-gnueabihf@4.34.8: - resolution: {integrity: sha512-A4iphFGNkWRd+5m3VIGuqHnG3MVnqKe7Al57u9mwgbyZ2/xF9Jio72MaY7xxh+Y87VAHmGQr73qoKL9HPbXj1g==} - cpu: [arm] - os: [linux] - requiresBuild: true + /ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} dev: true - optional: true - /@rollup/rollup-linux-arm-musleabihf@4.34.8: - resolution: {integrity: sha512-S0lqKLfTm5u+QTxlFiAnb2J/2dgQqRy/XvziPtDd1rKZFXHTyYLoVL58M/XFwDI01AQCDIevGLbQrMAtdyanpA==} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-linux-arm64-gnu@4.34.8: - resolution: {integrity: sha512-jpz9YOuPiSkL4G4pqKrus0pn9aYwpImGkosRKwNi+sJSkz+WU3anZe6hi73StLOQdfXYXC7hUfsQlTnjMd3s1A==} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-linux-arm64-musl@4.34.8: - resolution: {integrity: sha512-KdSfaROOUJXgTVxJNAZ3KwkRc5nggDk+06P6lgi1HLv1hskgvxHUKZ4xtwHkVYJ1Rep4GNo+uEfycCRRxht7+Q==} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-linux-loongarch64-gnu@4.34.8: - resolution: {integrity: sha512-NyF4gcxwkMFRjgXBM6g2lkT58OWztZvw5KkV2K0qqSnUEqCVcqdh2jN4gQrTn/YUpAcNKyFHfoOZEer9nwo6uQ==} - cpu: [loong64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-linux-powerpc64le-gnu@4.34.8: - resolution: {integrity: sha512-LMJc999GkhGvktHU85zNTDImZVUCJ1z/MbAJTnviiWmmjyckP5aQsHtcujMjpNdMZPT2rQEDBlJfubhs3jsMfw==} - cpu: [ppc64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-linux-riscv64-gnu@4.34.8: - resolution: {integrity: sha512-xAQCAHPj8nJq1PI3z8CIZzXuXCstquz7cIOL73HHdXiRcKk8Ywwqtx2wrIy23EcTn4aZ2fLJNBB8d0tQENPCmw==} - cpu: [riscv64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-linux-s390x-gnu@4.34.8: - resolution: {integrity: sha512-DdePVk1NDEuc3fOe3dPPTb+rjMtuFw89gw6gVWxQFAuEqqSdDKnrwzZHrUYdac7A7dXl9Q2Vflxpme15gUWQFA==} - cpu: [s390x] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-linux-x64-gnu@4.34.8: - resolution: {integrity: sha512-8y7ED8gjxITUltTUEJLQdgpbPh1sUQ0kMTmufRF/Ns5tI9TNMNlhWtmPKKHCU0SilX+3MJkZ0zERYYGIVBYHIA==} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-linux-x64-musl@4.34.8: - resolution: {integrity: sha512-SCXcP0ZpGFIe7Ge+McxY5zKxiEI5ra+GT3QRxL0pMMtxPfpyLAKleZODi1zdRHkz5/BhueUrYtYVgubqe9JBNQ==} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-win32-arm64-msvc@4.34.8: - resolution: {integrity: sha512-YHYsgzZgFJzTRbth4h7Or0m5O74Yda+hLin0irAIobkLQFRQd1qWmnoVfwmKm9TXIZVAD0nZ+GEb2ICicLyCnQ==} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-win32-ia32-msvc@4.34.8: - resolution: {integrity: sha512-r3NRQrXkHr4uWy5TOjTpTYojR9XmF0j/RYgKCef+Ag46FWUTltm5ziticv8LdNsDMehjJ543x/+TJAek/xBA2w==} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-win32-x64-msvc@4.34.8: - resolution: {integrity: sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g==} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@sveltejs/adapter-static@3.0.8(@sveltejs/kit@2.17.2): - resolution: {integrity: sha512-YaDrquRpZwfcXbnlDsSrBQNCChVOT9MGuSg+dMAyfsAa1SmiAhrA5jUYUiIMC59G92kIbY/AaQOWcBdq+lh+zg==} - peerDependencies: - '@sveltejs/kit': ^2.0.0 - dependencies: - '@sveltejs/kit': 2.17.2(@sveltejs/vite-plugin-svelte@5.0.3)(svelte@5.20.1)(vite@6.1.0) - dev: true - - /@sveltejs/kit@2.17.2(@sveltejs/vite-plugin-svelte@5.0.3)(svelte@5.20.1)(vite@6.1.0): - resolution: {integrity: sha512-Vypk02baf7qd3SOB1uUwUC/3Oka+srPo2J0a8YN3EfJypRshDkNx9HzNKjSmhOnGWwT+SSO06+N0mAb8iVTmTQ==} - engines: {node: '>=18.13'} - hasBin: true - peerDependencies: - '@sveltejs/vite-plugin-svelte': ^3.0.0 || ^4.0.0-next.1 || ^5.0.0 - svelte: ^4.0.0 || ^5.0.0-next.0 - vite: ^5.0.3 || ^6.0.0 - dependencies: - '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.20.1)(vite@6.1.0) - '@types/cookie': 0.6.0 - cookie: 0.6.0 - devalue: 5.1.1 - esm-env: 1.2.2 - import-meta-resolve: 4.1.0 - kleur: 4.1.5 - magic-string: 0.30.17 - mrmime: 2.0.1 - sade: 1.8.1 - set-cookie-parser: 2.7.1 - sirv: 3.0.1 - svelte: 5.20.1 - vite: 6.1.0 - dev: true - - /@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.0.3)(svelte@5.20.1)(vite@6.1.0): - resolution: {integrity: sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22} - peerDependencies: - '@sveltejs/vite-plugin-svelte': ^5.0.0 - svelte: ^5.0.0 - vite: ^6.0.0 - dependencies: - '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.20.1)(vite@6.1.0) - debug: 4.4.0 - svelte: 5.20.1 - vite: 6.1.0 - transitivePeerDependencies: - - supports-color - dev: true - - /@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.20.1)(vite@6.1.0): - resolution: {integrity: sha512-MCFS6CrQDu1yGwspm4qtli0e63vaPCehf6V7pIMP15AsWgMKrqDGCPFF/0kn4SP0ii4aySu4Pa62+fIRGFMjgw==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22} - peerDependencies: - svelte: ^5.0.0 - vite: ^6.0.0 - dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.0.3)(svelte@5.20.1)(vite@6.1.0) - debug: 4.4.0 - deepmerge: 4.3.1 - kleur: 4.1.5 - magic-string: 0.30.17 - svelte: 5.20.1 - vite: 6.1.0 - vitefu: 1.0.5(vite@6.1.0) - transitivePeerDependencies: - - supports-color - dev: true - - /@swc/core-darwin-arm64@1.10.17: - resolution: {integrity: sha512-LSQhSjESleTc0c45BnVKRacp9Nl4zhJMlV/nmhpFCOv/CqHI5YBDX5c9bPk9jTRNHIf0QH92uTtswt8yN++TCQ==} - engines: {node: '>=10'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /@swc/core-darwin-x64@1.10.17: - resolution: {integrity: sha512-TTaZFS4jLuA3y6+D2HYv4yVGhmjkOGG6KyAwBiJEeoUaazX5MYOyQwaZBPhRGtzHZFrzi4t4jNix4kAkMajPkQ==} - engines: {node: '>=10'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /@swc/core-linux-arm-gnueabihf@1.10.17: - resolution: {integrity: sha512-8P+ESJyGnVdJi0nUcQfxkbTiB/7hnu6N3U72KbvHFBcuroherwzW4DId1XD4RTU2Cjsh1dztZoCcOLY8W9RW1Q==} - engines: {node: '>=10'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@swc/core-linux-arm64-gnu@1.10.17: - resolution: {integrity: sha512-zT21jDQCe+IslzOtw+BD/9ElO/H4qU4fkkOeVQ68PcxuqYS2gwyDxWqa9IGwpzWexYM+Lzi1rAbl/1BM6nGW8Q==} - engines: {node: '>=10'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@swc/core-linux-arm64-musl@1.10.17: - resolution: {integrity: sha512-C2jaW1X+93HscVcesKYgSuZ9GaKqKcQvwvD+q+4JZkaKF4Zopt/aguc6Tmn/nuavRk0WV8yVCpHXoP7lz/2akA==} - engines: {node: '>=10'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@swc/core-linux-x64-gnu@1.10.17: - resolution: {integrity: sha512-vfyxqV5gddurG2NVJLemR/68s7GTe0QruozrZiDpNqr9V4VX9t3PadDKMDAvQz6jKrtiqMtshNXQTNRKAKlzFw==} - engines: {node: '>=10'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@swc/core-linux-x64-musl@1.10.17: - resolution: {integrity: sha512-8M+nI5MHZGQUnXyfTLsGw85a3oQRXMsFjgMZuOEJO9ZGBIEnYVuWOxENfcP6MmlJmTOW+cJxHnMGhKY+fjcntw==} - engines: {node: '>=10'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@swc/core-win32-arm64-msvc@1.10.17: - resolution: {integrity: sha512-iUeIBFM6c/NwsreLFSAH395Dahc+54mSi0Kq//IrZ2Y16VlqCV7VHdOIMrdAyDoBFUvh0jKuLJPWt+jlKGtSLg==} - engines: {node: '>=10'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@swc/core-win32-ia32-msvc@1.10.17: - resolution: {integrity: sha512-lPXYFvkfYIN8HdNmG6dCnQqgA+rOSTgeAjIhGsYCEyLsYkkhF2FQw34OF6PnWawQ6hOdOE9v6Bw3T4enj3Lb6w==} - engines: {node: '>=10'} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@swc/core-win32-x64-msvc@1.10.17: - resolution: {integrity: sha512-KrnkFEWpBmxSe8LixhAZXeeUwTNDVukrPeXJ1PiG+pmb5nI989I9J9IQVIgBv+JXXaK+rmiWjlcIkphaDJJEAA==} - engines: {node: '>=10'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@swc/core@1.10.17: - resolution: {integrity: sha512-FXZx7jHpiwz4fTuuueWwsvN7VFLSoeS3mcxCTPUNOHs/K2ecaBO+slh5T5Xvt/KGuD2I/2T8G6Zts0maPkt2lQ==} - engines: {node: '>=10'} - requiresBuild: true - peerDependencies: - '@swc/helpers': '*' - peerDependenciesMeta: - '@swc/helpers': - optional: true - dependencies: - '@swc/counter': 0.1.3 - '@swc/types': 0.1.17 - optionalDependencies: - '@swc/core-darwin-arm64': 1.10.17 - '@swc/core-darwin-x64': 1.10.17 - '@swc/core-linux-arm-gnueabihf': 1.10.17 - '@swc/core-linux-arm64-gnu': 1.10.17 - '@swc/core-linux-arm64-musl': 1.10.17 - '@swc/core-linux-x64-gnu': 1.10.17 - '@swc/core-linux-x64-musl': 1.10.17 - '@swc/core-win32-arm64-msvc': 1.10.17 - '@swc/core-win32-ia32-msvc': 1.10.17 - '@swc/core-win32-x64-msvc': 1.10.17 - dev: true - - /@swc/counter@0.1.3: - resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} - dev: true - - /@swc/helpers@0.5.2: - resolution: {integrity: sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==} - dependencies: - tslib: 2.6.2 - dev: false - - /@swc/types@0.1.17: - resolution: {integrity: sha512-V5gRru+aD8YVyCOMAjMpWR1Ui577DD5KSJsHP8RAxopAH22jFz6GZd/qxqjO6MJHQhcsjvjOFXyDhyLQUnMveQ==} - dependencies: - '@swc/counter': 0.1.3 - dev: true - - /@tailwindcss/node@4.0.6: - resolution: {integrity: sha512-jb6E0WeSq7OQbVYcIJ6LxnZTeC4HjMvbzFBMCrQff4R50HBlo/obmYNk6V2GCUXDeqiXtvtrQgcIbT+/boB03Q==} - dependencies: - enhanced-resolve: 5.18.1 - jiti: 2.4.2 - tailwindcss: 4.0.6 - dev: true - - /@tailwindcss/oxide-android-arm64@4.0.6: - resolution: {integrity: sha512-xDbym6bDPW3D2XqQqX3PjqW3CKGe1KXH7Fdkc60sX5ZLVUbzPkFeunQaoP+BuYlLc2cC1FoClrIRYnRzof9Sow==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: true - optional: true - - /@tailwindcss/oxide-darwin-arm64@4.0.6: - resolution: {integrity: sha512-1f71/ju/tvyGl5c2bDkchZHy8p8EK/tDHCxlpYJ1hGNvsYihZNurxVpZ0DefpN7cNc9RTT8DjrRoV8xXZKKRjg==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /@tailwindcss/oxide-darwin-x64@4.0.6: - resolution: {integrity: sha512-s/hg/ZPgxFIrGMb0kqyeaqZt505P891buUkSezmrDY6lxv2ixIELAlOcUVTkVh245SeaeEiUVUPiUN37cwoL2g==} - engines: {node: '>= 10'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /@tailwindcss/oxide-freebsd-x64@4.0.6: - resolution: {integrity: sha512-Z3Wo8FWZnmio8+xlcbb7JUo/hqRMSmhQw8IGIRoRJ7GmLR0C+25Wq+bEX/135xe/yEle2lFkhu9JBHd4wZYiig==} - engines: {node: '>= 10'} - cpu: [x64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true - - /@tailwindcss/oxide-linux-arm-gnueabihf@4.0.6: - resolution: {integrity: sha512-SNSwkkim1myAgmnbHs4EjXsPL7rQbVGtjcok5EaIzkHkCAVK9QBQsWeP2Jm2/JJhq4wdx8tZB9Y7psMzHYWCkA==} - engines: {node: '>= 10'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@tailwindcss/oxide-linux-arm64-gnu@4.0.6: - resolution: {integrity: sha512-tJ+mevtSDMQhKlwCCuhsFEFg058kBiSy4TkoeBG921EfrHKmexOaCyFKYhVXy4JtkaeeOcjJnCLasEeqml4i+Q==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@tailwindcss/oxide-linux-arm64-musl@4.0.6: - resolution: {integrity: sha512-IoArz1vfuTR4rALXMUXI/GWWfx2EaO4gFNtBNkDNOYhlTD4NVEwE45nbBoojYiTulajI4c2XH8UmVEVJTOJKxA==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@tailwindcss/oxide-linux-x64-gnu@4.0.6: - resolution: {integrity: sha512-QtsUfLkEAeWAC3Owx9Kg+7JdzE+k9drPhwTAXbXugYB9RZUnEWWx5x3q/au6TvUYcL+n0RBqDEO2gucZRvRFgQ==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@tailwindcss/oxide-linux-x64-musl@4.0.6: - resolution: {integrity: sha512-QthvJqIji2KlGNwLcK/PPYo7w1Wsi/8NK0wAtRGbv4eOPdZHkQ9KUk+oCoP20oPO7i2a6X1aBAFQEL7i08nNMA==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@tailwindcss/oxide-win32-arm64-msvc@4.0.6: - resolution: {integrity: sha512-+oka+dYX8jy9iP00DJ9Y100XsqvbqR5s0yfMZJuPR1H/lDVtDfsZiSix1UFBQ3X1HWxoEEl6iXNJHWd56TocVw==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@tailwindcss/oxide-win32-x64-msvc@4.0.6: - resolution: {integrity: sha512-+o+juAkik4p8Ue/0LiflQXPmVatl6Av3LEZXpBTfg4qkMIbZdhCGWFzHdt2NjoMiLOJCFDddoV6GYaimvK1Olw==} - engines: {node: '>= 10'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@tailwindcss/oxide@4.0.6: - resolution: {integrity: sha512-lVyKV2y58UE9CeKVcYykULe9QaE1dtKdxDEdrTPIdbzRgBk6bdxHNAoDqvcqXbIGXubn3VOl1O/CFF77v/EqSA==} - engines: {node: '>= 10'} - optionalDependencies: - '@tailwindcss/oxide-android-arm64': 4.0.6 - '@tailwindcss/oxide-darwin-arm64': 4.0.6 - '@tailwindcss/oxide-darwin-x64': 4.0.6 - '@tailwindcss/oxide-freebsd-x64': 4.0.6 - '@tailwindcss/oxide-linux-arm-gnueabihf': 4.0.6 - '@tailwindcss/oxide-linux-arm64-gnu': 4.0.6 - '@tailwindcss/oxide-linux-arm64-musl': 4.0.6 - '@tailwindcss/oxide-linux-x64-gnu': 4.0.6 - '@tailwindcss/oxide-linux-x64-musl': 4.0.6 - '@tailwindcss/oxide-win32-arm64-msvc': 4.0.6 - '@tailwindcss/oxide-win32-x64-msvc': 4.0.6 - dev: true - - /@tailwindcss/vite@4.0.6(vite@6.1.0): - resolution: {integrity: sha512-O25vZ/URWbZ2JHdk2o8wH7jOKqEGCsYmX3GwGmYS5DjE4X3mpf93a72Rn7VRnefldNauBzr5z2hfZptmBNtTUQ==} - peerDependencies: - vite: ^5.2.0 || ^6 - dependencies: - '@tailwindcss/node': 4.0.6 - '@tailwindcss/oxide': 4.0.6 - lightningcss: 1.29.1 - tailwindcss: 4.0.6 - vite: 6.1.0 - dev: true - - /@types/cookie@0.6.0: - resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} - dev: true - - /@types/estree@1.0.6: - resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} - dev: true - - /@types/json-schema@7.0.15: - resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - dev: true - - /@types/node@20.11.24: - resolution: {integrity: sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==} - dependencies: - undici-types: 5.26.5 - dev: true - - /@types/prop-types@15.7.11: - resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==} - dev: true - - /@types/react-dom@18.2.19: - resolution: {integrity: sha512-aZvQL6uUbIJpjZk4U8JZGbau9KDeAwMfmhyWorxgBkqDIEf6ROjRozcmPIicqsUwPUjbkDfHKgGee1Lq65APcA==} - dependencies: - '@types/react': 18.2.61 - dev: true - - /@types/react@18.2.61: - resolution: {integrity: sha512-NURTN0qNnJa7O/k4XUkEW2yfygA+NxS0V5h1+kp9jPwhzZy95q3ADoGMP0+JypMhrZBTTgjKAUlTctde1zzeQA==} - dependencies: - '@types/prop-types': 15.7.11 - '@types/scheduler': 0.16.8 - csstype: 3.1.3 - dev: true - - /@types/scheduler@0.16.8: - resolution: {integrity: sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==} - dev: true - - /@typescript-eslint/eslint-plugin@8.24.1(@typescript-eslint/parser@8.24.1)(eslint@9.20.1)(typescript@5.3.3): - resolution: {integrity: sha512-ll1StnKtBigWIGqvYDVuDmXJHVH4zLVot1yQ4fJtLpL7qacwkxJc1T0bptqw+miBQ/QfUbhl1TcQ4accW5KUyA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.8.0' - dependencies: - '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.24.1(eslint@9.20.1)(typescript@5.3.3) - '@typescript-eslint/scope-manager': 8.24.1 - '@typescript-eslint/type-utils': 8.24.1(eslint@9.20.1)(typescript@5.3.3) - '@typescript-eslint/utils': 8.24.1(eslint@9.20.1)(typescript@5.3.3) - '@typescript-eslint/visitor-keys': 8.24.1 - eslint: 9.20.1 - graphemer: 1.4.0 - ignore: 5.3.1 - natural-compare: 1.4.0 - ts-api-utils: 2.0.1(typescript@5.3.3) - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/parser@8.24.1(eslint@9.20.1)(typescript@5.3.3): - resolution: {integrity: sha512-Tqoa05bu+t5s8CTZFaGpCH2ub3QeT9YDkXbPd3uQ4SfsLoh1/vv2GEYAioPoxCWJJNsenXlC88tRjwoHNts1oQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.8.0' - dependencies: - '@typescript-eslint/scope-manager': 8.24.1 - '@typescript-eslint/types': 8.24.1 - '@typescript-eslint/typescript-estree': 8.24.1(typescript@5.3.3) - '@typescript-eslint/visitor-keys': 8.24.1 - debug: 4.4.0 - eslint: 9.20.1 - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/scope-manager@8.24.1: - resolution: {integrity: sha512-OdQr6BNBzwRjNEXMQyaGyZzgg7wzjYKfX2ZBV3E04hUCBDv3GQCHiz9RpqdUIiVrMgJGkXm3tcEh4vFSHreS2Q==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dependencies: - '@typescript-eslint/types': 8.24.1 - '@typescript-eslint/visitor-keys': 8.24.1 - dev: true - - /@typescript-eslint/type-utils@8.24.1(eslint@9.20.1)(typescript@5.3.3): - resolution: {integrity: sha512-/Do9fmNgCsQ+K4rCz0STI7lYB4phTtEXqqCAs3gZW0pnK7lWNkvWd5iW545GSmApm4AzmQXmSqXPO565B4WVrw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.8.0' - dependencies: - '@typescript-eslint/typescript-estree': 8.24.1(typescript@5.3.3) - '@typescript-eslint/utils': 8.24.1(eslint@9.20.1)(typescript@5.3.3) - debug: 4.4.0 - eslint: 9.20.1 - ts-api-utils: 2.0.1(typescript@5.3.3) - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/types@8.24.1: - resolution: {integrity: sha512-9kqJ+2DkUXiuhoiYIUvIYjGcwle8pcPpdlfkemGvTObzgmYfJ5d0Qm6jwb4NBXP9W1I5tss0VIAnWFumz3mC5A==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dev: true - - /@typescript-eslint/typescript-estree@8.24.1(typescript@5.3.3): - resolution: {integrity: sha512-UPyy4MJ/0RE648DSKQe9g0VDSehPINiejjA6ElqnFaFIhI6ZEiZAkUI0D5MCk0bQcTf/LVqZStvQ6K4lPn/BRg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <5.8.0' - dependencies: - '@typescript-eslint/types': 8.24.1 - '@typescript-eslint/visitor-keys': 8.24.1 - debug: 4.4.0 - fast-glob: 3.3.2 - is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.7.1 - ts-api-utils: 2.0.1(typescript@5.3.3) - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/utils@8.24.1(eslint@9.20.1)(typescript@5.3.3): - resolution: {integrity: sha512-OOcg3PMMQx9EXspId5iktsI3eMaXVwlhC8BvNnX6B5w9a4dVgpkQZuU8Hy67TolKcl+iFWq0XX+jbDGN4xWxjQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.8.0' - dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.20.1) - '@typescript-eslint/scope-manager': 8.24.1 - '@typescript-eslint/types': 8.24.1 - '@typescript-eslint/typescript-estree': 8.24.1(typescript@5.3.3) - eslint: 9.20.1 - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/visitor-keys@8.24.1: - resolution: {integrity: sha512-EwVHlp5l+2vp8CoqJm9KikPZgi3gbdZAtabKT9KPShGeOcJhsv4Zdo3oc8T8I0uKEmYoU4ItyxbptjF08enaxg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dependencies: - '@typescript-eslint/types': 8.24.1 - eslint-visitor-keys: 4.2.0 - dev: true - - /acorn-jsx@5.3.2(acorn@8.12.1): - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - acorn: 8.12.1 - dev: true - - /acorn-jsx@5.3.2(acorn@8.14.0): - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - acorn: 8.14.0 - dev: true - - /acorn-typescript@1.4.13(acorn@8.12.1): - resolution: {integrity: sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q==} - peerDependencies: - acorn: '>=8.9.0' - dependencies: - acorn: 8.12.1 - dev: true - - /acorn@8.12.1: - resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} - engines: {node: '>=0.4.0'} - hasBin: true - dev: true - - /acorn@8.14.0: - resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} - engines: {node: '>=0.4.0'} - hasBin: true - dev: true - - /ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - dev: true - - /ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - dev: true - - /ansi-regex@6.0.1: - resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} - engines: {node: '>=12'} - dev: true - - /ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - dependencies: - color-convert: 2.0.1 + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 dev: true /ansi-styles@6.2.1: @@ -1546,15 +458,6 @@ packages: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} dev: true - /argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - dev: true - - /aria-query@5.3.2: - resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} - engines: {node: '>= 0.4'} - dev: true - /autoprefixer@10.4.18(postcss@8.4.35): resolution: {integrity: sha512-1DKbDfsr6KUElM6wg+0zRNkB/Q7WcKYAaK+pzXn+Xqmszm/5Xa9coeNdtP88Vi+dPzZnMjhge8GIV49ZQkDa+g==} engines: {node: ^10 || ^12 || >=14} @@ -1579,11 +482,6 @@ packages: - debug dev: true - /axobject-query@4.1.0: - resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} - engines: {node: '>= 0.4'} - dev: true - /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true @@ -1654,11 +552,6 @@ packages: resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} dev: true - /callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} - dev: true - /camelcase-css@2.0.1: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} @@ -1667,21 +560,6 @@ packages: /caniuse-lite@1.0.30001591: resolution: {integrity: sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ==} - /chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - dev: true - - /chart.js@4.4.7: - resolution: {integrity: sha512-pwkcKfdzTMAU/+jNosKhNL2bHtJc/sSmYgVbuGTEDhzkrhmyihmP7vUc/5ZK9WopidMDHNe3Wm7jOd/WhuHWuw==} - engines: {pnpm: '>=8'} - dependencies: - '@kurkle/color': 0.3.4 - dev: false - /chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} @@ -1697,13 +575,6 @@ packages: fsevents: 2.3.3 dev: true - /chokidar@4.0.3: - resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} - engines: {node: '>= 14.16.0'} - dependencies: - readdirp: 4.1.2 - dev: true - /chownr@2.0.0: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} @@ -1717,11 +588,6 @@ packages: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} dev: false - /clsx@2.1.1: - resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} - engines: {node: '>=6'} - dev: true - /color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -1738,34 +604,16 @@ packages: engines: {node: '>= 6'} dev: true - /complex.js@2.4.2: - resolution: {integrity: sha512-qtx7HRhPGSCBtGiST4/WGHuW+zeaND/6Ld+db6PbrulIB1i2Ev/2UPiqcmpQNPSyfBKraC0EOvOKCB5dGZKt3g==} - dev: false - /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} dev: true - /confbox@0.1.7: - resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} - dev: true - - /cookie@0.6.0: - resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} - engines: {node: '>= 0.6'} - dev: true - - /cross-spawn@7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} - engines: {node: '>= 8'} - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - dev: true - - /cross-spawn@7.0.6: - resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + /confbox@0.1.7: + resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} + dev: true + + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} dependencies: path-key: 3.1.1 @@ -1782,50 +630,15 @@ packages: /csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - /debug@4.4.0: - resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - dependencies: - ms: 2.1.3 - dev: true - - /decimal.js@10.5.0: - resolution: {integrity: sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==} - dev: false - /decode-uri-component@0.4.1: resolution: {integrity: sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ==} engines: {node: '>=14.16'} dev: true - /deep-is@0.1.4: - resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - dev: true - - /deepmerge@4.3.1: - resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} - engines: {node: '>=0.10.0'} - dev: true - /deprecation@2.3.1: resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==} dev: true - /detect-libc@1.0.3: - resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} - engines: {node: '>=0.10'} - hasBin: true - dev: true - - /devalue@5.1.1: - resolution: {integrity: sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==} - dev: true - /didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} dev: true @@ -1850,237 +663,11 @@ packages: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} dev: true - /enhanced-resolve@5.18.1: - resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} - engines: {node: '>=10.13.0'} - dependencies: - graceful-fs: 4.2.11 - tapable: 2.2.1 - dev: true - - /esbuild@0.24.2: - resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==} - engines: {node: '>=18'} - hasBin: true - requiresBuild: true - optionalDependencies: - '@esbuild/aix-ppc64': 0.24.2 - '@esbuild/android-arm': 0.24.2 - '@esbuild/android-arm64': 0.24.2 - '@esbuild/android-x64': 0.24.2 - '@esbuild/darwin-arm64': 0.24.2 - '@esbuild/darwin-x64': 0.24.2 - '@esbuild/freebsd-arm64': 0.24.2 - '@esbuild/freebsd-x64': 0.24.2 - '@esbuild/linux-arm': 0.24.2 - '@esbuild/linux-arm64': 0.24.2 - '@esbuild/linux-ia32': 0.24.2 - '@esbuild/linux-loong64': 0.24.2 - '@esbuild/linux-mips64el': 0.24.2 - '@esbuild/linux-ppc64': 0.24.2 - '@esbuild/linux-riscv64': 0.24.2 - '@esbuild/linux-s390x': 0.24.2 - '@esbuild/linux-x64': 0.24.2 - '@esbuild/netbsd-arm64': 0.24.2 - '@esbuild/netbsd-x64': 0.24.2 - '@esbuild/openbsd-arm64': 0.24.2 - '@esbuild/openbsd-x64': 0.24.2 - '@esbuild/sunos-x64': 0.24.2 - '@esbuild/win32-arm64': 0.24.2 - '@esbuild/win32-ia32': 0.24.2 - '@esbuild/win32-x64': 0.24.2 - dev: true - /escalade@3.1.2: resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} engines: {node: '>=6'} dev: true - /escape-latex@1.2.0: - resolution: {integrity: sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==} - dev: false - - /escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - dev: true - - /eslint-compat-utils@0.5.1(eslint@9.20.1): - resolution: {integrity: sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==} - engines: {node: '>=12'} - peerDependencies: - eslint: '>=6.0.0' - dependencies: - eslint: 9.20.1 - semver: 7.7.1 - dev: true - - /eslint-config-prettier@10.0.1(eslint@9.20.1): - resolution: {integrity: sha512-lZBts941cyJyeaooiKxAtzoPHTN+GbQTJFAIdQbRhA4/8whaAraEh47Whw/ZFfrjNSnlAxqfm9i0XVAEkULjCw==} - hasBin: true - peerDependencies: - eslint: '>=7.0.0' - dependencies: - eslint: 9.20.1 - dev: true - - /eslint-plugin-svelte@2.46.1(eslint@9.20.1)(svelte@5.20.1): - resolution: {integrity: sha512-7xYr2o4NID/f9OEYMqxsEQsCsj4KaMy4q5sANaKkAb6/QeCjYFxRmDm2S3YC3A3pl1kyPZ/syOx/i7LcWYSbIw==} - engines: {node: ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^7.0.0 || ^8.0.0-0 || ^9.0.0-0 - svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 - peerDependenciesMeta: - svelte: - optional: true - dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.20.1) - '@jridgewell/sourcemap-codec': 1.5.0 - eslint: 9.20.1 - eslint-compat-utils: 0.5.1(eslint@9.20.1) - esutils: 2.0.3 - known-css-properties: 0.35.0 - postcss: 8.5.2 - postcss-load-config: 3.1.4(postcss@8.5.2) - postcss-safe-parser: 6.0.0(postcss@8.5.2) - postcss-selector-parser: 6.1.2 - semver: 7.7.1 - svelte: 5.20.1 - svelte-eslint-parser: 0.43.0(svelte@5.20.1) - transitivePeerDependencies: - - ts-node - dev: true - - /eslint-scope@7.2.2: - resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - dev: true - - /eslint-scope@8.2.0: - resolution: {integrity: sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - dev: true - - /eslint-visitor-keys@3.4.3: - resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true - - /eslint-visitor-keys@4.2.0: - resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dev: true - - /eslint@9.20.1: - resolution: {integrity: sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - hasBin: true - peerDependencies: - jiti: '*' - peerDependenciesMeta: - jiti: - optional: true - dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.20.1) - '@eslint-community/regexpp': 4.12.1 - '@eslint/config-array': 0.19.2 - '@eslint/core': 0.11.0 - '@eslint/eslintrc': 3.2.0 - '@eslint/js': 9.20.0 - '@eslint/plugin-kit': 0.2.5 - '@humanfs/node': 0.16.6 - '@humanwhocodes/module-importer': 1.0.1 - '@humanwhocodes/retry': 0.4.1 - '@types/estree': 1.0.6 - '@types/json-schema': 7.0.15 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.6 - debug: 4.4.0 - escape-string-regexp: 4.0.0 - eslint-scope: 8.2.0 - eslint-visitor-keys: 4.2.0 - espree: 10.3.0 - esquery: 1.6.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 8.0.0 - find-up: 5.0.0 - glob-parent: 6.0.2 - ignore: 5.3.1 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - json-stable-stringify-without-jsonify: 1.0.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.4 - transitivePeerDependencies: - - supports-color - dev: true - - /esm-env@1.2.2: - resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} - dev: true - - /espree@10.3.0: - resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dependencies: - acorn: 8.14.0 - acorn-jsx: 5.3.2(acorn@8.14.0) - eslint-visitor-keys: 4.2.0 - dev: true - - /espree@9.6.1: - resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - acorn: 8.12.1 - acorn-jsx: 5.3.2(acorn@8.12.1) - eslint-visitor-keys: 3.4.3 - dev: true - - /esquery@1.6.0: - resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} - engines: {node: '>=0.10'} - dependencies: - estraverse: 5.3.0 - dev: true - - /esrap@1.4.5: - resolution: {integrity: sha512-CjNMjkBWWZeHn+VX+gS8YvFwJ5+NDhg8aWZBSFJPR8qQduDNjbJodA2WcwCm7uQa5Rjqj+nZvVmceg1RbHFB9g==} - dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 - dev: true - - /esrecurse@4.3.0: - resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} - engines: {node: '>=4.0'} - dependencies: - estraverse: 5.3.0 - dev: true - - /estraverse@5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} - engines: {node: '>=4.0'} - dev: true - - /esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} - dev: true - - /fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - dev: true - /fast-glob@3.3.2: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} engines: {node: '>=8.6.0'} @@ -2092,36 +679,12 @@ packages: micromatch: 4.0.5 dev: true - /fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - dev: true - - /fast-levenshtein@2.0.6: - resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - dev: true - /fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} dependencies: reusify: 1.0.4 dev: true - /fdir@6.4.3: - resolution: {integrity: sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==} - peerDependencies: - picomatch: ^3 || ^4 - peerDependenciesMeta: - picomatch: - optional: true - dev: true - - /file-entry-cache@8.0.0: - resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} - engines: {node: '>=16.0.0'} - dependencies: - flat-cache: 4.0.1 - dev: true - /fill-range@7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} @@ -2134,30 +697,10 @@ packages: engines: {node: '>=14.16'} dev: true - /find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} - dependencies: - locate-path: 6.0.0 - path-exists: 4.0.0 - dev: true - /fix-webm-duration@1.0.5: resolution: {integrity: sha512-b6oula3OfSknx0aWoLsxvp4DVIYbwsf+UAkr6EDAK3iuMYk/OSNKzmeSI61GXK0MmFTEuzle19BPvTxMIKjkZg==} dev: false - /flat-cache@4.0.1: - resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} - engines: {node: '>=16'} - dependencies: - flatted: 3.3.2 - keyv: 4.5.4 - dev: true - - /flatted@3.3.2: - resolution: {integrity: sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==} - dev: true - /follow-redirects@1.15.6: resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} engines: {node: '>=4.0'} @@ -2180,11 +723,6 @@ packages: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} dev: true - /fraction.js@5.2.1: - resolution: {integrity: sha512-Ah6t/7YCYjrPUFUFsOsRLMXAdnYM+aQwmojD2Ayb/Ezr82SwES0vuyQ8qZ3QO8n9j7W14VJuVZZet8U3bhSdQQ==} - engines: {node: '>= 12'} - dev: false - /fs-minipass@2.1.0: resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} engines: {node: '>= 8'} @@ -2200,7 +738,6 @@ packages: 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 @@ -2246,16 +783,6 @@ packages: path-is-absolute: 1.0.1 dev: true - /globals@14.0.0: - resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} - engines: {node: '>=18'} - dev: true - - /globals@15.15.0: - resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} - engines: {node: '>=18'} - dev: true - /goober@2.1.14(csstype@3.1.3): resolution: {integrity: sha512-4UpC0NdGyAFqLNPnhCT2iHpza2q+RAY3GV85a/mRPdzyPQMsj0KmMMuetdIkzWRbJ+Hgau1EZztq8ImmiMGhsg==} peerDependencies: @@ -2266,15 +793,7 @@ packages: /graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - - /graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - dev: true - - /has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - dev: true + dev: false /hasown@2.0.1: resolution: {integrity: sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==} @@ -2288,23 +807,6 @@ packages: engines: {node: '>= 4'} dev: true - /import-fresh@3.3.1: - resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} - engines: {node: '>=6'} - dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - dev: true - - /import-meta-resolve@4.1.0: - resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==} - dev: true - - /imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - dev: true - /inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. @@ -2352,198 +854,33 @@ packages: engines: {node: '>=0.12.0'} dev: true - /is-reference@3.0.3: - resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} - dependencies: - '@types/estree': 1.0.6 - dev: true - /isbinaryfile@5.0.2: resolution: {integrity: sha512-GvcjojwonMjWbTkfMpnVHVqXW/wKMYDfEpY94/8zy8HFMOqb/VL6oeONq9v87q4ttVlaTLnGXnJD4B5B1OTGIg==} engines: {node: '>= 18.0.0'} dev: true /isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - dev: true - - /jackspeak@2.3.6: - resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} - engines: {node: '>=14'} - dependencies: - '@isaacs/cliui': 8.0.2 - optionalDependencies: - '@pkgjs/parseargs': 0.11.0 - dev: true - - /javascript-natural-sort@0.7.1: - resolution: {integrity: sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==} - dev: false - - /jiti@1.21.0: - resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==} - hasBin: true - dev: true - - /jiti@2.4.2: - resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} - hasBin: true - dev: true - - /js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - dev: false - - /js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} - hasBin: true - dependencies: - argparse: 2.0.1 - dev: true - - /json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - dev: true - - /json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - dev: true - - /json-stable-stringify-without-jsonify@1.0.1: - resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - dev: true - - /keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - dependencies: - json-buffer: 3.0.1 - dev: true - - /kleur@4.1.5: - resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} - engines: {node: '>=6'} - dev: true - - /known-css-properties@0.35.0: - resolution: {integrity: sha512-a/RAk2BfKk+WFGhhOCAYqSiFLc34k8Mt/6NWRI4joER0EYUzXIcFivjjnoD3+XU1DggLn/tZc3DOAgke7l8a4A==} - dev: true - - /levn@0.4.1: - resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} - engines: {node: '>= 0.8.0'} - dependencies: - prelude-ls: 1.2.1 - type-check: 0.4.0 - dev: true - - /lightningcss-darwin-arm64@1.29.1: - resolution: {integrity: sha512-HtR5XJ5A0lvCqYAoSv2QdZZyoHNttBpa5EP9aNuzBQeKGfbyH5+UipLWvVzpP4Uml5ej4BYs5I9Lco9u1fECqw==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /lightningcss-darwin-x64@1.29.1: - resolution: {integrity: sha512-k33G9IzKUpHy/J/3+9MCO4e+PzaFblsgBjSGlpAaFikeBFm8B/CkO3cKU9oI4g+fjS2KlkLM/Bza9K/aw8wsNA==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /lightningcss-freebsd-x64@1.29.1: - resolution: {integrity: sha512-0SUW22fv/8kln2LnIdOCmSuXnxgxVC276W5KLTwoehiO0hxkacBxjHOL5EtHD8BAXg2BvuhsJPmVMasvby3LiQ==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true - - /lightningcss-linux-arm-gnueabihf@1.29.1: - resolution: {integrity: sha512-sD32pFvlR0kDlqsOZmYqH/68SqUMPNj+0pucGxToXZi4XZgZmqeX/NkxNKCPsswAXU3UeYgDSpGhu05eAufjDg==} - engines: {node: '>= 12.0.0'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /lightningcss-linux-arm64-gnu@1.29.1: - resolution: {integrity: sha512-0+vClRIZ6mmJl/dxGuRsE197o1HDEeeRk6nzycSy2GofC2JsY4ifCRnvUWf/CUBQmlrvMzt6SMQNMSEu22csWQ==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /lightningcss-linux-arm64-musl@1.29.1: - resolution: {integrity: sha512-UKMFrG4rL/uHNgelBsDwJcBqVpzNJbzsKkbI3Ja5fg00sgQnHw/VrzUTEc4jhZ+AN2BvQYz/tkHu4vt1kLuJyw==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /lightningcss-linux-x64-gnu@1.29.1: - resolution: {integrity: sha512-u1S+xdODy/eEtjADqirA774y3jLcm8RPtYztwReEXoZKdzgsHYPl0s5V52Tst+GKzqjebkULT86XMSxejzfISw==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /lightningcss-linux-x64-musl@1.29.1: - resolution: {integrity: sha512-L0Tx0DtaNUTzXv0lbGCLB/c/qEADanHbu4QdcNOXLIe1i8i22rZRpbT3gpWYsCh9aSL9zFujY/WmEXIatWvXbw==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /lightningcss-win32-arm64-msvc@1.29.1: - resolution: {integrity: sha512-QoOVnkIEFfbW4xPi+dpdft/zAKmgLgsRHfJalEPYuJDOWf7cLQzYg0DEh8/sn737FaeMJxHZRc1oBreiwZCjog==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /lightningcss-win32-x64-msvc@1.29.1: - resolution: {integrity: sha512-NygcbThNBe4JElP+olyTI/doBNGJvLs3bFCRPdvuCcxZCcCZ71B858IHpdm7L1btZex0FvCmM17FK98Y9MRy1Q==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [win32] - requiresBuild: true + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true - optional: true - /lightningcss@1.29.1: - resolution: {integrity: sha512-FmGoeD4S05ewj+AkhTY+D+myDvXI6eL27FjHIjoyUkO/uw7WZD1fBVs0QxeYWa7E17CUHJaYX/RUGISCtcrG4Q==} - engines: {node: '>= 12.0.0'} + /jackspeak@2.3.6: + resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} + engines: {node: '>=14'} dependencies: - detect-libc: 1.0.3 + '@isaacs/cliui': 8.0.2 optionalDependencies: - lightningcss-darwin-arm64: 1.29.1 - lightningcss-darwin-x64: 1.29.1 - lightningcss-freebsd-x64: 1.29.1 - lightningcss-linux-arm-gnueabihf: 1.29.1 - lightningcss-linux-arm64-gnu: 1.29.1 - lightningcss-linux-arm64-musl: 1.29.1 - lightningcss-linux-x64-gnu: 1.29.1 - lightningcss-linux-x64-musl: 1.29.1 - lightningcss-win32-arm64-msvc: 1.29.1 - lightningcss-win32-x64-msvc: 1.29.1 + '@pkgjs/parseargs': 0.11.0 dev: true + /jiti@1.21.0: + resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==} + hasBin: true + dev: true + + /js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + dev: false + /lilconfig@2.1.0: resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} engines: {node: '>=10'} @@ -2558,21 +895,6 @@ packages: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} dev: true - /locate-character@3.0.0: - resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} - dev: true - - /locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} - dependencies: - p-locate: 5.0.0 - dev: true - - /lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - dev: true - /loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -2585,28 +907,6 @@ packages: engines: {node: 14 || >=16.14} dev: true - /magic-string@0.30.17: - resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} - dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 - dev: true - - /mathjs@14.2.1: - resolution: {integrity: sha512-vARWETUx75+kR2K9qBV20n6NYtGXCuQKX8Zo4+AhJI5LX+ukSM1NYebv+wLnJG8KMvEe9H01sJUyC5bMciA4Tg==} - engines: {node: '>= 18'} - hasBin: true - dependencies: - '@babel/runtime': 7.26.9 - complex.js: 2.4.2 - decimal.js: 10.5.0 - escape-latex: 1.2.0 - fraction.js: 5.2.1 - javascript-natural-sort: 0.7.1 - seedrandom: 3.0.5 - tiny-emitter: 2.1.0 - typed-function: 4.2.1 - dev: false - /merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -2633,13 +933,6 @@ packages: brace-expansion: 2.0.1 dev: true - /minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} - engines: {node: '>=16 || 14 >=14.17'} - dependencies: - brace-expansion: 2.0.1 - dev: true - /minipass@3.3.6: resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} engines: {node: '>=8'} @@ -2680,20 +973,6 @@ packages: ufo: 1.5.3 dev: true - /mri@1.2.0: - resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} - engines: {node: '>=4'} - dev: true - - /mrmime@2.0.1: - resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} - engines: {node: '>=10'} - dev: true - - /ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - dev: true - /mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} dependencies: @@ -2707,16 +986,6 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - /nanoid@3.3.8: - resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - dev: true - - /natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - dev: true - /next@14.1.0(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-wlzrsbfeSU48YQBjZhDzOwhWhGsy+uQycR8bHAOt1LY1bn3zZEcDyHQOEoN3aWzQ8LHCAJ1nqrWCc9XF2+O45Q==} engines: {node: '>=18.17.0'} @@ -2786,44 +1055,6 @@ packages: wrappy: 1.0.2 dev: true - /optionator@0.9.4: - resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} - engines: {node: '>= 0.8.0'} - dependencies: - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.4.1 - prelude-ls: 1.2.1 - type-check: 0.4.0 - word-wrap: 1.2.5 - dev: true - - /p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} - dependencies: - yocto-queue: 0.1.0 - dev: true - - /p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} - dependencies: - p-limit: 3.1.0 - dev: true - - /parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} - dependencies: - callsites: 3.1.0 - dev: true - - /path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - dev: true - /path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} @@ -2853,10 +1084,6 @@ packages: /picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} - /picocolors@1.1.1: - resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - dev: true - /picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} @@ -2915,23 +1142,6 @@ packages: postcss: 8.4.35 dev: true - /postcss-load-config@3.1.4(postcss@8.5.2): - resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} - engines: {node: '>= 10'} - peerDependencies: - postcss: '>=8.0.9' - ts-node: '>=9.0.0' - peerDependenciesMeta: - postcss: - optional: true - ts-node: - optional: true - dependencies: - lilconfig: 2.1.0 - postcss: 8.5.2 - yaml: 1.10.2 - dev: true - /postcss-load-config@4.0.2(postcss@8.4.35): resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} engines: {node: '>= 14'} @@ -2959,24 +1169,6 @@ packages: postcss-selector-parser: 6.0.15 dev: true - /postcss-safe-parser@6.0.0(postcss@8.5.2): - resolution: {integrity: sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==} - engines: {node: '>=12.0'} - peerDependencies: - postcss: ^8.3.3 - dependencies: - postcss: 8.5.2 - dev: true - - /postcss-scss@4.0.9(postcss@8.5.2): - resolution: {integrity: sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==} - engines: {node: '>=12.0'} - peerDependencies: - postcss: ^8.4.29 - dependencies: - postcss: 8.5.2 - dev: true - /postcss-selector-parser@6.0.15: resolution: {integrity: sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==} engines: {node: '>=4'} @@ -2985,14 +1177,6 @@ packages: util-deprecate: 1.0.2 dev: true - /postcss-selector-parser@6.1.2: - resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} - engines: {node: '>=4'} - dependencies: - cssesc: 3.0.0 - util-deprecate: 1.0.2 - dev: true - /postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} dev: true @@ -3015,41 +1199,6 @@ packages: source-map-js: 1.0.2 dev: true - /postcss@8.5.2: - resolution: {integrity: sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==} - engines: {node: ^10 || ^12 || >=14} - dependencies: - nanoid: 3.3.8 - picocolors: 1.1.1 - source-map-js: 1.2.1 - dev: true - - /prelude-ls@1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} - engines: {node: '>= 0.8.0'} - dev: true - - /prettier-plugin-svelte@3.3.3(prettier@3.5.1)(svelte@5.20.1): - resolution: {integrity: sha512-yViK9zqQ+H2qZD1w/bH7W8i+bVfKrD8GIFjkFe4Thl6kCT9SlAsXVNmt3jCvQOCsnOhcvYgsoVlRV/Eu6x5nNw==} - peerDependencies: - prettier: ^3.0.0 - svelte: ^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0 - dependencies: - prettier: 3.5.1 - svelte: 5.20.1 - dev: true - - /prettier@3.5.1: - resolution: {integrity: sha512-hPpFQvHwL3Qv5AdRvBFMhnKo4tYxp0ReXiPn2bxkiohEX6mBeBwEpBSQTkD458RaaDKQMYSp4hX4UtfUTA5wDw==} - engines: {node: '>=14'} - hasBin: true - dev: true - - /punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} - dev: true - /query-registry@3.0.1: resolution: {integrity: sha512-M9RxRITi2mHMVPU5zysNjctUT8bAPx6ltEXo/ir9+qmiM47Y7f0Ir3+OxUO5OjYAWdicBQRew7RtHtqUXydqlg==} engines: {node: '>=20'} @@ -3137,20 +1286,6 @@ packages: picomatch: 2.3.1 dev: true - /readdirp@4.1.2: - resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} - engines: {node: '>= 14.18.0'} - dev: true - - /regenerator-runtime@0.14.1: - resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} - dev: false - - /resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} - dev: true - /resolve@1.22.8: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true @@ -3173,68 +1308,18 @@ packages: glob: 7.2.3 dev: true - /rollup@4.34.8: - resolution: {integrity: sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - dependencies: - '@types/estree': 1.0.6 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.34.8 - '@rollup/rollup-android-arm64': 4.34.8 - '@rollup/rollup-darwin-arm64': 4.34.8 - '@rollup/rollup-darwin-x64': 4.34.8 - '@rollup/rollup-freebsd-arm64': 4.34.8 - '@rollup/rollup-freebsd-x64': 4.34.8 - '@rollup/rollup-linux-arm-gnueabihf': 4.34.8 - '@rollup/rollup-linux-arm-musleabihf': 4.34.8 - '@rollup/rollup-linux-arm64-gnu': 4.34.8 - '@rollup/rollup-linux-arm64-musl': 4.34.8 - '@rollup/rollup-linux-loongarch64-gnu': 4.34.8 - '@rollup/rollup-linux-powerpc64le-gnu': 4.34.8 - '@rollup/rollup-linux-riscv64-gnu': 4.34.8 - '@rollup/rollup-linux-s390x-gnu': 4.34.8 - '@rollup/rollup-linux-x64-gnu': 4.34.8 - '@rollup/rollup-linux-x64-musl': 4.34.8 - '@rollup/rollup-win32-arm64-msvc': 4.34.8 - '@rollup/rollup-win32-ia32-msvc': 4.34.8 - '@rollup/rollup-win32-x64-msvc': 4.34.8 - fsevents: 2.3.3 - dev: true - /run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: queue-microtask: 1.2.3 dev: true - /sade@1.8.1: - resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} - engines: {node: '>=6'} - dependencies: - mri: 1.2.0 - dev: true - /scheduler@0.23.0: resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} dependencies: loose-envify: 1.4.0 dev: false - /seedrandom@3.0.5: - resolution: {integrity: sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==} - dev: false - - /semver@7.7.1: - resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} - engines: {node: '>=10'} - hasBin: true - dev: true - - /set-cookie-parser@2.7.1: - resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} - dev: true - /shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -3252,24 +1337,10 @@ packages: engines: {node: '>=14'} dev: true - /sirv@3.0.1: - resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==} - engines: {node: '>=18'} - dependencies: - '@polka/url': 1.0.0-next.28 - mrmime: 2.0.1 - totalist: 3.0.1 - dev: true - /source-map-js@1.0.2: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} - /source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} - engines: {node: '>=0.10.0'} - dev: true - /split-on-first@3.0.0: resolution: {integrity: sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA==} engines: {node: '>=12'} @@ -3317,11 +1388,6 @@ packages: ansi-regex: 6.0.1 dev: true - /strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - dev: true - /styled-jsx@5.1.1(react@18.2.0): resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} engines: {node: '>= 12.0.0'} @@ -3353,74 +1419,11 @@ packages: ts-interface-checker: 0.1.13 dev: true - /supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - dependencies: - has-flag: 4.0.0 - dev: true - /supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} dev: true - /svelte-check@4.1.4(svelte@5.20.1)(typescript@5.3.3): - resolution: {integrity: sha512-v0j7yLbT29MezzaQJPEDwksybTE2Ups9rUxEXy92T06TiA0cbqcO8wAOwNUVkFW6B0hsYHA+oAX3BS8b/2oHtw==} - engines: {node: '>= 18.0.0'} - hasBin: true - peerDependencies: - svelte: ^4.0.0 || ^5.0.0-next.0 - typescript: '>=5.0.0' - dependencies: - '@jridgewell/trace-mapping': 0.3.25 - chokidar: 4.0.3 - fdir: 6.4.3 - picocolors: 1.0.0 - sade: 1.8.1 - svelte: 5.20.1 - typescript: 5.3.3 - transitivePeerDependencies: - - picomatch - dev: true - - /svelte-eslint-parser@0.43.0(svelte@5.20.1): - resolution: {integrity: sha512-GpU52uPKKcVnh8tKN5P4UZpJ/fUDndmq7wfsvoVXsyP+aY0anol7Yqo01fyrlaWGMFfm4av5DyrjlaXdLRJvGA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 - peerDependenciesMeta: - svelte: - optional: true - dependencies: - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - postcss: 8.5.2 - postcss-scss: 4.0.9(postcss@8.5.2) - svelte: 5.20.1 - dev: true - - /svelte@5.20.1: - resolution: {integrity: sha512-aCARru2WTdzJl55Ws8SK27+kvQwd8tijl4kY7NoDUXUHtTHhxMa8Lf6QNZKmU7cuPu3jjFloDO1j5HgYJNIIWg==} - engines: {node: '>=18'} - dependencies: - '@ampproject/remapping': 2.3.0 - '@jridgewell/sourcemap-codec': 1.5.0 - '@types/estree': 1.0.6 - acorn: 8.12.1 - acorn-typescript: 1.4.13(acorn@8.12.1) - aria-query: 5.3.2 - axobject-query: 4.1.0 - clsx: 2.1.1 - esm-env: 1.2.2 - esrap: 1.4.5 - is-reference: 3.0.3 - locate-character: 3.0.0 - magic-string: 0.30.17 - zimmerframe: 1.1.2 - dev: true - /tailwindcss@3.4.1: resolution: {integrity: sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==} engines: {node: '>=14.0.0'} @@ -3452,15 +1455,6 @@ packages: - ts-node dev: true - /tailwindcss@4.0.6: - resolution: {integrity: sha512-mysewHYJKaXgNOW6pp5xon/emCsfAMnO8WMaGKZZ35fomnR/T5gYnRg2/yRTTrtXiEl1tiVkeRt0eMO6HxEZqw==} - dev: true - - /tapable@2.2.1: - resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} - engines: {node: '>=6'} - dev: true - /tar@6.2.1: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} @@ -3486,10 +1480,6 @@ packages: any-promise: 1.3.0 dev: true - /tiny-emitter@2.1.0: - resolution: {integrity: sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==} - dev: false - /to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -3497,20 +1487,6 @@ packages: is-number: 7.0.0 dev: true - /totalist@3.0.1: - resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} - engines: {node: '>=6'} - dev: true - - /ts-api-utils@2.0.1(typescript@5.3.3): - resolution: {integrity: sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==} - engines: {node: '>=18.12'} - peerDependencies: - typescript: '>=4.8.4' - dependencies: - typescript: 5.3.3 - dev: true - /ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} dev: true @@ -3519,39 +1495,11 @@ packages: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} dev: false - /type-check@0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} - engines: {node: '>= 0.8.0'} - dependencies: - prelude-ls: 1.2.1 - dev: true - /type-detect@4.0.8: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} dev: true - /typed-function@4.2.1: - resolution: {integrity: sha512-EGjWssW7Tsk4DGfE+5yluuljS1OGYWiI1J6e8puZz9nTMM51Oug8CD5Zo4gWMsOhq5BI+1bF+rWTm4Vbj3ivRA==} - engines: {node: '>= 18'} - dev: false - - /typescript-eslint@8.24.1(eslint@9.20.1)(typescript@5.3.3): - resolution: {integrity: sha512-cw3rEdzDqBs70TIcb0Gdzbt6h11BSs2pS0yaq7hDWDBtCCSei1pPSUXE9qUdQ/Wm9NgFg8mKtMt1b8fTHIl1jA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.8.0' - dependencies: - '@typescript-eslint/eslint-plugin': 8.24.1(@typescript-eslint/parser@8.24.1)(eslint@9.20.1)(typescript@5.3.3) - '@typescript-eslint/parser': 8.24.1(eslint@9.20.1)(typescript@5.3.3) - '@typescript-eslint/utils': 8.24.1(eslint@9.20.1)(typescript@5.3.3) - eslint: 9.20.1 - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color - dev: true - /typescript@5.3.3: resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} engines: {node: '>=14.17'} @@ -3586,12 +1534,6 @@ packages: picocolors: 1.0.0 dev: true - /uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - dependencies: - punycode: 2.3.1 - dev: true - /url-join@5.0.0: resolution: {integrity: sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -3601,96 +1543,11 @@ packages: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: true - /uuid@10.0.0: - resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} - hasBin: true - dev: true - /validate-npm-package-name@5.0.1: resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dev: true - /vite-plugin-top-level-await@1.5.0(vite@6.1.0): - resolution: {integrity: sha512-r/DtuvHrSqUVk23XpG2cl8gjt1aATMG5cjExXL1BUTcSNab6CzkcPua9BPEc9fuTP5UpwClCxUe3+dNGL0yrgQ==} - peerDependencies: - vite: '>=2.8' - dependencies: - '@rollup/plugin-virtual': 3.0.2 - '@swc/core': 1.10.17 - uuid: 10.0.0 - vite: 6.1.0 - transitivePeerDependencies: - - '@swc/helpers' - - rollup - dev: true - - /vite-plugin-wasm@3.4.1(vite@6.1.0): - resolution: {integrity: sha512-ja3nSo2UCkVeitltJGkS3pfQHAanHv/DqGatdI39ja6McgABlpsZ5hVgl6wuR8Qx5etY3T5qgDQhOWzc5RReZA==} - peerDependencies: - vite: ^2 || ^3 || ^4 || ^5 || ^6 - dependencies: - vite: 6.1.0 - dev: true - - /vite@6.1.0: - resolution: {integrity: sha512-RjjMipCKVoR4hVfPY6GQTgveinjNuyLw+qruksLDvA5ktI1150VmcMBKmQaEWJhg/j6Uaf6dNCNA0AfdzUb/hQ==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} - hasBin: true - peerDependencies: - '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 - jiti: '>=1.21.0' - less: '*' - lightningcss: ^1.21.0 - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' - terser: ^5.16.0 - tsx: ^4.8.1 - yaml: ^2.4.2 - peerDependenciesMeta: - '@types/node': - optional: true - jiti: - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - tsx: - optional: true - yaml: - optional: true - dependencies: - esbuild: 0.24.2 - postcss: 8.5.2 - rollup: 4.34.8 - optionalDependencies: - fsevents: 2.3.3 - dev: true - - /vitefu@1.0.5(vite@6.1.0): - resolution: {integrity: sha512-h4Vflt9gxODPFNGPwp4zAMZRpZR7eslzwH2c5hn5kNZ5rhnKyRJ50U+yGCdc2IRaBs8O4haIgLNGrV5CrpMsCA==} - peerDependencies: - vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 - peerDependenciesMeta: - vite: - optional: true - dependencies: - vite: 6.1.0 - dev: true - /wasm-pack@0.12.1: resolution: {integrity: sha512-dIyKWUumPFsGohdndZjDXRFaokUT/kQS+SavbbiXVAvA/eN4riX5QNdB6AhXQx37zNxluxQkuixZUgJ8adKjOg==} hasBin: true @@ -3708,11 +1565,6 @@ packages: isexe: 2.0.0 dev: true - /word-wrap@1.2.5: - resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} - engines: {node: '>=0.10.0'} - dev: true - /wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -3739,26 +1591,12 @@ packages: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: true - /yaml@1.10.2: - resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} - engines: {node: '>= 6'} - dev: true - /yaml@2.4.0: resolution: {integrity: sha512-j9iR8g+/t0lArF4V6NE/QCfT+CO7iLqrXAHZbJdo+LfjqP1vR8Fg5bSiaq6Q2lOD1AUEVrEVIgABvBFYojJVYQ==} engines: {node: '>= 14'} hasBin: true dev: true - /yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} - dev: true - - /zimmerframe@1.1.2: - resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==} - dev: true - /zod-package-json@1.0.3: resolution: {integrity: sha512-Mb6GzuRyUEl8X+6V6xzHbd4XV0au/4gOYrYP+CAfHL32uPmGswES+v2YqonZiW1NZWVA3jkssCKSU2knonm/aQ==} engines: {node: '>=20'} From 27c05cc0aafb89ccd5e63de9740970505688d396 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 9 Mar 2025 00:32:01 -0700 Subject: [PATCH 117/590] config.toml formatting --- .cargo/config.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index a14c7a91..1ecf5a7b 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,5 +1,5 @@ # Needed for WASM unstable features [build] -rustflags = [ "--cfg=web_sys_unstable_apis" ] -rustdocflags = [ "--cfg=web_sys_unstable_apis" ] +rustflags = ["--cfg=web_sys_unstable_apis"] +rustdocflags = ["--cfg=web_sys_unstable_apis"] target = "wasm32-unknown-unknown" From 1fce035e49751400f32ef3853635a5b4b0cbe6e5 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 9 Mar 2025 00:34:43 -0700 Subject: [PATCH 118/590] Remove debug I don't like --- crates/ratchet-nn/src/kv_cache.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/ratchet-nn/src/kv_cache.rs b/crates/ratchet-nn/src/kv_cache.rs index 5d8a24fc..4d1456e1 100644 --- a/crates/ratchet-nn/src/kv_cache.rs +++ b/crates/ratchet-nn/src/kv_cache.rs @@ -104,7 +104,6 @@ impl KVCache { Ok(mask.clone()) } else { log::debug!("Creating mask for {:?}", t); - log::debug!("masks: {:?}", self.masks); let ones = Tensor::ones::(&shape![t, t], &self.device); let mask = ones.tril(None)?; self.masks.insert(t, mask.clone()); From 923b1e51c63fadd9e0ce384438d07eb65dcc120b Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 9 Mar 2025 00:39:04 -0700 Subject: [PATCH 119/590] Add simple caching sanity check --- crates/ratchet-core/tests/caching.rs | 43 ++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 crates/ratchet-core/tests/caching.rs diff --git a/crates/ratchet-core/tests/caching.rs b/crates/ratchet-core/tests/caching.rs new file mode 100644 index 00000000..c31decc4 --- /dev/null +++ b/crates/ratchet-core/tests/caching.rs @@ -0,0 +1,43 @@ +// TODO(vinhowe): Remove or motivate this test +#[test] +fn test_simple_caching() -> anyhow::Result<()> { + let _ = env_logger::builder().is_test(true).try_init(); + + let device = Device::request_device(DeviceRequest::GPU).unwrap(); + let t1 = Tensor::from_data(vec![1f32], shape![1], Device::CPU) + .to(&device)? + .square()?; + println!("t1: {:?}", t1.to(&Device::CPU)?.to_vec::()?); + + assert_eq!(t1.to(&Device::CPU)?.to_vec::()?, vec![1f32]); + + let t2 = Tensor::from_data(vec![2f32], shape![1], Device::CPU) + .to(&device)? + .square()?; + println!("t2: {:?}", t2.to(&Device::CPU)?.to_vec::()?); + + assert_eq!(t2.to(&Device::CPU)?.to_vec::()?, vec![4f32]); + + let t3 = Tensor::from_data(vec![3f32], shape![1], Device::CPU) + .to(&device)? + .square()?; + println!("t3: {:?}", t3.to(&Device::CPU)?.to_vec::()?); + + assert_eq!(t3.to(&Device::CPU)?.to_vec::()?, vec![9f32]); + + let t4 = Tensor::from_data(vec![4f32], shape![1], Device::CPU) + .to(&device)? + .square()?; + println!("t4: {:?}", t4.to(&Device::CPU)?.to_vec::()?); + + assert_eq!(t4.to(&Device::CPU)?.to_vec::()?, vec![16f32]); + + let t5 = Tensor::from_data(vec![5f32], shape![1], Device::CPU) + .to(&device)? + .square()?; + println!("t5: {:?}", t5.to(&Device::CPU)?.to_vec::()?); + + assert_eq!(t5.to(&Device::CPU)?.to_vec::()?, vec![25f32]); + + Ok(()) +} From ba4596435890d22c56e321f87792fa9d5d026c30 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 9 Mar 2025 00:40:35 -0700 Subject: [PATCH 120/590] Comment this assertion for now; might be important --- .../src/gpu/buffer_allocator/allocator.rs | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/crates/ratchet-core/src/gpu/buffer_allocator/allocator.rs b/crates/ratchet-core/src/gpu/buffer_allocator/allocator.rs index 907c6ea2..aafc74c7 100644 --- a/crates/ratchet-core/src/gpu/buffer_allocator/allocator.rs +++ b/crates/ratchet-core/src/gpu/buffer_allocator/allocator.rs @@ -409,25 +409,25 @@ impl BufferAllocator { assignments.insert(output.id(), output_buffer); } - #[cfg(debug_assertions)] - { - let mut output_allocations = BTreeMap::new(); - for t in execution_order.iter() { - if let Some(allocation) = assignments.get(&t.id()) { - if output_tensors.contains_key(&t.id()) { - output_allocations.insert(allocation.global_id(), t.id()); - } else if let Some(output_id) = output_allocations.get(&allocation.global_id()) - { - panic!( - "Allocation {:?} used by output tensor {:?} was reused by tensor {:?}", - allocation.global_id(), - output_id, - t.id() - ); - } - } - } - } + // #[cfg(debug_assertions)] + // { + // let mut output_allocations = BTreeMap::new(); + // for t in execution_order.iter() { + // if let Some(allocation) = assignments.get(&t.id()) { + // if output_tensors.contains_key(&t.id()) { + // output_allocations.insert(allocation.global_id(), t.id()); + // } else if let Some(output_id) = output_allocations.get(&allocation.global_id()) + // { + // panic!( + // "Allocation {:?} used by output tensor {:?} was reused by tensor {:?}", + // allocation.global_id(), + // output_id, + // t.id() + // ); + // } + // } + // } + // } log::debug!( "Total bytes allocated: {}kb", From 91ac98d2379ab377400092a493ec7ec483cf1ad7 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 9 Mar 2025 00:44:22 -0700 Subject: [PATCH 121/590] Ignore samply profiles --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 3ffe4ce7..eb6fee90 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,6 @@ venv/ # proptest regression tests proptest-regressions/ + +# samply profile +profile.json From 55106ed142c80ccb456f38f66fd83565785a45af Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 9 Mar 2025 00:51:11 -0700 Subject: [PATCH 122/590] Ignore webdriver configs --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index eb6fee90..ed4b9df2 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,6 @@ proptest-regressions/ # samply profile profile.json + +# webdriver configs +webdriver.json From d5713e3649ea9f96d962e6f2604841a1402ca8f5 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 9 Mar 2025 00:52:59 -0700 Subject: [PATCH 123/590] Check in impl of async/future buffer syncing --- crates/ratchet-core/src/executable.rs | 60 +++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/crates/ratchet-core/src/executable.rs b/crates/ratchet-core/src/executable.rs index 8f1fbae3..61a0f549 100644 --- a/crates/ratchet-core/src/executable.rs +++ b/crates/ratchet-core/src/executable.rs @@ -257,6 +257,66 @@ impl Executable { None }; + // let cpu_bufs = if let Some(debug_list) = &self.debug_list { + // let cpu_bufs = Arc::new(RwLock::new(HashMap::with_capacity_and_hasher( + // debug_list.len(), + // Default::default(), + // ))); + + // #[cfg(target_arch = "wasm32")] + // let mut buf_futures = vec![]; + + // // Dump all of our debug results + // for step in debug_steps.iter() { + // let d = device.clone(); + // let dt = debug_list + // .get(&step.tensor_id.expect("Tensor id is not set")) + // .expect("Tensor not found in debug list") + // .dt(); + // let tensor_id = step.tensor_id.expect("Tensor id is not set"); + // let debug_buffer = step.debug_buffer.clone().expect("Debug buffer is not set"); + // let alignment = dt.size_of(); + + // #[maybe_async] + // async fn buffer_fn( + // d: WgpuDevice, + // debug_buffer: Arc, + // alignment: usize, + // ) -> CPUBuffer { + // wgpu_buffer_to_cpu_buffer(debug_buffer.as_ref(), alignment, None, &d).await + // } + + // #[cfg(target_arch = "wasm32")] + // { + // let cpu_bufs = cpu_bufs.clone(); + // let future = async move { + // let cpu_buf = buffer_fn(d, debug_buffer, alignment).await; + // cpu_bufs.write().insert(tensor_id, cpu_buf); + // }; + // buf_futures.push(future); + // } + + // #[cfg(not(target_arch = "wasm32"))] + // { + // let cpu_buf = buffer_fn(d, debug_buffer, alignment); + // cpu_bufs.write().insert(tensor_id, cpu_buf); + // } + // } + + // #[cfg(target_arch = "wasm32")] + // { + // futures::future::join_all(buf_futures).await; + // } + + // log::debug!("Created {} debug cpu buffers", cpu_bufs.read().len()); + + // Some(Arc::try_unwrap(cpu_bufs).unwrap().into_inner()) + // } else { + // None + // }; + + // log::error!("GPU bufs: {:?}", gpu_bufs.as_ref().map(|m| m.len())); + Ok(( index, ExecutionResult { From 39faa93a26d449c1711cf810516cc27c3a7819f6 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 9 Mar 2025 00:53:11 -0700 Subject: [PATCH 124/590] Revert "Check in impl of async/future buffer syncing" This reverts commit 8da3a84311b02467e5fd502bf294a512e841f8b9. --- crates/ratchet-core/src/executable.rs | 60 --------------------------- 1 file changed, 60 deletions(-) diff --git a/crates/ratchet-core/src/executable.rs b/crates/ratchet-core/src/executable.rs index 61a0f549..8f1fbae3 100644 --- a/crates/ratchet-core/src/executable.rs +++ b/crates/ratchet-core/src/executable.rs @@ -257,66 +257,6 @@ impl Executable { None }; - // let cpu_bufs = if let Some(debug_list) = &self.debug_list { - // let cpu_bufs = Arc::new(RwLock::new(HashMap::with_capacity_and_hasher( - // debug_list.len(), - // Default::default(), - // ))); - - // #[cfg(target_arch = "wasm32")] - // let mut buf_futures = vec![]; - - // // Dump all of our debug results - // for step in debug_steps.iter() { - // let d = device.clone(); - // let dt = debug_list - // .get(&step.tensor_id.expect("Tensor id is not set")) - // .expect("Tensor not found in debug list") - // .dt(); - // let tensor_id = step.tensor_id.expect("Tensor id is not set"); - // let debug_buffer = step.debug_buffer.clone().expect("Debug buffer is not set"); - // let alignment = dt.size_of(); - - // #[maybe_async] - // async fn buffer_fn( - // d: WgpuDevice, - // debug_buffer: Arc, - // alignment: usize, - // ) -> CPUBuffer { - // wgpu_buffer_to_cpu_buffer(debug_buffer.as_ref(), alignment, None, &d).await - // } - - // #[cfg(target_arch = "wasm32")] - // { - // let cpu_bufs = cpu_bufs.clone(); - // let future = async move { - // let cpu_buf = buffer_fn(d, debug_buffer, alignment).await; - // cpu_bufs.write().insert(tensor_id, cpu_buf); - // }; - // buf_futures.push(future); - // } - - // #[cfg(not(target_arch = "wasm32"))] - // { - // let cpu_buf = buffer_fn(d, debug_buffer, alignment); - // cpu_bufs.write().insert(tensor_id, cpu_buf); - // } - // } - - // #[cfg(target_arch = "wasm32")] - // { - // futures::future::join_all(buf_futures).await; - // } - - // log::debug!("Created {} debug cpu buffers", cpu_bufs.read().len()); - - // Some(Arc::try_unwrap(cpu_bufs).unwrap().into_inner()) - // } else { - // None - // }; - - // log::error!("GPU bufs: {:?}", gpu_bufs.as_ref().map(|m| m.len())); - Ok(( index, ExecutionResult { From 6c1c2ab7d476927cbadc431563555a2a016333e6 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 9 Mar 2025 00:56:56 -0700 Subject: [PATCH 125/590] Remove moondream impl --- crates/ratchet-models/src/lib.rs | 1 - .../ratchet-models/src/moondream/generate.rs | 207 - crates/ratchet-models/src/moondream/mlp.rs | 18 - crates/ratchet-models/src/moondream/mod.rs | 8 - crates/ratchet-models/src/moondream/model.rs | 284 - .../src/moondream/text_model.rs | 192 - .../src/moondream/vision_encoder.rs | 173 - crates/ratchet-models/src/registry.rs | 3 - crates/ratchet-web/src/model.rs | 34 - examples/ratchet-moondream/.gitignore | 23 - examples/ratchet-moondream/README.md | 30 - examples/ratchet-moondream/package-lock.json | 18080 ---------------- examples/ratchet-moondream/package.json | 43 - examples/ratchet-moondream/public/index.html | 27 - .../ratchet-moondream/public/manifest.json | 25 - examples/ratchet-moondream/public/robots.txt | 3 - examples/ratchet-moondream/src/App.css | 38 - examples/ratchet-moondream/src/App.js | 242 - examples/ratchet-moondream/src/index.css | 13 - examples/ratchet-moondream/src/index.js | 11 - 20 files changed, 19455 deletions(-) delete mode 100644 crates/ratchet-models/src/moondream/generate.rs delete mode 100644 crates/ratchet-models/src/moondream/mlp.rs delete mode 100644 crates/ratchet-models/src/moondream/mod.rs delete mode 100644 crates/ratchet-models/src/moondream/model.rs delete mode 100644 crates/ratchet-models/src/moondream/text_model.rs delete mode 100644 crates/ratchet-models/src/moondream/vision_encoder.rs delete mode 100644 examples/ratchet-moondream/.gitignore delete mode 100644 examples/ratchet-moondream/README.md delete mode 100644 examples/ratchet-moondream/package-lock.json delete mode 100644 examples/ratchet-moondream/package.json delete mode 100644 examples/ratchet-moondream/public/index.html delete mode 100644 examples/ratchet-moondream/public/manifest.json delete mode 100644 examples/ratchet-moondream/public/robots.txt delete mode 100644 examples/ratchet-moondream/src/App.css delete mode 100644 examples/ratchet-moondream/src/App.js delete mode 100644 examples/ratchet-moondream/src/index.css delete mode 100644 examples/ratchet-moondream/src/index.js diff --git a/crates/ratchet-models/src/lib.rs b/crates/ratchet-models/src/lib.rs index 956d80ff..f9ed2527 100644 --- a/crates/ratchet-models/src/lib.rs +++ b/crates/ratchet-models/src/lib.rs @@ -1,6 +1,5 @@ #![allow(clippy::upper_case_acronyms)] pub mod gpt2; -// pub mod moondream; // pub mod phi2; // pub mod phi3; pub mod registry; diff --git a/crates/ratchet-models/src/moondream/generate.rs b/crates/ratchet-models/src/moondream/generate.rs deleted file mode 100644 index 2a97b599..00000000 --- a/crates/ratchet-models/src/moondream/generate.rs +++ /dev/null @@ -1,207 +0,0 @@ -use super::model::Moondream; -use crate::TokenOutputStream; -use ndarray::Axis; -use ndarray_stats::QuantileExt; -use ratchet::shape; -use ratchet::Device; -use ratchet::Tensor; -use ratchet_nn::Module; -use tokenizers::Tokenizer; - -#[cfg(not(target_arch = "wasm32"))] -pub fn generate( - model: &mut Moondream, - image_bytes: &[u8], - question: String, - tokenizer: Tokenizer, - callback: impl Fn(String), -) -> anyhow::Result<()> { - use ratchet::rvec; - use web_time::Instant; - let device = model.text_model.device.clone(); - - let prompt = format!("\n\nQuestion: {}\n\nAnswer:", question); - log::warn!("Prompt: {}", prompt); - - let mut tos = TokenOutputStream::new(tokenizer); - - let img = image::ImageReader::new(std::io::Cursor::new(image_bytes)) - .with_guessed_format()? - .decode() - .unwrap() - .resize_to_fill(378, 378, image::imageops::FilterType::Triangle); // Adjusted to 378x378 - - let pixels: Vec<_> = img - .to_rgb8() - .to_vec() - .iter() - .map(|&x| (x as f32 / 255.0)) - .collect(); - - let img_tensor = Tensor::from_data(pixels, shape![378, 378, 3], device.clone()) - .permute(&[2, 0, 1])? - .view(shape![1, 3, 378, 378])? - .cast(device.compute_precision())?; - - let img_embed = model.vision_encoder.schedule(img_tensor)?.resolve()?; - - let bos_token = model - .text_model - .embedding - .schedule(Tensor::from_data([50256], shape![1], device.clone()))? - .view(shape![1, 1, 2048])?; - - let encoding = tos.tokenizer().encode(prompt, false).unwrap(); - - let mut tokens = encoding - .get_ids() - .iter() - .map(|&x| x as i32) - .collect::>(); - - let mut all_tokens = tokens.clone(); - - let start = Instant::now(); - let mut generated_tokens = vec![]; - while *tokens.last().unwrap() != 50256 { - let input = Tensor::from_data(tokens.clone(), shape![1, tokens.len()], device.clone()); - let mut embeds: Tensor; - if generated_tokens.is_empty() { - embeds = model.text_model.embedding.schedule(input)?; - embeds = Tensor::cat( - rvec![bos_token.clone(), img_embed.clone(), embeds.clone()], - 1, - )?; - } else { - embeds = model.text_model.embedding.schedule(input).unwrap(); - } - - let result = model - .text_model - .schedule(embeds.clone())? - .full()? - .resolve()?; - - model.text_model.cache_mut().update(embeds.shape()[1]); - - let logits = result.to(&Device::CPU).unwrap(); - let next_tokens = logits - .to_ndarray_view::() - .map_axis(Axis(2), |row| row.argmax_skipnan().unwrap()) - .iter() - .map(|&x| x as i32) - .collect::>(); - tokens = next_tokens.clone(); - generated_tokens.extend(next_tokens.clone()); - all_tokens.extend(next_tokens.clone()); - - if let Some(t) = tos.next_token(next_tokens[0] as u32)? { - callback(t); - } - } - - let elapsed = start.elapsed(); - log::warn!("Elapsed: {:?}", elapsed); - log::warn!("Tok/s {}", all_tokens.len() as f64 / elapsed.as_secs_f64()); - model.text_model.reset(); - Ok(()) -} - -#[cfg(target_arch = "wasm32")] -pub async fn generate( - model: &mut Moondream, - image_bytes: Vec, - question: String, - tokenizer: Tokenizer, - callback: impl Fn(String), -) -> anyhow::Result<()> { - use web_time::Instant; - let device = model.text_model.device.clone(); - - let img = image::io::Reader::new(std::io::Cursor::new(image_bytes)) - .with_guessed_format()? - .decode() - .unwrap() - .resize_to_fill(378, 378, image::imageops::FilterType::Triangle); // Adjusted to 378x378 - - let prompt = format!("\n\nQuestion: {}\n\nAnswer:", question); - log::warn!("Prompt: {}", prompt); - - let mut tos = TokenOutputStream::new(tokenizer); - - let pixels: Vec<_> = img - .to_rgb8() - .to_vec() - .iter() - .map(|&x| (x as f32 / 255.0)) - .collect(); - - let img_tensor = Tensor::from_data(&pixels, shape![378, 378, 3], device.clone()) - .permute(&[2, 0, 1])? - .view(shape![1, 3, 378, 378])?; - - let img_embed = model.vision_encoder.schedule(img_tensor)?.resolve()?; - - let bos_token = model - .text_model - .embedding - .schedule(Tensor::from_data([50256], shape![1], device.clone()))? - .view(shape![1, 1, 2048])?; - - let encoding = tos.tokenizer().encode(prompt, false).unwrap(); - - let mut tokens = encoding - .get_ids() - .iter() - .map(|&x| x as i32) - .collect::>(); - - let mut all_tokens = tokens.clone(); - - let start = Instant::now(); - let mut generated_tokens = vec![]; - while *tokens.last().unwrap() != 50256 { - let input = Tensor::from_data(tokens.clone(), shape![1, tokens.len()], device.clone()); - let mut embeds: Tensor; - if generated_tokens.len() == 0 { - embeds = model.text_model.embedding.schedule(input).unwrap(); - embeds = Tensor::cat( - vec![bos_token.clone(), img_embed.clone(), embeds.clone()].into(), - 1, - ) - .unwrap(); - } else { - embeds = model.text_model.embedding.schedule(input).unwrap(); - } - - let result = model - .text_model - .schedule(embeds.clone())? - .full()? - .resolve()?; - - model.text_model.cache_mut().update(embeds.shape()[1]); - - let logits = result.to(&Device::CPU).await.unwrap(); - let next_tokens = logits - .to_ndarray_view::() - .map_axis(Axis(2), |row| row.argmax_skipnan().unwrap()) - .iter() - .map(|&x| x as i32) - .collect::>(); - tokens = next_tokens.clone(); - generated_tokens.extend(next_tokens.clone()); - all_tokens.extend(next_tokens.clone()); - - if let Some(t) = tos.next_token(next_tokens[0] as u32)? { - callback(t); - } - } - - let elapsed = start.elapsed(); - log::warn!("Elapsed: {:?}", elapsed); - log::warn!("Tok/s {}", all_tokens.len() as f64 / elapsed.as_secs_f64()); - println!("Tok/s {}", all_tokens.len() as f64 / elapsed.as_secs_f64()); - model.text_model.reset(); - Ok(()) -} diff --git a/crates/ratchet-models/src/moondream/mlp.rs b/crates/ratchet-models/src/moondream/mlp.rs deleted file mode 100644 index e94c02ec..00000000 --- a/crates/ratchet-models/src/moondream/mlp.rs +++ /dev/null @@ -1,18 +0,0 @@ -use ratchet::Tensor; -use ratchet_nn::{Linear, Module}; - -#[derive(Debug, derive_new::new)] -pub struct MLP { - pub fc1: Linear, - pub fc2: Linear, -} - -impl Module for MLP { - type Input = Tensor; - - fn schedule(&self, input: Self::Input) -> anyhow::Result { - let input_dt = input.dt(); - self.fc2 - .schedule(self.fc1.schedule(input)?.full()?.gelu()?.cast(input_dt)?) - } -} diff --git a/crates/ratchet-models/src/moondream/mod.rs b/crates/ratchet-models/src/moondream/mod.rs deleted file mode 100644 index 02d58c48..00000000 --- a/crates/ratchet-models/src/moondream/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -mod generate; -mod mlp; -pub mod model; -mod text_model; -mod vision_encoder; - -pub use generate::generate; -pub use model::Moondream; diff --git a/crates/ratchet-models/src/moondream/model.rs b/crates/ratchet-models/src/moondream/model.rs deleted file mode 100644 index 37ccfa26..00000000 --- a/crates/ratchet-models/src/moondream/model.rs +++ /dev/null @@ -1,284 +0,0 @@ -use std::io::{BufRead, Seek}; - -use anyhow::Ok; -use half::f16; -use ratchet::{shape, DType, Device, Tensor}; -use ratchet_loader::gguf::gguf::Header; -use ratchet_nn::{Embedding, KVCache, LayerNorm, Linear, RotaryEmbedding}; - -#[cfg(target_arch = "wasm32")] -use {crate::ratchet_from_gguf_web, crate::TensorMap}; - -use super::{ - mlp::MLP, - text_model::{DecoderLayer, SelfAttention, TextModel}, - vision_encoder::{ - Attention, LinearPatchEmbedding, VisionEncoder, VisionProjection, VisionTransformer, - VitBlock, - }, -}; - -#[derive(Debug)] -pub struct Moondream { - pub vision_encoder: VisionEncoder, - pub text_model: TextModel, -} - -impl Moondream { - pub fn load( - header: Header, - reader: &mut R, - device: &Device, - ) -> anyhow::Result { - let lt = |name: &str| header.tensor(reader, name, device).unwrap(); - Self::load_inner(&header, lt, device) - } - - #[cfg(target_arch = "wasm32")] - pub async fn from_web(header: Header, mut tensors: TensorMap) -> anyhow::Result { - let device = Device::request_device(ratchet::DeviceRequest::GPU).await?; - let mut lt = |name: &str| { - let tensor = tensors - .remove(name) - .ok_or_else(|| anyhow::anyhow!("missing tensor")); - ratchet_from_gguf_web(tensor.unwrap(), &device).unwrap() - }; - Self::load_inner(&header, lt, &device) - } - - fn load_inner(_header: &Header, mut lt: F, device: &Device) -> anyhow::Result - where - F: FnMut(&str) -> Tensor, - { - let n_layers = 24_i32; - let dim = 2048_f32; - let n_heads = 32_u32; - let n_kv_heads = 32_u32; - let rope_base = 10000.0f32; - let rope_dim = 32_u32; - let ln_eps = 1e-05; - let hdim = dim / n_heads as f32; - let softmax_scale = Tensor::from_data([1.0 / hdim.sqrt()], shape![1], device.clone()); - let cache_shape = shape![1, 32, 4096, 64]; - - let kv_cache = match device.compute_precision() { - DType::F16 => KVCache::new::(n_layers as _, cache_shape, device), - DType::F32 => KVCache::new::(n_layers as _, cache_shape, device), - _ => unimplemented!(), - }; - - let text_model = TextModel::new( - Embedding::new(lt("text_model.transformer.embd.wte.weight")), - (0..n_layers) - .map(|i| { - DecoderLayer::new( - LayerNorm::new( - lt(&format!("text_model.transformer.h.{}.ln.weight", i)), - Some(lt(&format!("text_model.transformer.h.{}.ln.bias", i))), - ln_eps, - ), - SelfAttention::new( - Linear::new( - lt(&format!("text_model.transformer.h.{}.mixer.Wqkv.weight", i)), - Some(lt(&format!( - "text_model.transformer.h.{}.mixer.Wqkv.bias", - i - ))), - ), - Linear::new( - lt(&format!( - "text_model.transformer.h.{}.mixer.out_proj.weight", - i - )), - Some(lt(&format!( - "text_model.transformer.h.{}.mixer.out_proj.bias", - i - ))), - ), - RotaryEmbedding::new(rope_dim as usize, false, rope_base, 1.0), - n_heads, - softmax_scale.clone(), - n_kv_heads, - ), - MLP::new( - Linear::new( - lt(&format!("text_model.transformer.h.{}.mlp.fc1.weight", i)), - Some(lt(&format!("text_model.transformer.h.{}.mlp.fc1.bias", i))), - ), - Linear::new( - lt(&format!("text_model.transformer.h.{}.mlp.fc2.weight", i)), - Some(lt(&format!("text_model.transformer.h.{}.mlp.fc2.bias", i))), - ), - ), - ) - }) - .collect(), - LayerNorm::new( - lt("text_model.lm_head.ln.weight"), - Some(lt("text_model.lm_head.ln.bias")), - ln_eps, - ), - Linear::new( - lt("text_model.lm_head.linear.weight"), - Some(lt("text_model.lm_head.linear.bias")), - ), - kv_cache, - device.clone(), - ); - - let projection = VisionProjection::new(MLP::new( - Linear::new( - lt("vision_encoder.projection.mlp.fc1.weight"), - Some(lt("vision_encoder.projection.mlp.fc1.bias")), - ), - Linear::new( - lt("vision_encoder.projection.mlp.fc2.weight"), - Some(lt("vision_encoder.projection.mlp.fc2.bias")), - ), - )); - - let transformer = VisionTransformer::new( - LinearPatchEmbedding::new( - Linear::new(lt("vision_encoder.encoder.model.visual.patch_embed.linear.weight"), Some(lt("vision_encoder.encoder.model.visual.patch_embed.linear.bias"))), - ), - lt("vision_encoder.encoder.model.visual.pos_embed"), - (0..27) - .map(|layer| { - let qkvw = lt(&format!("vision_encoder.encoder.model.visual.blocks.{}.attn.qkv.weight", layer)); - let qkvb = lt(&format!("vision_encoder.encoder.model.visual.blocks.{}.attn.qkv.bias", layer)); - - let n_heads = 16; - let dim = 1152; - let h_dim = dim / n_heads; - let scale_factor = - Tensor::from_data([1.0 / (h_dim as f32).sqrt()], shape![1], device.clone()); - - VitBlock::new( - 1152, - Attention::new( - n_heads, - dim, - Linear::new(qkvw, Some(qkvb)), - Linear::new( - lt(&format!("vision_encoder.encoder.model.visual.blocks.{}.attn.proj.weight", layer)), - Some(lt(&format!("vision_encoder.encoder.model.visual.blocks.{}.attn.proj.bias", layer))), - ), - scale_factor, - ), - MLP::new( - Linear::new( - lt(&format!("vision_encoder.encoder.model.visual.blocks.{}.mlp.fc1.weight", layer)), - Some(lt(&format!("vision_encoder.encoder.model.visual.blocks.{}.mlp.fc1.bias", layer))), - ), - Linear::new( - lt(&format!("vision_encoder.encoder.model.visual.blocks.{}.mlp.fc2.weight", layer)), - Some(lt(&format!("vision_encoder.encoder.model.visual.blocks.{}.mlp.fc2.bias", layer))), - ), - ), - LayerNorm::new( - lt(&format!("vision_encoder.encoder.model.visual.blocks.{}.norm1.weight", layer)), - Some(lt(&format!("vision_encoder.encoder.model.visual.blocks.{}.norm1.bias", layer))), - ln_eps, - ), - LayerNorm::new( - lt(&format!("vision_encoder.encoder.model.visual.blocks.{}.norm2.weight", layer)), - Some(lt(&format!("vision_encoder.encoder.model.visual.blocks.{}.norm2.bias", layer))), - ln_eps, - ), - ) - }).collect::>(), - LayerNorm::new(lt("vision_encoder.encoder.model.visual.norm.weight"), Some(lt("vision_encoder.encoder.model.visual.norm.bias")), ln_eps), - ); - - let vision_encoder = VisionEncoder::new(projection, transformer); - Ok(Self { - vision_encoder, - text_model, - }) - } -} - -#[cfg(all(test, not(target_arch = "wasm32"), feature = "pyo3"))] -mod tests { - use std::fs; - - use anyhow::Ok; - use hf_hub::api::sync::Api; - use ratchet::{shape, test_util::run_py_prg, Device, DeviceRequest, Tensor}; - use ratchet_loader::gguf; - use ratchet_nn::Module; - use tokenizers::Tokenizer; - - use crate::moondream::{ - generate::generate, text_model::TextModel, vision_encoder::VisionEncoder, - }; - - use super::Moondream; - - fn vision_ground_truth(tensor: Tensor) -> anyhow::Result { - let prg = r#" -from transformers import AutoModelForCausalLM, AutoTokenizer -import torch - -def ground(*args): - tensor = torch.from_numpy(args[0]) - model_id = "vikhyatk/moondream2" - revision = "2024-05-20" - model = AutoModelForCausalLM.from_pretrained( - model_id, trust_remote_code=True, revision=revision - ) - return model.encode_image(tensor).numpy() -"#; - - run_py_prg(prg.to_string(), &[&tensor], &[], ratchet::DType::F32) - } - - #[test] - #[cfg_attr(feature = "ci", ignore)] - fn moondream_encoder() -> anyhow::Result<()> { - let device = Device::request_device(DeviceRequest::GPU).unwrap(); - let api = Api::new().unwrap(); - let model_repo = api.model("ratchet-community/ratchet-moondream-2".to_string()); - let model_path = model_repo.get("moondream_f32.gguf").unwrap(); - let mut reader = std::io::BufReader::new(std::fs::File::open(model_path).unwrap()); - let content = gguf::gguf::Header::read(&mut reader).unwrap(); - let model = Moondream::load(content, &mut reader, &device).unwrap(); - - let input = Tensor::randn::(shape![1, 3, 378, 378], device); - let ours = model - .vision_encoder - .schedule(input.clone())? - .resolve()? - .to(&Device::CPU)?; - let theirs = vision_ground_truth(input.to(&Device::CPU).unwrap()).unwrap(); - ours.all_close(&theirs, 1e-1, 1e-1).unwrap(); - Ok(()) - } - - #[test] - #[cfg_attr(feature = "ci", ignore)] - fn moondream_end_to_end() { - let device = Device::request_device(DeviceRequest::GPU).unwrap(); - let api = Api::new().unwrap(); - let model_repo = api.model("ratchet-community/ratchet-moondream-2".to_string()); - let model_path = model_repo.get("moondream_q8_0.gguf").unwrap(); - let mut reader = std::io::BufReader::new(std::fs::File::open(model_path).unwrap()); - let content = gguf::gguf::Header::read(&mut reader).unwrap(); - let mut model = Moondream::load(content, &mut reader, &device).unwrap(); - - let tokenizer_path = model_repo.get("tokenizer.json").unwrap(); - let tokenizer = Tokenizer::from_file(tokenizer_path).unwrap(); - - let img_path = model_repo.get("demo.jpg").unwrap(); - let img = fs::read(img_path).unwrap(); - - generate( - &mut model, - &img, - "What is happening here?".to_owned(), - tokenizer, - |token| print!("{}", token), - ) - .unwrap(); - } -} diff --git a/crates/ratchet-models/src/moondream/text_model.rs b/crates/ratchet-models/src/moondream/text_model.rs deleted file mode 100644 index 57bb4a91..00000000 --- a/crates/ratchet-models/src/moondream/text_model.rs +++ /dev/null @@ -1,192 +0,0 @@ -use ratchet::{rvec, shape, Device, Tensor}; -use ratchet_nn::{ - Embedding, KVCache, KVEntry, LayerNorm, Linear, Module, RotaryEmbedding, RotaryInput, -}; - -use super::mlp::MLP; - -#[derive(Debug, derive_new::new)] -pub struct SelfAttention { - qkv: Linear, - o: Linear, - rope: RotaryEmbedding, - n_heads: u32, - softmax_scale: Tensor, - n_kv_heads: u32, -} - -pub struct AttnInput { - pub input: Tensor, - pub mask: Option, - pub kv_cache: Option, -} - -impl Module for SelfAttention { - type Input = AttnInput; - - fn schedule(&self, input: Self::Input) -> anyhow::Result { - let AttnInput { - input, - mask, - kv_cache, - } = input; - let [batch_size, q_len, n_state]: [usize; 3] = input.shape().try_into()?; - - let hdim = n_state / self.n_heads as usize; - let kv_x_hdim = self.n_kv_heads as usize * hdim; - - let qkv = self.qkv.schedule(input)?; - let query_pos = self.n_heads as usize * hdim; - let key_pos = query_pos + kv_x_hdim; - let value_pos = key_pos + kv_x_hdim; - - let query_states = qkv - .clone() - .slice(&[0..batch_size, 0..q_len, 0..query_pos])?; - let key_states = qkv - .clone() - .slice(&[0..batch_size, 0..q_len, query_pos..key_pos])?; - let value_states = qkv - .clone() - .slice(&[0..batch_size, 0..q_len, key_pos..value_pos])?; - - let q_shape = shape![batch_size as _, q_len, self.n_heads as _, hdim]; - let kv_shape = shape![batch_size as _, q_len, self.n_kv_heads as _, hdim]; - - let query_states = query_states.view(q_shape)?.permute(&[0, 2, 1, 3])?; - let key_states = key_states.view(kv_shape.clone())?.permute(&[0, 2, 1, 3])?; - let value_states = value_states.view(kv_shape)?.permute(&[0, 2, 1, 3])?; - - let offset = kv_cache.as_ref().map(|kv| kv.entries).unwrap_or(0); - let q_dt = query_states.dt(); - let query_states = self - .rope - .schedule(RotaryInput { - input: query_states.full()?, - offset, - })? - .cast(q_dt)?; - let key_states = self - .rope - .schedule(RotaryInput { - input: key_states.full()?, - offset, - })? - .cast(q_dt)?; - - let (key_states, value_states) = if let Some(kv) = kv_cache { - let k_cache = kv.k_cache.cache(key_states, 2, offset)?; - let v_cache = kv.v_cache.cache(value_states, 2, offset)?; - (k_cache, v_cache) - } else { - (key_states, value_states) - }; - - let mut attn_weights = query_states - .full()? - .matmul(key_states.full()?, false, true)? - .mul(self.softmax_scale.clone())? - .cast(q_dt)?; - - if let Some(m) = mask { - let attn_dt = attn_weights.dt(); - attn_weights = attn_weights.add(m.cast(attn_dt)?)?; - } - - let w = attn_weights.full()?.softmax(3)?.cast(value_states.dt())?; - let wv = w - .matmul(value_states, false, false)? - .permute(&[0, 2, 1, 3])?; - let wv = wv.view(shape![batch_size as _, q_len, n_state])?; - self.o.schedule(wv) - } -} - -#[derive(Debug, derive_new::new)] -pub struct DecoderLayer { - pub ln: LayerNorm, - pub self_attn: SelfAttention, - pub mlp: MLP, -} - -#[derive(Debug)] -pub struct DecoderLayerInput { - pub x: Tensor, - pub mask: Option, - pub kv_cache: Option, -} - -impl Module for DecoderLayer { - type Input = DecoderLayerInput; - - fn schedule(&self, input: Self::Input) -> anyhow::Result { - let DecoderLayerInput { x, mask, kv_cache } = input; - let residual = x.clone(); - let xs = self.ln.schedule(x)?; - let attn_output = self.self_attn.schedule(AttnInput { - input: xs.clone(), - mask, - kv_cache, - })?; - let ff_hs = self.mlp.schedule(xs)?; - attn_output.add(ff_hs)?.add(residual) - } -} - -#[derive(Debug, derive_new::new)] -pub struct TextModel { - pub embedding: Embedding, - pub layers: Vec, - pub ln_post: LayerNorm, - pub lm_head: Linear, - pub kv_cache: KVCache, - pub device: Device, -} - -impl Module for TextModel { - type Input = Tensor; - - fn schedule(&self, input: Self::Input) -> anyhow::Result { - let mut x = input.clone(); - let [_, seq_len, n_state]: [usize; 3] = x.shape().try_into()?; - let mask = if seq_len <= 1 { - None - } else { - Some(Self::generate_mask(seq_len, x.device())?) - }; - - for (i, layer) in self.layers.iter().enumerate() { - let input = DecoderLayerInput { - x, - mask: mask.clone(), - kv_cache: Some(self.kv_cache[i].clone()), - }; - x = layer.schedule(input)?; - } - x = self.ln_post.schedule(x)?; - x = x.slice(&[0..1, seq_len - 1..seq_len, 0..n_state])?; - let logits = self.lm_head.schedule(x)?; - Ok(logits) - } -} - -impl TextModel { - pub fn generate_mask(seq_len: usize, device: &Device) -> anyhow::Result { - let mask: Vec<_> = (0..seq_len) - .flat_map(|i| (0..seq_len).map(move |j| if j > i { f32::NEG_INFINITY } else { 0f32 })) - .collect(); - - Ok(Tensor::from_data( - mask, - shape![seq_len, seq_len], - device.clone(), - )) - } - - pub fn cache_mut(&mut self) -> &mut KVCache { - &mut self.kv_cache - } - pub fn reset(&mut self) { - self.kv_cache.reset(); - } -} diff --git a/crates/ratchet-models/src/moondream/vision_encoder.rs b/crates/ratchet-models/src/moondream/vision_encoder.rs deleted file mode 100644 index 26eb9c3a..00000000 --- a/crates/ratchet-models/src/moondream/vision_encoder.rs +++ /dev/null @@ -1,173 +0,0 @@ -use ratchet::{prelude::shape, rvec, Tensor}; -use ratchet_nn::{LayerNorm, Linear, Module}; - -use super::mlp::MLP; - -#[derive(Debug, derive_new::new)] -pub struct Attention { - n_heads: usize, - dim: usize, - qkv: Linear, - proj: Linear, - scale_factor: Tensor, -} - -impl Module for Attention { - type Input = Tensor; - - fn schedule(&self, input: Self::Input) -> anyhow::Result { - let h_dim = self.dim / self.n_heads; - let [b, n, c]: [usize; 3] = input.shape().try_into()?; - // step 1 - 0, 1, 2, 3, 4 - // step 2 - 0, 2, 1, 3, 4 - // step 3 - 2, 0, 1, 3, 4 - // step 4 - 2, 0, 3, 1, 4 - - // b, n, 3, nh, hd - let mut qkv = self.qkv.schedule(input.clone())?; - // b, 3, n, nh, hd - qkv = qkv - .view(shape![b, n, 3, self.n_heads * h_dim])? - .permute(&[0, 2, 1, 3])?; - // 3, b, n, nh, hd - qkv = qkv - .view(shape![b, 3, n * self.n_heads * h_dim])? - .permute(&[1, 0, 2])?; - // 3, b, nh, n, hd - qkv = qkv - .view(shape![3 * b, n, self.n_heads, h_dim])? - .permute(&[0, 2, 1, 3])? - .view(shape![3, b * self.n_heads * n * h_dim])?; - - let q = qkv - .clone() - .slice(&[0..1, 0..(b * self.n_heads * n * h_dim)])? - .view(shape![b, self.n_heads, n, h_dim])?; - let k = qkv - .clone() - .slice(&[1..2, 0..(b * self.n_heads * n * h_dim)])? - .view(shape![b, self.n_heads, n, h_dim])?; - let v = qkv - .clone() - .slice(&[2..3, 0..(b * self.n_heads * n * h_dim)])? - .view(shape![b, self.n_heads, n, h_dim])?; - - // scaled dot-product attention - let mut attn_weights = q - .full()? - .matmul(k.permute(&[0, 1, 3, 2])?.full()?, false, false)? - .mul(self.scale_factor.clone())?; - attn_weights = attn_weights.softmax(3)?.cast(v.dt())?; - let mut x = attn_weights.matmul(v, false, false)?; - x = x.permute(&[0, 2, 1, 3])?.view(shape![b, n, c])?; - self.proj.schedule(x) - } -} - -#[derive(Debug, derive_new::new)] -pub struct VitBlock { - embed_dim: usize, - attn: Attention, - mlp: MLP, - norm1: LayerNorm, - norm2: LayerNorm, -} - -impl Module for VitBlock { - type Input = Tensor; - - fn schedule(&self, input: Self::Input) -> anyhow::Result { - let x = input - .clone() - .add(self.attn.schedule(self.norm1.schedule(input)?)?)?; - x.clone().add(self.mlp.schedule(self.norm2.schedule(x)?)?) - } -} - -#[derive(Debug, derive_new::new)] -pub struct LinearPatchEmbedding { - linear: Linear, -} - -impl Module for LinearPatchEmbedding { - type Input = Tensor; - - fn schedule(&self, input: Self::Input) -> anyhow::Result { - let [b, c, hp1, wp2]: [usize; 4] = input.shape().try_into()?; - let (p1, p2) = (14, 14); - let (h, w) = (hp1 / p1, wp2 / p2); - // step 1 - 0, 1, 2, 3, 4, 5 - // step 2 - 0, 2, 1, 3, 4, 5 - // step 3 - 0, 2, 1, 4, 3, 5 - // step 4 - 0, 2, 4, 1, 3, 5 - - // b, c, h, p1, w, p2 - let mut x = input - .view(shape![b, c, h, p1 * w * p2])? - .permute(&[0, 2, 1, 3])?; - // b, h, c, p1, w, p2 - x = x - .view(shape![b * h * c, p1, w, p2])? - .permute(&[0, 2, 1, 3])?; - // b, h, c, w, p1, p2 - x = x - .view(shape![b * h, c, w, p1 * p2])? - .permute(&[0, 2, 1, 3])?; - // b, h, w, c, p1, p2 - x = x.view(shape![b, h * w, c * p1 * p2])?; - self.linear.schedule(x) - } -} - -#[derive(Debug, derive_new::new)] -pub struct VisionTransformer { - patch_embed: LinearPatchEmbedding, - pos_embed: Tensor, - blocks: Vec, - norm: LayerNorm, -} - -impl Module for VisionTransformer { - type Input = Tensor; - - fn schedule(&self, input: Self::Input) -> anyhow::Result { - let mut x = self.patch_embed.schedule(input)?; - x = x.clone().add(self.pos_embed.clone())?; - x = self - .blocks - .iter() - .fold(x.clone(), |acc, blk| blk.schedule(acc).unwrap()); - self.norm.schedule(x) - } -} - -#[derive(Debug, derive_new::new)] -pub struct VisionProjection { - mlp: MLP, -} - -impl Module for VisionProjection { - type Input = Tensor; - - fn schedule(&self, input: Self::Input) -> anyhow::Result { - self.mlp.schedule(input) - } -} - -#[derive(Debug, derive_new::new)] -pub struct VisionEncoder { - projection: VisionProjection, - transformer: VisionTransformer, -} - -impl Module for VisionEncoder { - type Input = Tensor; - - fn schedule(&self, input: Self::Input) -> anyhow::Result { - let transformed = self.transformer.schedule(input)?; - self.projection.schedule(Tensor::cat( - rvec![transformed.clone(), transformed.clone()], - 2, - )?) - } -} diff --git a/crates/ratchet-models/src/registry.rs b/crates/ratchet-models/src/registry.rs index a493664c..2143ceef 100644 --- a/crates/ratchet-models/src/registry.rs +++ b/crates/ratchet-models/src/registry.rs @@ -32,7 +32,6 @@ pub enum PhiVariants { #[cfg_attr(target_arch = "wasm32", tsify(from_wasm_abi))] pub enum AvailableModels { Phi(PhiVariants), - Moondream, } impl AvailableModels { @@ -42,7 +41,6 @@ impl AvailableModels { PhiVariants::Phi2 => "FL33TW00D-HF/phi2", PhiVariants::Phi3 => "FL33TW00D-HF/phi3", }, - AvailableModels::Moondream => "ratchet-community/ratchet-moondream-2", }; id.to_string() } @@ -53,7 +51,6 @@ impl AvailableModels { PhiVariants::Phi2 => "phi2", PhiVariants::Phi3 => "phi3-mini-4k", }, - AvailableModels::Moondream => "moondream", }; match quantization { Quantization::Q8_0 => format!("{}_q8_0.gguf", model_stem), diff --git a/crates/ratchet-web/src/model.rs b/crates/ratchet-web/src/model.rs index e0df45da..3e4cb789 100644 --- a/crates/ratchet-web/src/model.rs +++ b/crates/ratchet-web/src/model.rs @@ -3,7 +3,6 @@ use futures::stream::TryStreamExt; use futures::StreamExt; use ratchet_hub::{Api, ApiBuilder, RepoType}; use ratchet_loader::gguf::gguf::{self, Header, TensorInfo}; -use ratchet_models::moondream::{self, Moondream}; use ratchet_models::phi2; use ratchet_models::phi2::Phi2; use ratchet_models::phi3::{self, Phi3}; @@ -16,7 +15,6 @@ use wasm_bindgen::prelude::*; pub enum WebModel { Phi2(Phi2), Phi3(Phi3), - Moondream(Moondream), } impl WebModel { @@ -54,26 +52,6 @@ impl WebModel { .unwrap(); Ok(JsValue::NULL) } - WebModel::Moondream(model) => { - let input: MoondreamInputs = serde_wasm_bindgen::from_value(input)?; - let rs_callback = |output: String| { - let _ = input.callback.call1(&JsValue::NULL, &output.into()); - }; - let model_repo = - ApiBuilder::from_hf("tgestson/ratchet-moondream2", RepoType::Model).build(); - let model_bytes = model_repo.get("tokenizer.json").await?; - let tokenizer = Tokenizer::from_bytes(model_bytes.to_vec()).unwrap(); - moondream::generate( - model, - input.image_bytes, - input.question, - tokenizer, - rs_callback, - ) - .await - .unwrap(); - Ok(JsValue::NULL) - } } } @@ -93,10 +71,6 @@ impl WebModel { Ok(WebModel::Phi3(model)) } }, - AvailableModels::Moondream => { - let model = Moondream::from_web(header, tensor_map).await?; - Ok(WebModel::Moondream(model)) - } _ => Err(anyhow::anyhow!("Unknown model type")), } } @@ -109,14 +83,6 @@ pub struct PhiInputs { pub callback: js_sys::Function, } -#[derive(serde::Serialize, serde::Deserialize)] -pub struct MoondreamInputs { - pub question: String, - pub image_bytes: Vec, - #[serde(with = "serde_wasm_bindgen::preserve")] - pub callback: js_sys::Function, -} - #[wasm_bindgen] #[derive(Debug)] pub struct Model { diff --git a/examples/ratchet-moondream/.gitignore b/examples/ratchet-moondream/.gitignore deleted file mode 100644 index 4d29575d..00000000 --- a/examples/ratchet-moondream/.gitignore +++ /dev/null @@ -1,23 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js - -# testing -/coverage - -# production -/build - -# misc -.DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local - -npm-debug.log* -yarn-debug.log* -yarn-error.log* diff --git a/examples/ratchet-moondream/README.md b/examples/ratchet-moondream/README.md deleted file mode 100644 index 5e7cf25e..00000000 --- a/examples/ratchet-moondream/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# Getting Started with Create React App - -This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). - -## Available Scripts - -In the project directory, you can run: - -### `npm start` - -Runs the app in the development mode.\ -Open [http://localhost:3000](http://localhost:3000) to view it in your browser. - -The page will reload when you make changes.\ -You may also see any lint errors in the console. - -### `npm test` - -Launches the test runner in the interactive watch mode.\ -See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. - -### `npm run build` - -Builds the app for production to the `build` folder.\ -It correctly bundles React in production mode and optimizes the build for the best performance. - -The build is minified and the filenames include the hashes.\ -Your app is ready to be deployed! - -See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. diff --git a/examples/ratchet-moondream/package-lock.json b/examples/ratchet-moondream/package-lock.json deleted file mode 100644 index 06febf98..00000000 --- a/examples/ratchet-moondream/package-lock.json +++ /dev/null @@ -1,18080 +0,0 @@ -{ - "name": "ratchet-moondream", - "version": "0.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "ratchet-moondream", - "version": "0.1.0", - "dependencies": { - "@emotion/react": "^11.11.4", - "@emotion/styled": "^11.11.5", - "@mui/icons-material": "^5.15.19", - "@mui/material": "^5.15.19", - "@ratchet-ml/ratchet-web": "file:../../target/pkg/ratchet-web", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "react-scripts": "5.0.1", - "web-vitals": "^2.1.4" - }, - "devDependencies": { - "prettier": "3.3.1" - } - }, - "../../target/pkg/ratchet-web": { - "name": "@ratchet/ratchet-web", - "version": "0.3.0", - "license": "MIT" - }, - "node_modules/@alloc/quick-lru": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", - "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", - "dependencies": { - "@babel/highlight": "^7.24.7", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.7.tgz", - "integrity": "sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz", - "integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helpers": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/eslint-parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.24.7.tgz", - "integrity": "sha512-SO5E3bVxDuxyNxM5agFv480YA2HO6ohZbGxbazZdIk3KQOPOGVNw6q78I9/lbviIf95eq6tPozeYnJLbjnC8IA==", - "dependencies": { - "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", - "eslint-visitor-keys": "^2.1.0", - "semver": "^6.3.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || >=14.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.11.0", - "eslint": "^7.5.0 || ^8.0.0 || ^9.0.0" - } - }, - "node_modules/@babel/eslint-parser/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "engines": { - "node": ">=10" - } - }, - "node_modules/@babel/eslint-parser/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz", - "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==", - "dependencies": { - "@babel/types": "^7.24.7", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.24.7.tgz", - "integrity": "sha512-xZeCVVdwb4MsDBkkyZ64tReWYrLRHlMN72vP7Bdm3OUOuyFZExhsHUUnuWnm2/XOlAJzR0LfPpB56WXZn0X/lA==", - "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.7.tgz", - "integrity": "sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==", - "dependencies": { - "@babel/compat-data": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "browserslist": "^4.22.2", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.7.tgz", - "integrity": "sha512-kTkaDl7c9vO80zeX1rJxnuRpEsD5tA81yh11X1gQo+PhSti3JS+7qeZo9U4RHobKRiFPKaGK3svUAeb8D0Q7eg==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-member-expression-to-functions": "^7.24.7", - "@babel/helper-optimise-call-expression": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.24.7.tgz", - "integrity": "sha512-03TCmXy2FtXJEZfbXDTSqq1fRJArk7lX9DOFC/47VthYcxyIOx+eXQmdo6DOQvrbpIix+KfXwvuXdFDZHxt+rA==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "regexpu-core": "^5.3.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", - "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", - "dependencies": { - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", - "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", - "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", - "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", - "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.7.tgz", - "integrity": "sha512-LGeMaf5JN4hAT471eJdBs/GK1DoYIJ5GCtZN/EsL6KUiiDZOvO/eKE11AMZJa2zP4zk4qe9V2O/hxAmkRc8p6w==", - "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", - "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", - "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.7.tgz", - "integrity": "sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==", - "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.24.7.tgz", - "integrity": "sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A==", - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz", - "integrity": "sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.24.7.tgz", - "integrity": "sha512-9pKLcTlZ92hNZMQfGCHImUpDOlAgkkpqalWEeftW5FBya75k8Li2ilerxkM/uBEj01iBZXcCIB/bwvDYgWyibA==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-wrap-function": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.7.tgz", - "integrity": "sha512-qTAxxBM81VEyoAY0TtLrx1oAEJc09ZK67Q9ljQToqCnA+55eNwCORaxlKyu+rNfX86o8OXRUSNUnrtsAZXM9sg==", - "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-member-expression-to-functions": "^7.24.7", - "@babel/helper-optimise-call-expression": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", - "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", - "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.7.tgz", - "integrity": "sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ==", - "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", - "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz", - "integrity": "sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz", - "integrity": "sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-wrap-function": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.24.7.tgz", - "integrity": "sha512-N9JIYk3TD+1vq/wn77YnJOqMtfWhNewNE+DJV4puD2X7Ew9J4JvrzrFDfTfyv5EgEXVy9/Wt8QiOErzEmv5Ifw==", - "dependencies": { - "@babel/helper-function-name": "^7.24.7", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.7.tgz", - "integrity": "sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==", - "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", - "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", - "dependencies": { - "@babel/helper-validator-identifier": "^7.24.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", - "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.24.7.tgz", - "integrity": "sha512-TiT1ss81W80eQsN+722OaeQMY/G4yTb4G9JrqeiDADs3N8lbPMGldWi9x8tyqCW5NLx1Jh2AvkE6r6QvEltMMQ==", - "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.24.7.tgz", - "integrity": "sha512-unaQgZ/iRu/By6tsjMZzpeBZjChYfLYry6HrEXPoz3KmfF0sVBQ1l8zKMQ4xRGLWVsjuvB8nQfjNP/DcfEOCsg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.7.tgz", - "integrity": "sha512-+izXIbke1T33mY4MSNnrqhPXDz01WYhEf3yF5NbnUtkiNnm+XBZJl3kNfoK6NKmYlz/D07+l2GWVK/QfDkNCuQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.13.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.24.7.tgz", - "integrity": "sha512-utA4HuR6F4Vvcr+o4DnjL8fCOlgRFGbeeBEGNg3ZTrLFw6VWG5XmUrvcQ0FjIYMU2ST4XcR2Wsp7t9qOAPnxMg==", - "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-proposal-class-properties": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", - "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-decorators": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.24.7.tgz", - "integrity": "sha512-RL9GR0pUG5Kc8BUWLNDm2T5OpYwSX15r98I0IkgmRQTXuELq/OynH8xtMTMvTJFjXbMWFVTKtYkTaYQsuAwQlQ==", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-decorators": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", - "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead.", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-numeric-separator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", - "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-numeric-separator instead.", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-optional-chaining": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", - "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead.", - "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-private-methods": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", - "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead.", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-decorators": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.24.7.tgz", - "integrity": "sha512-Ui4uLJJrRV1lb38zg1yYTmRKmiZLiftDEvZN2iq3kd9kUFU+PttmzTbAFC2ucRk/XJmtek6G23gPsuZbhrT8fQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-flow": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.24.7.tgz", - "integrity": "sha512-9G8GYT/dxn/D1IIKOUBmGX0mnmj46mGH9NnZyJLwtCpgh5f7D2VbuKodb+2s9m1Yavh1s7ASQN8lf0eqrb1LTw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.7.tgz", - "integrity": "sha512-Ec3NRUMoi8gskrkBe3fNmEQfxDvY8bgfQpz6jlk/41kX9eUjvpyqWU7PBP/pLAvMaSQjbMNKJmvX57jP+M6bPg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.7.tgz", - "integrity": "sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz", - "integrity": "sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.7.tgz", - "integrity": "sha512-c/+fVeJBB0FeKsFvwytYiUD+LBvhHjGSI0g446PRGdSVGZLRNArBUno2PETbAly3tpiNAQR5XaZ+JslxkotsbA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-unicode-sets-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", - "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.7.tgz", - "integrity": "sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.24.7.tgz", - "integrity": "sha512-o+iF77e3u7ZS4AoAuJvapz9Fm001PuD2V3Lp6OSE4FYQke+cSewYtnek+THqGRWyQloRCyvWL1OkyfNEl9vr/g==", - "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-remap-async-to-generator": "^7.24.7", - "@babel/plugin-syntax-async-generators": "^7.8.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.7.tgz", - "integrity": "sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA==", - "dependencies": { - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-remap-async-to-generator": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.7.tgz", - "integrity": "sha512-yO7RAz6EsVQDaBH18IDJcMB1HnrUn2FJ/Jslc/WtPPWcjhpUJXU/rjbwmluzp7v/ZzWcEhTMXELnnsz8djWDwQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.7.tgz", - "integrity": "sha512-Nd5CvgMbWc+oWzBsuaMcbwjJWAcp5qzrbg69SZdHSP7AMY0AbWFqFO0WTFCA1jxhMCwodRwvRec8k0QUbZk7RQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.7.tgz", - "integrity": "sha512-vKbfawVYayKcSeSR5YYzzyXvsDFWU2mD8U5TFeXtbCPLFUqe7GyCgvO6XDHzje862ODrOwy6WCPmKeWHbCFJ4w==", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.7.tgz", - "integrity": "sha512-HMXK3WbBPpZQufbMG4B46A90PkuuhN9vBCb5T8+VAHqvAqvcLi+2cKoukcpmUYkszLhScU3l1iudhrks3DggRQ==", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.7.tgz", - "integrity": "sha512-CFbbBigp8ln4FU6Bpy6g7sE8B/WmCmzvivzUC6xDAdWVsjYTXijpuuGJmYkAaoWAzcItGKT3IOAbxRItZ5HTjw==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.7.tgz", - "integrity": "sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/template": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.7.tgz", - "integrity": "sha512-19eJO/8kdCQ9zISOf+SEUJM/bAUIsvY3YDnXZTupUCQ8LgrWnsG/gFB9dvXqdXnRXMAM8fvt7b0CBKQHNGy1mw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.7.tgz", - "integrity": "sha512-ZOA3W+1RRTSWvyqcMJDLqbchh7U4NRGqwRfFSVbOLS/ePIP4vHB5e8T8eXcuqyN1QkgKyj5wuW0lcS85v4CrSw==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.7.tgz", - "integrity": "sha512-JdYfXyCRihAe46jUIliuL2/s0x0wObgwwiGxw/UbgJBr20gQBThrokO4nYKgWkD7uBaqM7+9x5TU7NkExZJyzw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.7.tgz", - "integrity": "sha512-sc3X26PhZQDb3JhORmakcbvkeInvxz+A8oda99lj7J60QRuPZvNAk9wQlTBS1ZynelDrDmTU4pw1tyc5d5ZMUg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.7.tgz", - "integrity": "sha512-Rqe/vSc9OYgDajNIK35u7ot+KeCoetqQYFXM4Epf7M7ez3lWlOjrDjrwMei6caCVhfdw+mIKD4cgdGNy5JQotQ==", - "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.7.tgz", - "integrity": "sha512-v0K9uNYsPL3oXZ/7F9NNIbAj2jv1whUEtyA6aujhekLs56R++JDQuzRcP2/z4WX5Vg/c5lE9uWZA0/iUoFhLTA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-flow-strip-types": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.24.7.tgz", - "integrity": "sha512-cjRKJ7FobOH2eakx7Ja+KpJRj8+y+/SiB3ooYm/n2UJfxu0oEaOoxOinitkJcPqv9KxS0kxTGPUaR7L2XcXDXA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-flow": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.7.tgz", - "integrity": "sha512-wo9ogrDG1ITTTBsy46oGiN1dS9A7MROBTcYsfS8DtsImMkHk9JXJ3EWQM6X2SUw4x80uGPlwj0o00Uoc6nEE3g==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.7.tgz", - "integrity": "sha512-U9FcnA821YoILngSmYkW6FjyQe2TyZD5pHt4EVIhmcTkrJw/3KqcrRSxuOo5tFZJi7TE19iDyI1u+weTI7bn2w==", - "dependencies": { - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.7.tgz", - "integrity": "sha512-2yFnBGDvRuxAaE/f0vfBKvtnvvqU8tGpMHqMNpTN2oWMKIR3NqFkjaAgGwawhqK/pIN2T3XdjGPdaG0vDhOBGw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-json-strings": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.7.tgz", - "integrity": "sha512-vcwCbb4HDH+hWi8Pqenwnjy+UiklO4Kt1vfspcQYFhJdpthSnW8XvWGyDZWKNVrVbVViI/S7K9PDJZiUmP2fYQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.7.tgz", - "integrity": "sha512-4D2tpwlQ1odXmTEIFWy9ELJcZHqrStlzK/dAOWYyxX3zT0iXQB6banjgeOJQXzEc4S0E0a5A+hahxPaEFYftsw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.7.tgz", - "integrity": "sha512-T/hRC1uqrzXMKLQ6UCwMT85S3EvqaBXDGf0FaMf4446Qx9vKwlghvee0+uuZcDUCZU5RuNi4781UQ7R308zzBw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.7.tgz", - "integrity": "sha512-9+pB1qxV3vs/8Hdmz/CulFB8w2tuu6EB94JZFsjdqxQokwGa9Unap7Bo2gGBGIvPmDIVvQrom7r5m/TCDMURhg==", - "dependencies": { - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.7.tgz", - "integrity": "sha512-iFI8GDxtevHJ/Z22J5xQpVqFLlMNstcLXh994xifFwxxGslr2ZXXLWgtBeLctOD63UFDArdvN6Tg8RFw+aEmjQ==", - "dependencies": { - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-simple-access": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.24.7.tgz", - "integrity": "sha512-GYQE0tW7YoaN13qFh3O1NCY4MPkUiAH3fiF7UcV/I3ajmDKEdG3l+UOcbAm4zUE3gnvUU+Eni7XrVKo9eO9auw==", - "dependencies": { - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.7.tgz", - "integrity": "sha512-3aytQvqJ/h9z4g8AsKPLvD4Zqi2qT+L3j7XoFFu1XBlZWEl2/1kWnhmAbxpLgPrHSY0M6UA02jyTiwUVtiKR6A==", - "dependencies": { - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.24.7.tgz", - "integrity": "sha512-/jr7h/EWeJtk1U/uz2jlsCioHkZk1JJZVcc8oQsJ1dUlaJD83f4/6Zeh2aHt9BIFokHIsSeDfhUmju0+1GPd6g==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-new-target": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.7.tgz", - "integrity": "sha512-RNKwfRIXg4Ls/8mMTza5oPF5RkOW8Wy/WgMAp1/F1yZ8mMbtwXW+HDoJiOsagWrAhI5f57Vncrmr9XeT4CVapA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.7.tgz", - "integrity": "sha512-Ts7xQVk1OEocqzm8rHMXHlxvsfZ0cEF2yomUqpKENHWMF4zKk175Y4q8H5knJes6PgYad50uuRmt3UJuhBw8pQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.7.tgz", - "integrity": "sha512-e6q1TiVUzvH9KRvicuxdBTUj4AdKSRwzIyFFnfnezpCfP2/7Qmbb8qbU2j7GODbl4JMkblitCQjKYUaX/qkkwA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.7.tgz", - "integrity": "sha512-4QrHAr0aXQCEFni2q4DqKLD31n2DL+RxcwnNjDFkSG0eNQ/xCavnRkfCUjsyqGC2OviNJvZOF/mQqZBw7i2C5Q==", - "dependencies": { - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.7.tgz", - "integrity": "sha512-A/vVLwN6lBrMFmMDmPPz0jnE6ZGx7Jq7d6sT/Ev4H65RER6pZ+kczlf1DthF5N0qaPHBsI7UXiE8Zy66nmAovg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.7.tgz", - "integrity": "sha512-uLEndKqP5BfBbC/5jTwPxLh9kqPWWgzN/f8w6UwAIirAEqiIVJWWY312X72Eub09g5KF9+Zn7+hT7sDxmhRuKA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.7.tgz", - "integrity": "sha512-tK+0N9yd4j+x/4hxF3F0e0fu/VdcxU18y5SevtyM/PCFlQvXbR0Zmlo2eBrKtVipGNFzpq56o8WsIIKcJFUCRQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.7.tgz", - "integrity": "sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.7.tgz", - "integrity": "sha512-COTCOkG2hn4JKGEKBADkA8WNb35TGkkRbI5iT845dB+NyqgO8Hn+ajPbSnIQznneJTa3d30scb6iz/DhH8GsJQ==", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.7.tgz", - "integrity": "sha512-9z76mxwnwFxMyxZWEgdgECQglF2Q7cFLm0kMf8pGwt+GSJsY0cONKj/UuO4bOH0w/uAel3ekS4ra5CEAyJRmDA==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.7.tgz", - "integrity": "sha512-EMi4MLQSHfd2nrCqQEWxFdha2gBCqU4ZcCng4WBGZ5CJL4bBRW0ptdqqDdeirGZcpALazVVNJqRmsO8/+oNCBA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-constant-elements": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.24.7.tgz", - "integrity": "sha512-7LidzZfUXyfZ8/buRW6qIIHBY8wAZ1OrY9c/wTr8YhZ6vMPo+Uc/CVFLYY1spZrEQlD4w5u8wjqk5NQ3OVqQKA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.24.7.tgz", - "integrity": "sha512-H/Snz9PFxKsS1JLI4dJLtnJgCJRoo0AUm3chP6NYr+9En1JMKloheEiLIhlp5MDVznWo+H3AAC1Mc8lmUEpsgg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.24.7.tgz", - "integrity": "sha512-+Dj06GDZEFRYvclU6k4bme55GKBEWUmByM/eoKuqg4zTNQHiApWRhQph5fxQB2wAEFvRzL1tOEj1RJ19wJrhoA==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-jsx": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.24.7.tgz", - "integrity": "sha512-QG9EnzoGn+Qar7rxuW+ZOsbWOt56FvvI93xInqsZDC5fsekx1AlIO4KIJ5M+D0p0SqSH156EpmZyXq630B8OlQ==", - "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.24.7.tgz", - "integrity": "sha512-PLgBVk3fzbmEjBJ/u8kFzOqS9tUeDjiaWud/rRym/yjCo/M9cASPlnrd2ZmmZpQT40fOOrvR8jh+n8jikrOhNA==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.7.tgz", - "integrity": "sha512-lq3fvXPdimDrlg6LWBoqj+r/DEWgONuwjuOuQCSYgRroXDH/IdM1C0IZf59fL5cHLpjEH/O6opIRBbqv7ELnuA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "regenerator-transform": "^0.15.2" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.7.tgz", - "integrity": "sha512-0DUq0pHcPKbjFZCfTss/pGkYMfy3vFWydkUBd9r0GHpIyfs2eCDENvqadMycRS9wZCXR41wucAfJHJmwA0UmoQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-runtime": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.7.tgz", - "integrity": "sha512-YqXjrk4C+a1kZjewqt+Mmu2UuV1s07y8kqcUf4qYLnoqemhR4gRQikhdAhSVJioMjVTu6Mo6pAbaypEA3jY6fw==", - "dependencies": { - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.1", - "babel-plugin-polyfill-regenerator": "^0.6.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.7.tgz", - "integrity": "sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.7.tgz", - "integrity": "sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.7.tgz", - "integrity": "sha512-kHPSIJc9v24zEml5geKg9Mjx5ULpfncj0wRpYtxbvKyTtHCYDkVE3aHQ03FrpEo4gEe2vrJJS1Y9CJTaThA52g==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.7.tgz", - "integrity": "sha512-AfDTQmClklHCOLxtGoP7HkeMw56k1/bTQjwsfhL6pppo/M4TOBSq+jjBUBLmV/4oeFg4GWMavIl44ZeCtmmZTw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.7.tgz", - "integrity": "sha512-VtR8hDy7YLB7+Pet9IarXjg/zgCMSF+1mNS/EQEiEaUPoFXCVsHG64SIxcaaI2zJgRiv+YmgaQESUfWAdbjzgg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typescript": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.24.7.tgz", - "integrity": "sha512-iLD3UNkgx2n/HrjBesVbYX6j0yqn/sJktvbtKKgcaLIQ4bTTQ8obAypc1VpyHPD2y4Phh9zHOaAt8e/L14wCpw==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-typescript": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.7.tgz", - "integrity": "sha512-U3ap1gm5+4edc2Q/P+9VrBNhGkfnf+8ZqppY71Bo/pzZmXhhLdqgaUl6cuB07O1+AQJtCLfaOmswiNbSQ9ivhw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.7.tgz", - "integrity": "sha512-uH2O4OV5M9FZYQrwc7NdVmMxQJOCCzFeYudlZSzUAHRFeOujQefa92E74TQDVskNHCzOXoigEuoyzHDhaEaK5w==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.7.tgz", - "integrity": "sha512-hlQ96MBZSAXUq7ltkjtu3FJCCSMx/j629ns3hA3pXnBXjanNP0LHi+JpPeA81zaWgVK1VGH95Xuy7u0RyQ8kMg==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.7.tgz", - "integrity": "sha512-2G8aAvF4wy1w/AGZkemprdGMRg5o6zPNhbHVImRz3lss55TYCBd6xStN19rt8XJHq20sqV0JbyWjOWwQRwV/wg==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/preset-env": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.7.tgz", - "integrity": "sha512-1YZNsc+y6cTvWlDHidMBsQZrZfEFjRIo/BZCT906PMdzOyXtSLTgqGdrpcuTDCXyd11Am5uQULtDIcCfnTc8fQ==", - "dependencies": { - "@babel/compat-data": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.24.7", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.7", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.7", - "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.24.7", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.24.7", - "@babel/plugin-transform-async-generator-functions": "^7.24.7", - "@babel/plugin-transform-async-to-generator": "^7.24.7", - "@babel/plugin-transform-block-scoped-functions": "^7.24.7", - "@babel/plugin-transform-block-scoping": "^7.24.7", - "@babel/plugin-transform-class-properties": "^7.24.7", - "@babel/plugin-transform-class-static-block": "^7.24.7", - "@babel/plugin-transform-classes": "^7.24.7", - "@babel/plugin-transform-computed-properties": "^7.24.7", - "@babel/plugin-transform-destructuring": "^7.24.7", - "@babel/plugin-transform-dotall-regex": "^7.24.7", - "@babel/plugin-transform-duplicate-keys": "^7.24.7", - "@babel/plugin-transform-dynamic-import": "^7.24.7", - "@babel/plugin-transform-exponentiation-operator": "^7.24.7", - "@babel/plugin-transform-export-namespace-from": "^7.24.7", - "@babel/plugin-transform-for-of": "^7.24.7", - "@babel/plugin-transform-function-name": "^7.24.7", - "@babel/plugin-transform-json-strings": "^7.24.7", - "@babel/plugin-transform-literals": "^7.24.7", - "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", - "@babel/plugin-transform-member-expression-literals": "^7.24.7", - "@babel/plugin-transform-modules-amd": "^7.24.7", - "@babel/plugin-transform-modules-commonjs": "^7.24.7", - "@babel/plugin-transform-modules-systemjs": "^7.24.7", - "@babel/plugin-transform-modules-umd": "^7.24.7", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", - "@babel/plugin-transform-new-target": "^7.24.7", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", - "@babel/plugin-transform-numeric-separator": "^7.24.7", - "@babel/plugin-transform-object-rest-spread": "^7.24.7", - "@babel/plugin-transform-object-super": "^7.24.7", - "@babel/plugin-transform-optional-catch-binding": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.7", - "@babel/plugin-transform-parameters": "^7.24.7", - "@babel/plugin-transform-private-methods": "^7.24.7", - "@babel/plugin-transform-private-property-in-object": "^7.24.7", - "@babel/plugin-transform-property-literals": "^7.24.7", - "@babel/plugin-transform-regenerator": "^7.24.7", - "@babel/plugin-transform-reserved-words": "^7.24.7", - "@babel/plugin-transform-shorthand-properties": "^7.24.7", - "@babel/plugin-transform-spread": "^7.24.7", - "@babel/plugin-transform-sticky-regex": "^7.24.7", - "@babel/plugin-transform-template-literals": "^7.24.7", - "@babel/plugin-transform-typeof-symbol": "^7.24.7", - "@babel/plugin-transform-unicode-escapes": "^7.24.7", - "@babel/plugin-transform-unicode-property-regex": "^7.24.7", - "@babel/plugin-transform-unicode-regex": "^7.24.7", - "@babel/plugin-transform-unicode-sets-regex": "^7.24.7", - "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.4", - "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.31.0", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-env/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/preset-modules": { - "version": "0.1.6-no-external-plugins", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", - "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/@babel/preset-react": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.24.7.tgz", - "integrity": "sha512-AAH4lEkpmzFWrGVlHaxJB7RLH21uPQ9+He+eFLWHmF9IuFQVugz8eAsamaW0DXRrTfco5zj1wWtpdcXJUOfsag==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "@babel/plugin-transform-react-display-name": "^7.24.7", - "@babel/plugin-transform-react-jsx": "^7.24.7", - "@babel/plugin-transform-react-jsx-development": "^7.24.7", - "@babel/plugin-transform-react-pure-annotations": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-typescript": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.24.7.tgz", - "integrity": "sha512-SyXRe3OdWwIwalxDg5UtJnJQO+YPcTfwiIY2B0Xlddh9o7jpWLvv8X1RthIeDOxQ+O1ML5BLPCONToObyVQVuQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "@babel/plugin-syntax-jsx": "^7.24.7", - "@babel/plugin-transform-modules-commonjs": "^7.24.7", - "@babel/plugin-transform-typescript": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" - }, - "node_modules/@babel/runtime": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", - "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", - "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz", - "integrity": "sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==", - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz", - "integrity": "sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==", - "dependencies": { - "@babel/helper-string-parser": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" - }, - "node_modules/@csstools/normalize.css": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-12.1.1.tgz", - "integrity": "sha512-YAYeJ+Xqh7fUou1d1j9XHl44BmsuThiTr4iNrgCQ3J27IbhXsxXDGZ1cXv8Qvs99d4rBbLiSKy3+WZiet32PcQ==" - }, - "node_modules/@csstools/postcss-cascade-layers": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.1.1.tgz", - "integrity": "sha512-+KdYrpKC5TgomQr2DlZF4lDEpHcoxnj5IGddYYfBWJAKfj1JtuHUIqMa+E1pJJ+z3kvDViWMqyqPlG4Ja7amQA==", - "dependencies": { - "@csstools/selector-specificity": "^2.0.2", - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-color-function": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz", - "integrity": "sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw==", - "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-font-format-keywords": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.1.tgz", - "integrity": "sha512-ZgrlzuUAjXIOc2JueK0X5sZDjCtgimVp/O5CEqTcs5ShWBa6smhWYbS0x5cVc/+rycTDbjjzoP0KTDnUneZGOg==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-hwb-function": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.2.tgz", - "integrity": "sha512-YHdEru4o3Rsbjmu6vHy4UKOXZD+Rn2zmkAmLRfPet6+Jz4Ojw8cbWxe1n42VaXQhD3CQUXXTooIy8OkVbUcL+w==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-ic-unit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.1.tgz", - "integrity": "sha512-Ot1rcwRAaRHNKC9tAqoqNZhjdYBzKk1POgWfhN4uCOE47ebGcLRqXjKkApVDpjifL6u2/55ekkpnFcp+s/OZUw==", - "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-is-pseudo-class": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.7.tgz", - "integrity": "sha512-7JPeVVZHd+jxYdULl87lvjgvWldYu+Bc62s9vD/ED6/QTGjy0jy0US/f6BG53sVMTBJ1lzKZFpYmofBN9eaRiA==", - "dependencies": { - "@csstools/selector-specificity": "^2.0.0", - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-nested-calc": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-1.0.0.tgz", - "integrity": "sha512-JCsQsw1wjYwv1bJmgjKSoZNvf7R6+wuHDAbi5f/7MbFhl2d/+v+TvBTU4BJH3G1X1H87dHl0mh6TfYogbT/dJQ==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-normalize-display-values": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz", - "integrity": "sha512-jcOanIbv55OFKQ3sYeFD/T0Ti7AMXc9nM1hZWu8m/2722gOTxFg7xYu4RDLJLeZmPUVQlGzo4jhzvTUq3x4ZUw==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-oklab-function": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.1.tgz", - "integrity": "sha512-nJpJgsdA3dA9y5pgyb/UfEzE7W5Ka7u0CX0/HIMVBNWzWemdcTH3XwANECU6anWv/ao4vVNLTMxhiPNZsTK6iA==", - "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-progressive-custom-properties": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz", - "integrity": "sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.3" - } - }, - "node_modules/@csstools/postcss-stepped-value-functions": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.1.tgz", - "integrity": "sha512-dz0LNoo3ijpTOQqEJLY8nyaapl6umbmDcgj4AD0lgVQ572b2eqA1iGZYTTWhrcrHztWDDRAX2DGYyw2VBjvCvQ==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-text-decoration-shorthand": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-1.0.0.tgz", - "integrity": "sha512-c1XwKJ2eMIWrzQenN0XbcfzckOLLJiczqy+YvfGmzoVXd7pT9FfObiSEfzs84bpE/VqfpEuAZ9tCRbZkZxxbdw==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-trigonometric-functions": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz", - "integrity": "sha512-woKaLO///4bb+zZC2s80l+7cm07M7268MsyG3M0ActXXEFi6SuhvriQYcb58iiKGbjwwIU7n45iRLEHypB47Og==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-unset-value": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz", - "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==", - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/selector-specificity": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz", - "integrity": "sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw==", - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss-selector-parser": "^6.0.10" - } - }, - "node_modules/@emotion/babel-plugin": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", - "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", - "dependencies": { - "@babel/helper-module-imports": "^7.16.7", - "@babel/runtime": "^7.18.3", - "@emotion/hash": "^0.9.1", - "@emotion/memoize": "^0.8.1", - "@emotion/serialize": "^1.1.2", - "babel-plugin-macros": "^3.1.0", - "convert-source-map": "^1.5.0", - "escape-string-regexp": "^4.0.0", - "find-root": "^1.1.0", - "source-map": "^0.5.7", - "stylis": "4.2.0" - } - }, - "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" - }, - "node_modules/@emotion/babel-plugin/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@emotion/babel-plugin/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@emotion/cache": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", - "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", - "dependencies": { - "@emotion/memoize": "^0.8.1", - "@emotion/sheet": "^1.2.2", - "@emotion/utils": "^1.2.1", - "@emotion/weak-memoize": "^0.3.1", - "stylis": "4.2.0" - } - }, - "node_modules/@emotion/hash": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", - "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" - }, - "node_modules/@emotion/is-prop-valid": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz", - "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==", - "dependencies": { - "@emotion/memoize": "^0.8.1" - } - }, - "node_modules/@emotion/memoize": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", - "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" - }, - "node_modules/@emotion/react": { - "version": "11.11.4", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz", - "integrity": "sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==", - "dependencies": { - "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.11.0", - "@emotion/cache": "^11.11.0", - "@emotion/serialize": "^1.1.3", - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", - "@emotion/utils": "^1.2.1", - "@emotion/weak-memoize": "^0.3.1", - "hoist-non-react-statics": "^3.3.1" - }, - "peerDependencies": { - "react": ">=16.8.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@emotion/serialize": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.4.tgz", - "integrity": "sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ==", - "dependencies": { - "@emotion/hash": "^0.9.1", - "@emotion/memoize": "^0.8.1", - "@emotion/unitless": "^0.8.1", - "@emotion/utils": "^1.2.1", - "csstype": "^3.0.2" - } - }, - "node_modules/@emotion/sheet": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", - "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==" - }, - "node_modules/@emotion/styled": { - "version": "11.11.5", - "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.5.tgz", - "integrity": "sha512-/ZjjnaNKvuMPxcIiUkf/9SHoG4Q196DRl1w82hQ3WCsjo1IUR8uaGWrC6a87CrYAW0Kb/pK7hk8BnLgLRi9KoQ==", - "dependencies": { - "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.11.0", - "@emotion/is-prop-valid": "^1.2.2", - "@emotion/serialize": "^1.1.4", - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", - "@emotion/utils": "^1.2.1" - }, - "peerDependencies": { - "@emotion/react": "^11.0.0-rc.0", - "react": ">=16.8.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@emotion/unitless": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", - "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" - }, - "node_modules/@emotion/use-insertion-effect-with-fallbacks": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", - "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==", - "peerDependencies": { - "react": ">=16.8.0" - } - }, - "node_modules/@emotion/utils": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", - "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==" - }, - "node_modules/@emotion/weak-memoize": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", - "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.1.tgz", - "integrity": "sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/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==" - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/eslintrc/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==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@eslint/eslintrc/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@floating-ui/core": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.2.tgz", - "integrity": "sha512-+2XpQV9LLZeanU4ZevzRnGFg2neDeKHgFLjP6YLW+tly0IvrhqT4u8enLGjLH3qeh85g19xY5rsAusfwTdn5lg==", - "dependencies": { - "@floating-ui/utils": "^0.2.0" - } - }, - "node_modules/@floating-ui/dom": { - "version": "1.6.5", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.5.tgz", - "integrity": "sha512-Nsdud2X65Dz+1RHjAIP0t8z5e2ff/IRbei6BqFrl1urT8sDVzM1HMQ+R0XcU5ceRfyO3I6ayeqIfh+6Wb8LGTw==", - "dependencies": { - "@floating-ui/core": "^1.0.0", - "@floating-ui/utils": "^0.2.0" - } - }, - "node_modules/@floating-ui/react-dom": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.0.tgz", - "integrity": "sha512-lNzj5EQmEKn5FFKc04+zasr09h/uX8RtJRNj5gUXsSQIXHVWTVh+hVAg1vOMCexkX8EgvemMvIFpQfkosnVNyA==", - "dependencies": { - "@floating-ui/dom": "^1.0.0" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/@floating-ui/utils": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.2.tgz", - "integrity": "sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw==" - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==" - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "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/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "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/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "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/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", - "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^27.5.1", - "jest-util": "^27.5.1", - "slash": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/console/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/console/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/console/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/console/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==" - }, - "node_modules/@jest/console/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/core": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz", - "integrity": "sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==", - "dependencies": { - "@jest/console": "^27.5.1", - "@jest/reporters": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.8.1", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^27.5.1", - "jest-config": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-resolve-dependencies": "^27.5.1", - "jest-runner": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "jest-watcher": "^27.5.1", - "micromatch": "^4.0.4", - "rimraf": "^3.0.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/core/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/core/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/core/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/core/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==" - }, - "node_modules/@jest/core/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/core/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/environment": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", - "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", - "dependencies": { - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", - "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", - "dependencies": { - "@jest/types": "^27.5.1", - "@sinonjs/fake-timers": "^8.0.1", - "@types/node": "*", - "jest-message-util": "^27.5.1", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", - "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/types": "^27.5.1", - "expect": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz", - "integrity": "sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==", - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.2", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-haste-map": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "slash": "^3.0.0", - "source-map": "^0.6.0", - "string-length": "^4.0.1", - "terminal-link": "^2.0.0", - "v8-to-istanbul": "^8.1.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/reporters/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/reporters/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/reporters/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/reporters/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==" - }, - "node_modules/@jest/reporters/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/reporters/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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@jest/reporters/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/schemas": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", - "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", - "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", - "dependencies": { - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9", - "source-map": "^0.6.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/source-map/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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@jest/test-result": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", - "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", - "dependencies": { - "@jest/console": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz", - "integrity": "sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==", - "dependencies": { - "@jest/test-result": "^27.5.1", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-runtime": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", - "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", - "dependencies": { - "@babel/core": "^7.1.0", - "@jest/types": "^27.5.1", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-util": "^27.5.1", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/transform/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/transform/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/transform/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/transform/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==" - }, - "node_modules/@jest/transform/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" - }, - "node_modules/@jest/transform/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/transform/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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@jest/transform/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/types/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/types/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/types/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/types/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==" - }, - "node_modules/@jest/types/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/types/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", - "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@leichtgewicht/ip-codec": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", - "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==" - }, - "node_modules/@mui/base": { - "version": "5.0.0-beta.40", - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz", - "integrity": "sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@floating-ui/react-dom": "^2.0.8", - "@mui/types": "^7.2.14", - "@mui/utils": "^5.15.14", - "@popperjs/core": "^2.11.8", - "clsx": "^2.1.0", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/core-downloads-tracker": { - "version": "5.15.19", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.19.tgz", - "integrity": "sha512-tCHSi/Tomez9ERynFhZRvFO6n9ATyrPs+2N80DMDzp6xDVirbBjEwhPcE+x7Lj+nwYw0SqFkOxyvMP0irnm55w==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - } - }, - "node_modules/@mui/icons-material": { - "version": "5.15.19", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.19.tgz", - "integrity": "sha512-RsEiRxA5azN9b8gI7JRqekkgvxQUlitoBOtZglflb8cUDyP12/cP4gRwhb44Ea1/zwwGGjAj66ZJpGHhKfibNA==", - "dependencies": { - "@babel/runtime": "^7.23.9" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@mui/material": "^5.0.0", - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/material": { - "version": "5.15.19", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.19.tgz", - "integrity": "sha512-lp5xQBbcRuxNtjpWU0BWZgIrv2XLUz4RJ0RqFXBdESIsKoGCQZ6P3wwU5ZPuj5TjssNiKv9AlM+vHopRxZhvVQ==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@mui/base": "5.0.0-beta.40", - "@mui/core-downloads-tracker": "^5.15.19", - "@mui/system": "^5.15.15", - "@mui/types": "^7.2.14", - "@mui/utils": "^5.15.14", - "@types/react-transition-group": "^4.4.10", - "clsx": "^2.1.0", - "csstype": "^3.1.3", - "prop-types": "^15.8.1", - "react-is": "^18.2.0", - "react-transition-group": "^4.4.5" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@emotion/react": "^11.5.0", - "@emotion/styled": "^11.3.0", - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - }, - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/material/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" - }, - "node_modules/@mui/private-theming": { - "version": "5.15.14", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.14.tgz", - "integrity": "sha512-UH0EiZckOWcxiXLX3Jbb0K7rC8mxTr9L9l6QhOZxYc4r8FHUkefltV9VDGLrzCaWh30SQiJvAEd7djX3XXY6Xw==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@mui/utils": "^5.15.14", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/styled-engine": { - "version": "5.15.14", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.14.tgz", - "integrity": "sha512-RILkuVD8gY6PvjZjqnWhz8fu68dVkqhM5+jYWfB5yhlSQKg+2rHkmEwm75XIeAqI3qwOndK6zELK5H6Zxn4NHw==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@emotion/cache": "^11.11.0", - "csstype": "^3.1.3", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@emotion/react": "^11.4.1", - "@emotion/styled": "^11.3.0", - "react": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - } - } - }, - "node_modules/@mui/system": { - "version": "5.15.15", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.15.tgz", - "integrity": "sha512-aulox6N1dnu5PABsfxVGOZffDVmlxPOVgj56HrUnJE8MCSh8lOvvkd47cebIVQQYAjpwieXQXiDPj5pwM40jTQ==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@mui/private-theming": "^5.15.14", - "@mui/styled-engine": "^5.15.14", - "@mui/types": "^7.2.14", - "@mui/utils": "^5.15.14", - "clsx": "^2.1.0", - "csstype": "^3.1.3", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@emotion/react": "^11.5.0", - "@emotion/styled": "^11.3.0", - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - }, - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/types": { - "version": "7.2.14", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.14.tgz", - "integrity": "sha512-MZsBZ4q4HfzBsywtXgM1Ksj6HDThtiwmOKUXH1pKYISI9gAVXCNHNpo7TlGoGrBaYWZTdNoirIN7JsQcQUjmQQ==", - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/utils": { - "version": "5.15.14", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.14.tgz", - "integrity": "sha512-0lF/7Hh/ezDv5X7Pry6enMsbYyGKjADzvHyo3Qrc/SSlTsQ1VkbDMbH0m2t3OR5iIVLwMoxwM7yGd+6FCMtTFA==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@types/prop-types": "^15.7.11", - "prop-types": "^15.8.1", - "react-is": "^18.2.0" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/utils/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" - }, - "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { - "version": "5.1.1-v1", - "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", - "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", - "dependencies": { - "eslint-scope": "5.1.1" - } - }, - "node_modules/@nicolo-ribaudo/eslint-scope-5-internals/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@nicolo-ribaudo/eslint-scope-5-internals/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "engines": { - "node": ">=4.0" - } - }, - "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==", - "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==", - "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==", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@pmmmwh/react-refresh-webpack-plugin": { - "version": "0.5.15", - "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.15.tgz", - "integrity": "sha512-LFWllMA55pzB9D34w/wXUCf8+c+IYKuJDgxiZ3qMhl64KRMBHYM1I3VdGaD2BV5FNPV2/S2596bppxHbv2ZydQ==", - "dependencies": { - "ansi-html": "^0.0.9", - "core-js-pure": "^3.23.3", - "error-stack-parser": "^2.0.6", - "html-entities": "^2.1.0", - "loader-utils": "^2.0.4", - "schema-utils": "^4.2.0", - "source-map": "^0.7.3" - }, - "engines": { - "node": ">= 10.13" - }, - "peerDependencies": { - "@types/webpack": "4.x || 5.x", - "react-refresh": ">=0.10.0 <1.0.0", - "sockjs-client": "^1.4.0", - "type-fest": ">=0.17.0 <5.0.0", - "webpack": ">=4.43.0 <6.0.0", - "webpack-dev-server": "3.x || 4.x || 5.x", - "webpack-hot-middleware": "2.x", - "webpack-plugin-serve": "0.x || 1.x" - }, - "peerDependenciesMeta": { - "@types/webpack": { - "optional": true - }, - "sockjs-client": { - "optional": true - }, - "type-fest": { - "optional": true - }, - "webpack-dev-server": { - "optional": true - }, - "webpack-hot-middleware": { - "optional": true - }, - "webpack-plugin-serve": { - "optional": true - } - } - }, - "node_modules/@popperjs/core": { - "version": "2.11.8", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", - "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/popperjs" - } - }, - "node_modules/@ratchet-ml/ratchet-web": { - "resolved": "../../target/pkg/ratchet-web", - "link": true - }, - "node_modules/@rollup/plugin-babel": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", - "integrity": "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==", - "dependencies": { - "@babel/helper-module-imports": "^7.10.4", - "@rollup/pluginutils": "^3.1.0" - }, - "engines": { - "node": ">= 10.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0", - "@types/babel__core": "^7.1.9", - "rollup": "^1.20.0||^2.0.0" - }, - "peerDependenciesMeta": { - "@types/babel__core": { - "optional": true - } - } - }, - "node_modules/@rollup/plugin-node-resolve": { - "version": "11.2.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", - "integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==", - "dependencies": { - "@rollup/pluginutils": "^3.1.0", - "@types/resolve": "1.17.1", - "builtin-modules": "^3.1.0", - "deepmerge": "^4.2.2", - "is-module": "^1.0.0", - "resolve": "^1.19.0" - }, - "engines": { - "node": ">= 10.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0" - } - }, - "node_modules/@rollup/plugin-replace": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz", - "integrity": "sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==", - "dependencies": { - "@rollup/pluginutils": "^3.1.0", - "magic-string": "^0.25.7" - }, - "peerDependencies": { - "rollup": "^1.20.0 || ^2.0.0" - } - }, - "node_modules/@rollup/pluginutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", - "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", - "dependencies": { - "@types/estree": "0.0.39", - "estree-walker": "^1.0.1", - "picomatch": "^2.2.2" - }, - "engines": { - "node": ">= 8.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0" - } - }, - "node_modules/@rollup/pluginutils/node_modules/@types/estree": { - "version": "0.0.39", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", - "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==" - }, - "node_modules/@rushstack/eslint-patch": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.3.tgz", - "integrity": "sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg==" - }, - "node_modules/@sinclair/typebox": { - "version": "0.24.51", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", - "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" - }, - "node_modules/@sinonjs/commons": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", - "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", - "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", - "dependencies": { - "@sinonjs/commons": "^1.7.0" - } - }, - "node_modules/@surma/rollup-plugin-off-main-thread": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", - "integrity": "sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==", - "dependencies": { - "ejs": "^3.1.6", - "json5": "^2.2.0", - "magic-string": "^0.25.0", - "string.prototype.matchall": "^4.0.6" - } - }, - "node_modules/@svgr/babel-plugin-add-jsx-attribute": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz", - "integrity": "sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg==", - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz", - "integrity": "sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg==", - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz", - "integrity": "sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA==", - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz", - "integrity": "sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/babel-plugin-svg-dynamic-title": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz", - "integrity": "sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg==", - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/babel-plugin-svg-em-dimensions": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz", - "integrity": "sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw==", - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/babel-plugin-transform-react-native-svg": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz", - "integrity": "sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q==", - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/babel-plugin-transform-svg-component": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz", - "integrity": "sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/babel-preset": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-5.5.0.tgz", - "integrity": "sha512-4FiXBjvQ+z2j7yASeGPEi8VD/5rrGQk4Xrq3EdJmoZgz/tpqChpo5hgXDvmEauwtvOc52q8ghhZK4Oy7qph4ig==", - "dependencies": { - "@svgr/babel-plugin-add-jsx-attribute": "^5.4.0", - "@svgr/babel-plugin-remove-jsx-attribute": "^5.4.0", - "@svgr/babel-plugin-remove-jsx-empty-expression": "^5.0.1", - "@svgr/babel-plugin-replace-jsx-attribute-value": "^5.0.1", - "@svgr/babel-plugin-svg-dynamic-title": "^5.4.0", - "@svgr/babel-plugin-svg-em-dimensions": "^5.4.0", - "@svgr/babel-plugin-transform-react-native-svg": "^5.4.0", - "@svgr/babel-plugin-transform-svg-component": "^5.5.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/core": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/core/-/core-5.5.0.tgz", - "integrity": "sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ==", - "dependencies": { - "@svgr/plugin-jsx": "^5.5.0", - "camelcase": "^6.2.0", - "cosmiconfig": "^7.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/hast-util-to-babel-ast": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz", - "integrity": "sha512-cAaR/CAiZRB8GP32N+1jocovUtvlj0+e65TB50/6Lcime+EA49m/8l+P2ko+XPJ4dw3xaPS3jOL4F2X4KWxoeQ==", - "dependencies": { - "@babel/types": "^7.12.6" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/plugin-jsx": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz", - "integrity": "sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA==", - "dependencies": { - "@babel/core": "^7.12.3", - "@svgr/babel-preset": "^5.5.0", - "@svgr/hast-util-to-babel-ast": "^5.5.0", - "svg-parser": "^2.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/plugin-svgo": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz", - "integrity": "sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ==", - "dependencies": { - "cosmiconfig": "^7.0.0", - "deepmerge": "^4.2.2", - "svgo": "^1.2.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/webpack": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-5.5.0.tgz", - "integrity": "sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g==", - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/plugin-transform-react-constant-elements": "^7.12.1", - "@babel/preset-env": "^7.12.1", - "@babel/preset-react": "^7.12.5", - "@svgr/core": "^5.5.0", - "@svgr/plugin-jsx": "^5.5.0", - "@svgr/plugin-svgo": "^5.5.0", - "loader-utils": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/@trysound/sax": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", - "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.8", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", - "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/body-parser": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/bonjour": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", - "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/connect-history-api-fallback": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", - "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", - "dependencies": { - "@types/express-serve-static-core": "*", - "@types/node": "*" - } - }, - "node_modules/@types/eslint": { - "version": "8.56.10", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", - "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==", - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" - }, - "node_modules/@types/express": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", - "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.19.3", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.3.tgz", - "integrity": "sha512-KOzM7MhcBFlmnlr/fzISFF5vGWVSvN6fTd4T+ExOt08bA/dA5kpSzY52nMsI1KDFmUREpJelPYyuslLRSjjgCg==", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==" - }, - "node_modules/@types/http-errors": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" - }, - "node_modules/@types/http-proxy": { - "version": "1.17.14", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz", - "integrity": "sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" - }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" - }, - "node_modules/@types/node": { - "version": "20.14.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.2.tgz", - "integrity": "sha512-xyu6WAMVwv6AKFLB+e/7ySZVr/0zLCzOa7rSpq6jNwpqOrUbcACDWC+53d4n2QHOnDou0fbIsg8wZu/sxrnI4Q==", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/node-forge": { - "version": "1.3.11", - "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", - "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/parse-json": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", - "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" - }, - "node_modules/@types/prettier": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", - "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==" - }, - "node_modules/@types/prop-types": { - "version": "15.7.12", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", - "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" - }, - "node_modules/@types/q": { - "version": "1.5.8", - "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.8.tgz", - "integrity": "sha512-hroOstUScF6zhIi+5+x0dzqrHA1EJi+Irri6b1fxolMTqqHIV/Cg77EtnQcZqZCu8hR3mX2BzIxN4/GzI68Kfw==" - }, - "node_modules/@types/qs": { - "version": "6.9.15", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", - "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==" - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" - }, - "node_modules/@types/react": { - "version": "18.3.3", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz", - "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==", - "dependencies": { - "@types/prop-types": "*", - "csstype": "^3.0.2" - } - }, - "node_modules/@types/react-transition-group": { - "version": "4.4.10", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", - "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==", - "dependencies": { - "@types/react": "*" - } - }, - "node_modules/@types/resolve": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", - "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" - }, - "node_modules/@types/semver": { - "version": "7.5.8", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==" - }, - "node_modules/@types/send": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", - "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-index": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", - "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", - "dependencies": { - "@types/express": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.7", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", - "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "*" - } - }, - "node_modules/@types/sockjs": { - "version": "0.3.36", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", - "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==" - }, - "node_modules/@types/trusted-types": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", - "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==" - }, - "node_modules/@types/ws": { - "version": "8.5.10", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", - "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==" - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", - "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", - "dependencies": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/type-utils": "5.62.0", - "@typescript-eslint/utils": "5.62.0", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/experimental-utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.62.0.tgz", - "integrity": "sha512-RTXpeB3eMkpoclG3ZHft6vG/Z30azNHuqY6wKPBHlVMZFuEvrtlEDe8gMqDb+SO+9hjC/pLekeSCryf9vMZlCw==", - "dependencies": { - "@typescript-eslint/utils": "5.62.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", - "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", - "dependencies": { - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", - "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", - "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", - "dependencies": { - "@typescript-eslint/typescript-estree": "5.62.0", - "@typescript-eslint/utils": "5.62.0", - "debug": "^4.3.4", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/types": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", - "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", - "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", - "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", - "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", - "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", - "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", - "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==" - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", - "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==" - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", - "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==" - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", - "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", - "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==" - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", - "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.12.1" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", - "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", - "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", - "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==" - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", - "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-opt": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1", - "@webassemblyjs/wast-printer": "1.12.1" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", - "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", - "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", - "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-api-error": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", - "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" - }, - "node_modules/abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", - "deprecated": "Use your platform's native atob() and btoa() methods instead" - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", - "dependencies": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" - } - }, - "node_modules/acorn-globals/node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-import-assertions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", - "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", - "peerDependencies": { - "acorn": "^8" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/address": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz", - "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/adjust-sourcemap-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", - "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", - "dependencies": { - "loader-utils": "^2.0.0", - "regex-parser": "^2.2.11" - }, - "engines": { - "node": ">=8.9" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.16.0.tgz", - "integrity": "sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.4.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats/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==" - }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-html": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.9.tgz", - "integrity": "sha512-ozbS3LuenHVxNRh/wdnN16QapUHzauqSomAl1jwwJRRsGwFwtj644lIhxfWu0Fy0acCij2+AEgHvjscq3dlVXg==", - "engines": [ - "node >= 0.8.0" - ], - "bin": { - "ansi-html": "bin/ansi-html" - } - }, - "node_modules/ansi-html-community": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", - "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", - "engines": [ - "node >= 0.8.0" - ], - "bin": { - "ansi-html": "bin/ansi-html" - } - }, - "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==", - "engines": { - "node": ">=8" - } - }, - "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==", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", - "dependencies": { - "dequal": "^2.0.3" - } - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", - "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", - "dependencies": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, - "node_modules/array-includes": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", - "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/array.prototype.findlast": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", - "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.findlastindex": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", - "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", - "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.reduce": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.7.tgz", - "integrity": "sha512-mzmiUCVwtiD4lgxYP8g7IYy8El8p2CSMePvIbTS7gchKir/L1fgJrk0yDKmAX6mnRQFKNADYIk8nNlTris5H1Q==", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-array-method-boxes-properly": "^1.0.0", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.toreversed": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz", - "integrity": "sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - } - }, - "node_modules/array.prototype.tosorted": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", - "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", - "es-errors": "^1.3.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.2.1", - "get-intrinsic": "^1.2.3", - "is-array-buffer": "^3.0.4", - "is-shared-array-buffer": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" - }, - "node_modules/ast-types-flow": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", - "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==" - }, - "node_modules/async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/autoprefixer": { - "version": "10.4.19", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", - "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "browserslist": "^4.23.0", - "caniuse-lite": "^1.0.30001599", - "fraction.js": "^4.3.7", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/axe-core": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.0.tgz", - "integrity": "sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/axobject-query": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", - "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", - "dependencies": { - "dequal": "^2.0.3" - } - }, - "node_modules/babel-jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", - "integrity": "sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==", - "dependencies": { - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^27.5.1", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-jest/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/babel-jest/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/babel-jest/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/babel-jest/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==" - }, - "node_modules/babel-jest/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-jest/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-loader": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.3.0.tgz", - "integrity": "sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==", - "dependencies": { - "find-cache-dir": "^3.3.1", - "loader-utils": "^2.0.0", - "make-dir": "^3.1.0", - "schema-utils": "^2.6.5" - }, - "engines": { - "node": ">= 8.9" - }, - "peerDependencies": { - "@babel/core": "^7.0.0", - "webpack": ">=2" - } - }, - "node_modules/babel-loader/node_modules/schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", - "dependencies": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 8.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz", - "integrity": "sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==", - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.0.0", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/babel-plugin-macros": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", - "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", - "dependencies": { - "@babel/runtime": "^7.12.5", - "cosmiconfig": "^7.0.0", - "resolve": "^1.19.0" - }, - "engines": { - "node": ">=10", - "npm": ">=6" - } - }, - "node_modules/babel-plugin-named-asset-import": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.8.tgz", - "integrity": "sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==", - "peerDependencies": { - "@babel/core": "^7.1.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.11", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", - "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==", - "dependencies": { - "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.6.2", - "semver": "^6.3.1" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.4.tgz", - "integrity": "sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg==", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.1", - "core-js-compat": "^3.36.1" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", - "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.2" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-transform-react-remove-prop-types": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", - "integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==" - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz", - "integrity": "sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==", - "dependencies": { - "babel-plugin-jest-hoist": "^27.5.1", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-react-app": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-10.0.1.tgz", - "integrity": "sha512-b0D9IZ1WhhCWkrTXyFuIIgqGzSkRIH5D5AmB0bXbzYAB1OBAwHcUeyWW2LorutLWF5btNo/N7r/cIdmvvKJlYg==", - "dependencies": { - "@babel/core": "^7.16.0", - "@babel/plugin-proposal-class-properties": "^7.16.0", - "@babel/plugin-proposal-decorators": "^7.16.4", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.0", - "@babel/plugin-proposal-numeric-separator": "^7.16.0", - "@babel/plugin-proposal-optional-chaining": "^7.16.0", - "@babel/plugin-proposal-private-methods": "^7.16.0", - "@babel/plugin-transform-flow-strip-types": "^7.16.0", - "@babel/plugin-transform-react-display-name": "^7.16.0", - "@babel/plugin-transform-runtime": "^7.16.4", - "@babel/preset-env": "^7.16.4", - "@babel/preset-react": "^7.16.0", - "@babel/preset-typescript": "^7.16.0", - "@babel/runtime": "^7.16.3", - "babel-plugin-macros": "^3.1.0", - "babel-plugin-transform-react-remove-prop-types": "^0.4.24" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==" - }, - "node_modules/bfj": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/bfj/-/bfj-7.1.0.tgz", - "integrity": "sha512-I6MMLkn+anzNdCUp9hMRyui1HaNEUCco50lxbvNS4+EyXg8lN3nJ48PjPWtbH8UVS9CuMoaKE9U2V3l29DaRQw==", - "dependencies": { - "bluebird": "^3.7.2", - "check-types": "^11.2.3", - "hoopy": "^0.1.4", - "jsonpath": "^1.1.1", - "tryer": "^1.0.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "engines": { - "node": "*" - } - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" - }, - "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/bonjour-service": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz", - "integrity": "sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "multicast-dns": "^7.2.5" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "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==", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" - }, - "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" - }, - "node_modules/builtin-modules": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", - "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/camel-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", - "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "dependencies": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } - }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/caniuse-api": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", - "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", - "dependencies": { - "browserslist": "^4.0.0", - "caniuse-lite": "^1.0.0", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001629", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001629.tgz", - "integrity": "sha512-c3dl911slnQhmxUIT4HhYzT7wnBK/XYpGnYLOj4nJBaRiw52Ibe7YxlDaAeRECvA786zCuExhxIUJ2K7nHMrBw==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/case-sensitive-paths-webpack-plugin": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", - "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==", - "engines": { - "node": ">=4" - } - }, - "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==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "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==", - "engines": { - "node": ">=10" - } - }, - "node_modules/check-types": { - "version": "11.2.3", - "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.2.3.tgz", - "integrity": "sha512-+67P1GkJRaxQD6PKK0Et9DhwQB+vGg3PM5+aavopCpZT1lj9jeqfvpgTLAWErNj8qApkkmXlu/Ug74kmhagkXg==" - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/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==", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/chrome-trace-event": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", - "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", - "engines": { - "node": ">=6.0" - } - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz", - "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==" - }, - "node_modules/clean-css": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", - "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", - "dependencies": { - "source-map": "~0.6.0" - }, - "engines": { - "node": ">= 10.0" - } - }, - "node_modules/clean-css/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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/coa": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", - "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", - "dependencies": { - "@types/q": "^1.5.1", - "chalk": "^2.4.1", - "q": "^1.1.2" - }, - "engines": { - "node": ">= 4.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==" - }, - "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==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "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==" - }, - "node_modules/colord": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", - "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==" - }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "engines": { - "node": ">= 12" - } - }, - "node_modules/common-tags": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", - "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "dependencies": { - "mime-db": ">= 1.43.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "dependencies": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/compression/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/compression/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/compression/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==" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "node_modules/confusing-browser-globals": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", - "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==" - }, - "node_modules/connect-history-api-fallback": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", - "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" - }, - "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, - "node_modules/core-js": { - "version": "3.37.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.37.1.tgz", - "integrity": "sha512-Xn6qmxrQZyB0FFY8E3bgRXei3lWDJHhvI+u0q9TKIYM49G8pAr0FgnnrFRAmsbptZL1yxRADVXn+x5AGsbBfyw==", - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-js-compat": { - "version": "3.37.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.1.tgz", - "integrity": "sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==", - "dependencies": { - "browserslist": "^4.23.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-js-pure": { - "version": "3.37.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.37.1.tgz", - "integrity": "sha512-J/r5JTHSmzTxbiYYrzXg9w1VpqrYt+gexenBE9pugeyhwPZTAEJddyiReJWsLO6uNQ8xJZFbod6XC7KKwatCiA==", - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "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==" - }, - "node_modules/cosmiconfig": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/css-blank-pseudo": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", - "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==", - "dependencies": { - "postcss-selector-parser": "^6.0.9" - }, - "bin": { - "css-blank-pseudo": "dist/cli.cjs" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/css-declaration-sorter": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz", - "integrity": "sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==", - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.0.9" - } - }, - "node_modules/css-has-pseudo": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz", - "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==", - "dependencies": { - "postcss-selector-parser": "^6.0.9" - }, - "bin": { - "css-has-pseudo": "dist/cli.cjs" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/css-loader": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz", - "integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==", - "dependencies": { - "icss-utils": "^5.1.0", - "postcss": "^8.4.33", - "postcss-modules-extract-imports": "^3.1.0", - "postcss-modules-local-by-default": "^4.0.5", - "postcss-modules-scope": "^3.2.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "@rspack/core": "0.x || 1.x", - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "webpack": { - "optional": true - } - } - }, - "node_modules/css-minimizer-webpack-plugin": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz", - "integrity": "sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==", - "dependencies": { - "cssnano": "^5.0.6", - "jest-worker": "^27.0.2", - "postcss": "^8.3.5", - "schema-utils": "^4.0.0", - "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "@parcel/css": { - "optional": true - }, - "clean-css": { - "optional": true - }, - "csso": { - "optional": true - }, - "esbuild": { - "optional": true - } - } - }, - "node_modules/css-minimizer-webpack-plugin/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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/css-prefers-color-scheme": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", - "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", - "bin": { - "css-prefers-color-scheme": "dist/cli.cjs" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/css-select": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", - "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.0.1", - "domhandler": "^4.3.1", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-select-base-adapter": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", - "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" - }, - "node_modules/css-tree": { - "version": "1.0.0-alpha.37", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", - "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", - "dependencies": { - "mdn-data": "2.0.4", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/css-tree/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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/cssdb": { - "version": "7.11.2", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.11.2.tgz", - "integrity": "sha512-lhQ32TFkc1X4eTefGfYPvgovRSzIMofHkigfH8nWtyRL4XJLsRhJFreRvEgKzept7x1rjBuy3J/MurXLaFxW/A==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - } - ] - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cssnano": { - "version": "5.1.15", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.15.tgz", - "integrity": "sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==", - "dependencies": { - "cssnano-preset-default": "^5.2.14", - "lilconfig": "^2.0.3", - "yaml": "^1.10.2" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/cssnano" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/cssnano-preset-default": { - "version": "5.2.14", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz", - "integrity": "sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==", - "dependencies": { - "css-declaration-sorter": "^6.3.1", - "cssnano-utils": "^3.1.0", - "postcss-calc": "^8.2.3", - "postcss-colormin": "^5.3.1", - "postcss-convert-values": "^5.1.3", - "postcss-discard-comments": "^5.1.2", - "postcss-discard-duplicates": "^5.1.0", - "postcss-discard-empty": "^5.1.1", - "postcss-discard-overridden": "^5.1.0", - "postcss-merge-longhand": "^5.1.7", - "postcss-merge-rules": "^5.1.4", - "postcss-minify-font-values": "^5.1.0", - "postcss-minify-gradients": "^5.1.1", - "postcss-minify-params": "^5.1.4", - "postcss-minify-selectors": "^5.2.1", - "postcss-normalize-charset": "^5.1.0", - "postcss-normalize-display-values": "^5.1.0", - "postcss-normalize-positions": "^5.1.1", - "postcss-normalize-repeat-style": "^5.1.1", - "postcss-normalize-string": "^5.1.0", - "postcss-normalize-timing-functions": "^5.1.0", - "postcss-normalize-unicode": "^5.1.1", - "postcss-normalize-url": "^5.1.0", - "postcss-normalize-whitespace": "^5.1.1", - "postcss-ordered-values": "^5.1.3", - "postcss-reduce-initial": "^5.1.2", - "postcss-reduce-transforms": "^5.1.0", - "postcss-svgo": "^5.1.0", - "postcss-unique-selectors": "^5.1.1" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/cssnano-utils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", - "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/csso": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", - "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", - "dependencies": { - "css-tree": "^1.1.2" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/csso/node_modules/css-tree": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", - "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", - "dependencies": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/csso/node_modules/mdn-data": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" - }, - "node_modules/csso/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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cssom": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==" - }, - "node_modules/cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "dependencies": { - "cssom": "~0.3.6" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cssstyle/node_modules/cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" - }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" - }, - "node_modules/damerau-levenshtein": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", - "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==" - }, - "node_modules/data-urls": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", - "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", - "dependencies": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/data-view-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", - "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/data-view-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", - "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/data-view-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", - "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decimal.js": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" - }, - "node_modules/dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==" - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/default-gateway": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", - "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", - "dependencies": { - "execa": "^5.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "engines": { - "node": ">=8" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" - }, - "node_modules/detect-port-alt": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", - "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", - "dependencies": { - "address": "^1.0.1", - "debug": "^2.6.0" - }, - "bin": { - "detect": "bin/detect-port", - "detect-port": "bin/detect-port" - }, - "engines": { - "node": ">= 4.2.1" - } - }, - "node_modules/detect-port-alt/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/detect-port-alt/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" - }, - "node_modules/diff-sequences": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", - "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "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==", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" - }, - "node_modules/dns-packet": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", - "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", - "dependencies": { - "@leichtgewicht/ip-codec": "^2.0.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dom-converter": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", - "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", - "dependencies": { - "utila": "~0.4" - } - }, - "node_modules/dom-helpers": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", - "dependencies": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" - } - }, - "node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", - "deprecated": "Use your platform's native DOMException instead", - "dependencies": { - "webidl-conversions": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/domexception/node_modules/webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/dotenv": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", - "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", - "engines": { - "node": ">=10" - } - }, - "node_modules/dotenv-expand": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", - "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" - }, - "node_modules/duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "node_modules/ejs": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", - "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", - "dependencies": { - "jake": "^10.8.5" - }, - "bin": { - "ejs": "bin/cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.4.794", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.794.tgz", - "integrity": "sha512-6FApLtsYhDCY0Vglq3AptsdxQ+PJLc6AxlAM0HjEihUAiOPPbkASEsq9gtxUeZY9o0sJIEa3WnF0vVH4VT4iug==" - }, - "node_modules/emittery": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", - "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" - }, - "node_modules/emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/enhanced-resolve": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz", - "integrity": "sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/error-stack-parser": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", - "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", - "dependencies": { - "stackframe": "^1.3.4" - } - }, - "node_modules/es-abstract": { - "version": "1.23.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", - "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.3", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "data-view-buffer": "^1.0.1", - "data-view-byte-length": "^1.0.1", - "data-view-byte-offset": "^1.0.0", - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.0.3", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.4", - "get-symbol-description": "^1.0.2", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "hasown": "^2.0.2", - "internal-slot": "^1.0.7", - "is-array-buffer": "^3.0.4", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.1", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.3", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.13", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", - "object-keys": "^1.1.1", - "object.assign": "^4.1.5", - "regexp.prototype.flags": "^1.5.2", - "safe-array-concat": "^1.1.2", - "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.9", - "string.prototype.trimend": "^1.0.8", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-length": "^1.0.1", - "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.6", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.15" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-array-method-boxes-properly": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", - "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==" - }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-iterator-helpers": { - "version": "1.0.19", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.19.tgz", - "integrity": "sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", - "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.0.3", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "globalthis": "^1.0.3", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "iterator.prototype": "^1.1.2", - "safe-array-concat": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-module-lexer": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.3.tgz", - "integrity": "sha512-i1gCgmR9dCl6Vil6UKPI/trA69s08g/syhiDK9TG0Nf1RJjjFI+AzoWW7sPufzkgYAn861skuCwJa0pIIHYxvg==" - }, - "node_modules/es-object-atoms": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", - "dependencies": { - "get-intrinsic": "^1.2.4", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", - "dependencies": { - "hasown": "^2.0.0" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "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==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/escodegen/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==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-config-react-app": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz", - "integrity": "sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA==", - "dependencies": { - "@babel/core": "^7.16.0", - "@babel/eslint-parser": "^7.16.3", - "@rushstack/eslint-patch": "^1.1.0", - "@typescript-eslint/eslint-plugin": "^5.5.0", - "@typescript-eslint/parser": "^5.5.0", - "babel-preset-react-app": "^10.0.1", - "confusing-browser-globals": "^1.0.11", - "eslint-plugin-flowtype": "^8.0.3", - "eslint-plugin-import": "^2.25.3", - "eslint-plugin-jest": "^25.3.0", - "eslint-plugin-jsx-a11y": "^6.5.1", - "eslint-plugin-react": "^7.27.1", - "eslint-plugin-react-hooks": "^4.3.0", - "eslint-plugin-testing-library": "^5.0.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "eslint": "^8.0.0" - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-module-utils": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz", - "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==", - "dependencies": { - "debug": "^3.2.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-flowtype": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz", - "integrity": "sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ==", - "dependencies": { - "lodash": "^4.17.21", - "string-natural-compare": "^3.0.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "@babel/plugin-syntax-flow": "^7.14.5", - "@babel/plugin-transform-react-jsx": "^7.14.9", - "eslint": "^8.1.0" - } - }, - "node_modules/eslint-plugin-import": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", - "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", - "dependencies": { - "array-includes": "^3.1.7", - "array.prototype.findlastindex": "^1.2.3", - "array.prototype.flat": "^1.3.2", - "array.prototype.flatmap": "^1.3.2", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.8.0", - "hasown": "^2.0.0", - "is-core-module": "^2.13.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.7", - "object.groupby": "^1.0.1", - "object.values": "^1.1.7", - "semver": "^6.3.1", - "tsconfig-paths": "^3.15.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" - } - }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-plugin-jest": { - "version": "25.7.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-25.7.0.tgz", - "integrity": "sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==", - "dependencies": { - "@typescript-eslint/experimental-utils": "^5.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - }, - "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^4.0.0 || ^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "@typescript-eslint/eslint-plugin": { - "optional": true - }, - "jest": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-jsx-a11y": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.8.0.tgz", - "integrity": "sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA==", - "dependencies": { - "@babel/runtime": "^7.23.2", - "aria-query": "^5.3.0", - "array-includes": "^3.1.7", - "array.prototype.flatmap": "^1.3.2", - "ast-types-flow": "^0.0.8", - "axe-core": "=4.7.0", - "axobject-query": "^3.2.1", - "damerau-levenshtein": "^1.0.8", - "emoji-regex": "^9.2.2", - "es-iterator-helpers": "^1.0.15", - "hasown": "^2.0.0", - "jsx-ast-utils": "^3.3.5", - "language-tags": "^1.0.9", - "minimatch": "^3.1.2", - "object.entries": "^1.1.7", - "object.fromentries": "^2.0.7" - }, - "engines": { - "node": ">=4.0" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" - } - }, - "node_modules/eslint-plugin-react": { - "version": "7.34.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.2.tgz", - "integrity": "sha512-2HCmrU+/JNigDN6tg55cRDKCQWicYAPB38JGSFDQt95jDm8rrvSUo7YPkOIm5l6ts1j1zCvysNcasvfTMQzUOw==", - "dependencies": { - "array-includes": "^3.1.8", - "array.prototype.findlast": "^1.2.5", - "array.prototype.flatmap": "^1.3.2", - "array.prototype.toreversed": "^1.1.2", - "array.prototype.tosorted": "^1.1.3", - "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.0.19", - "estraverse": "^5.3.0", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.8", - "object.fromentries": "^2.0.8", - "object.hasown": "^1.1.4", - "object.values": "^1.2.0", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.5", - "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.11" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" - } - }, - "node_modules/eslint-plugin-react-hooks": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", - "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" - } - }, - "node_modules/eslint-plugin-react/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.5", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", - "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-plugin-testing-library": { - "version": "5.11.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.11.1.tgz", - "integrity": "sha512-5eX9e1Kc2PqVRed3taaLnAAqPZGEX75C+M/rXzUAI3wIg/ZxzUm1OVAwfe/O+vE+6YXOLetSe9g5GKD2ecXipw==", - "dependencies": { - "@typescript-eslint/utils": "^5.58.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0", - "npm": ">=6" - }, - "peerDependencies": { - "eslint": "^7.5.0 || ^8.0.0" - } - }, - "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-webpack-plugin": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-3.2.0.tgz", - "integrity": "sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w==", - "dependencies": { - "@types/eslint": "^7.29.0 || ^8.4.1", - "jest-worker": "^28.0.2", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0", - "webpack": "^5.0.0" - } - }, - "node_modules/eslint-webpack-plugin/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint-webpack-plugin/node_modules/jest-worker": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", - "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/eslint-webpack-plugin/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/eslint/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/eslint/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==" - }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/eslint/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/eslint/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==" - }, - "node_modules/eslint/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/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==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/eslint/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "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==", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-walker": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", - "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==" - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "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/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", - "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", - "dependencies": { - "@jest/types": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/express": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", - "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.2", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.6.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "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==" - }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "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.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/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==", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" - }, - "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/faye-websocket": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", - "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", - "dependencies": { - "websocket-driver": ">=0.5.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/file-loader": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", - "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", - "dependencies": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/file-loader/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "dependencies": { - "minimatch": "^5.0.1" - } - }, - "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/filesize": { - "version": "8.0.7", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", - "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==", - "engines": { - "node": ">= 0.4.0" - } - }, - "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==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/find-cache-dir": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/avajs/find-cache-dir?sponsor=1" - } - }, - "node_modules/find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" - }, - "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==", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==" - }, - "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/foreground-child/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==", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/fork-ts-checker-webpack-plugin": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.3.tgz", - "integrity": "sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==", - "dependencies": { - "@babel/code-frame": "^7.8.3", - "@types/json-schema": "^7.0.5", - "chalk": "^4.1.0", - "chokidar": "^3.4.2", - "cosmiconfig": "^6.0.0", - "deepmerge": "^4.2.2", - "fs-extra": "^9.0.0", - "glob": "^7.1.6", - "memfs": "^3.1.2", - "minimatch": "^3.0.4", - "schema-utils": "2.7.0", - "semver": "^7.3.2", - "tapable": "^1.0.0" - }, - "engines": { - "node": ">=10", - "yarn": ">=1.0.0" - }, - "peerDependencies": { - "eslint": ">= 6", - "typescript": ">= 2.7", - "vue-template-compiler": "*", - "webpack": ">= 4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - }, - "vue-template-compiler": { - "optional": true - } - } - }, - "node_modules/fork-ts-checker-webpack-plugin/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/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==" - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/cosmiconfig": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", - "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.7.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", - "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", - "dependencies": { - "@types/json-schema": "^7.0.4", - "ajv": "^6.12.2", - "ajv-keywords": "^3.4.1" - }, - "engines": { - "node": ">= 8.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/tapable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", - "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fraction.js": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", - "engines": { - "node": "*" - }, - "funding": { - "type": "patreon", - "url": "https://github.com/sponsors/rawify" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/fs-monkey": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz", - "integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==" - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "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==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "engines": { - "node": ">=6.9.0" - } - }, - "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==", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-own-enumerable-property-symbols": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", - "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==" - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "engines": { - "node": ">=8.0.0" - } - }, - "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==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-symbol-description": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", - "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", - "dependencies": { - "call-bind": "^1.0.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" - }, - "node_modules/global-modules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", - "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", - "dependencies": { - "global-prefix": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", - "dependencies": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/global-prefix/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/globalthis": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", - "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==" - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==" - }, - "node_modules/gzip-size": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", - "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", - "dependencies": { - "duplexer": "^0.1.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/handle-thing": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", - "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" - }, - "node_modules/harmony-reflect": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", - "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==" - }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==", - "engines": { - "node": ">=4" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "bin": { - "he": "bin/he" - } - }, - "node_modules/hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "dependencies": { - "react-is": "^16.7.0" - } - }, - "node_modules/hoist-non-react-statics/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, - "node_modules/hoopy": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", - "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==", - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", - "dependencies": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" - } - }, - "node_modules/hpack.js/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/hpack.js/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==", - "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/hpack.js/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==" - }, - "node_modules/hpack.js/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==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/html-encoding-sniffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", - "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", - "dependencies": { - "whatwg-encoding": "^1.0.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/html-entities": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", - "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/mdevils" - }, - { - "type": "patreon", - "url": "https://patreon.com/mdevils" - } - ] - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==" - }, - "node_modules/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", - "dependencies": { - "camel-case": "^4.1.2", - "clean-css": "^5.2.2", - "commander": "^8.3.0", - "he": "^1.2.0", - "param-case": "^3.0.4", - "relateurl": "^0.2.7", - "terser": "^5.10.0" - }, - "bin": { - "html-minifier-terser": "cli.js" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/html-webpack-plugin": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.0.tgz", - "integrity": "sha512-iwaY4wzbe48AfKLZ/Cc8k0L+FKG6oSNRaZ8x5A/T/IVDGyXcbHncM9TdDa93wn0FsSm82FhTKW7f3vS61thXAw==", - "dependencies": { - "@types/html-minifier-terser": "^6.0.0", - "html-minifier-terser": "^6.0.2", - "lodash": "^4.17.21", - "pretty-error": "^4.0.0", - "tapable": "^2.0.0" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/html-webpack-plugin" - }, - "peerDependencies": { - "@rspack/core": "0.x || 1.x", - "webpack": "^5.20.0" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "webpack": { - "optional": true - } - } - }, - "node_modules/htmlparser2": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", - "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" - } - }, - "node_modules/http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==" - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-parser-js": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", - "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" - }, - "node_modules/http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/http-proxy-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", - "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", - "dependencies": { - "@types/http-proxy": "^1.17.8", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "@types/express": "^4.17.13" - }, - "peerDependenciesMeta": { - "@types/express": { - "optional": true - } - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "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==", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/icss-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/idb": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", - "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==" - }, - "node_modules/identity-obj-proxy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", - "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==", - "dependencies": { - "harmony-reflect": "^1.4.6" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/immer": { - "version": "9.0.21", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", - "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/immer" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "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==", - "engines": { - "node": ">=4" - } - }, - "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" - }, - "node_modules/internal-slot": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", - "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", - "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ipaddr.js": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", - "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", - "engines": { - "node": ">= 10" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", - "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==" - }, - "node_modules/is-async-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", - "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dependencies": { - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-data-view": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", - "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", - "dependencies": { - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-finalizationregistry": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", - "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", - "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==" - }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", - "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" - }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", - "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-root": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", - "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-set": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", - "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", - "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", - "dependencies": { - "call-bind": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", - "dependencies": { - "which-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" - }, - "node_modules/is-weakmap": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", - "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakset": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", - "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", - "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report/node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/istanbul-lib-report/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps/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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/iterator.prototype": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", - "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", - "dependencies": { - "define-properties": "^1.2.1", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "reflect.getprototypeof": "^1.0.4", - "set-function-name": "^2.0.1" - } - }, - "node_modules/jackspeak": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.0.tgz", - "integrity": "sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/jake": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.1.tgz", - "integrity": "sha512-61btcOHNnLnsOdtLgA5efqQWjnSi/vow5HbI7HMdKKWqvrKR1bLK3BPlJn9gcSaP2ewuamUSMB5XEy76KUIS2w==", - "dependencies": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.4", - "minimatch": "^3.1.2" - }, - "bin": { - "jake": "bin/cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jake/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jake/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jake/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jake/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==" - }, - "node_modules/jake/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jake/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", - "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", - "dependencies": { - "@jest/core": "^27.5.1", - "import-local": "^3.0.2", - "jest-cli": "^27.5.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", - "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==", - "dependencies": { - "@jest/types": "^27.5.1", - "execa": "^5.0.0", - "throat": "^6.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-circus": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz", - "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==", - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "expect": "^27.5.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3", - "throat": "^6.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-circus/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-circus/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-circus/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-circus/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==" - }, - "node_modules/jest-circus/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-circus/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-cli": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz", - "integrity": "sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==", - "dependencies": { - "@jest/core": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "import-local": "^3.0.2", - "jest-config": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "prompts": "^2.0.1", - "yargs": "^16.2.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-cli/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-cli/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-cli/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-cli/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==" - }, - "node_modules/jest-cli/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-cli/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-config": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz", - "integrity": "sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==", - "dependencies": { - "@babel/core": "^7.8.0", - "@jest/test-sequencer": "^27.5.1", - "@jest/types": "^27.5.1", - "babel-jest": "^27.5.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.1", - "graceful-fs": "^4.2.9", - "jest-circus": "^27.5.1", - "jest-environment-jsdom": "^27.5.1", - "jest-environment-node": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-jasmine2": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-runner": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-config/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-config/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-config/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-config/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==" - }, - "node_modules/jest-config/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-config/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-diff": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-diff/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-diff/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-diff/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-diff/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==" - }, - "node_modules/jest-diff/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-diff/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-docblock": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", - "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-each": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", - "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", - "dependencies": { - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "jest-get-type": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-each/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-each/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-each/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-each/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==" - }, - "node_modules/jest-each/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-each/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-environment-jsdom": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", - "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1", - "jsdom": "^16.6.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", - "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", - "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/graceful-fs": "^4.1.2", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^27.5.1", - "jest-serializer": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "micromatch": "^4.0.4", - "walker": "^1.0.7" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-jasmine2": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz", - "integrity": "sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==", - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/source-map": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "expect": "^27.5.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1", - "throat": "^6.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-jasmine2/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-jasmine2/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-jasmine2/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-jasmine2/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==" - }, - "node_modules/jest-jasmine2/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-jasmine2/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-leak-detector": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", - "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", - "dependencies": { - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", - "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-matcher-utils/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-matcher-utils/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-matcher-utils/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-matcher-utils/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==" - }, - "node_modules/jest-matcher-utils/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-matcher-utils/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-message-util/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-message-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-message-util/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-message-util/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==" - }, - "node_modules/jest-message-util/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-message-util/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-mock": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", - "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", - "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", - "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", - "dependencies": { - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "resolve": "^1.20.0", - "resolve.exports": "^1.1.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", - "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", - "dependencies": { - "@jest/types": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-snapshot": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-resolve/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-resolve/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-resolve/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==" - }, - "node_modules/jest-resolve/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-resolve/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runner": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", - "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", - "dependencies": { - "@jest/console": "^27.5.1", - "@jest/environment": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.8.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^27.5.1", - "jest-environment-jsdom": "^27.5.1", - "jest-environment-node": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-leak-detector": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "source-map-support": "^0.5.6", - "throat": "^6.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runner/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-runner/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-runner/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-runner/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==" - }, - "node_modules/jest-runner/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runner/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runtime": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", - "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/globals": "^27.5.1", - "@jest/source-map": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "execa": "^5.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-mock": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runtime/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-runtime/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-runtime/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-runtime/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==" - }, - "node_modules/jest-runtime/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runtime/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-serializer": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", - "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==", - "dependencies": { - "@types/node": "*", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", - "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", - "dependencies": { - "@babel/core": "^7.7.2", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.0.0", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/babel__traverse": "^7.0.4", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^27.5.1", - "graceful-fs": "^4.2.9", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-util": "^27.5.1", - "natural-compare": "^1.4.0", - "pretty-format": "^27.5.1", - "semver": "^7.3.2" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-snapshot/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-snapshot/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-snapshot/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==" - }, - "node_modules/jest-snapshot/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-snapshot/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-util/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-util/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-util/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==" - }, - "node_modules/jest-util/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-util/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-validate": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", - "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", - "dependencies": { - "@jest/types": "^27.5.1", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^27.5.1", - "leven": "^3.1.0", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-validate/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-validate/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-validate/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-validate/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==" - }, - "node_modules/jest-validate/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-validate/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watch-typeahead": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-1.1.0.tgz", - "integrity": "sha512-Va5nLSJTN7YFtC2jd+7wsoe1pNe5K4ShLux/E5iHEwlB9AxaxmggY7to9KUqKojhaJw3aXqt5WAb4jGPOolpEw==", - "dependencies": { - "ansi-escapes": "^4.3.1", - "chalk": "^4.0.0", - "jest-regex-util": "^28.0.0", - "jest-watcher": "^28.0.0", - "slash": "^4.0.0", - "string-length": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "jest": "^27.0.0 || ^28.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/@jest/console": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", - "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", - "dependencies": { - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3", - "slash": "^3.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/@jest/console/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watch-typeahead/node_modules/@jest/test-result": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", - "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", - "dependencies": { - "@jest/console": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/@jest/types": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", - "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", - "dependencies": { - "@jest/schemas": "^28.1.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/@types/yargs": { - "version": "17.0.32", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", - "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-watch-typeahead/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-watch-typeahead/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-watch-typeahead/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-watch-typeahead/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==" - }, - "node_modules/jest-watch-typeahead/node_modules/emittery": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", - "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/jest-watch-typeahead/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watch-typeahead/node_modules/jest-message-util": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", - "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^28.1.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^28.1.3", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/jest-message-util/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watch-typeahead/node_modules/jest-regex-util": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", - "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/jest-util": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", - "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", - "dependencies": { - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/jest-watcher": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", - "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", - "dependencies": { - "@jest/test-result": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.10.2", - "jest-util": "^28.1.3", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/jest-watcher/node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-watch-typeahead/node_modules/jest-watcher/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==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watch-typeahead/node_modules/pretty-format": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", - "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", - "dependencies": { - "@jest/schemas": "^28.1.3", - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-watch-typeahead/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" - }, - "node_modules/jest-watch-typeahead/node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watch-typeahead/node_modules/string-length": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-5.0.1.tgz", - "integrity": "sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==", - "dependencies": { - "char-regex": "^2.0.0", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watch-typeahead/node_modules/string-length/node_modules/char-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-2.0.1.tgz", - "integrity": "sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw==", - "engines": { - "node": ">=12.20" - } - }, - "node_modules/jest-watch-typeahead/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/jest-watch-typeahead/node_modules/strip-ansi/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/jest-watch-typeahead/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watcher": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", - "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", - "dependencies": { - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "jest-util": "^27.5.1", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-watcher/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-watcher/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-watcher/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-watcher/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==" - }, - "node_modules/jest-watcher/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watcher/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/jest-worker/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/jiti": { - "version": "1.21.3", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.3.tgz", - "integrity": "sha512-uy2bNX5zQ+tESe+TiC7ilGRz8AtRGmnJH55NC5S0nSUjvvvM2hJHmefHErugGXN4pNv4Qx7vLsnNw9qJ9mtIsw==", - "bin": { - "jiti": "bin/jiti.js" - } - }, - "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==" - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsdom": { - "version": "16.7.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", - "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", - "dependencies": { - "abab": "^2.0.5", - "acorn": "^8.2.4", - "acorn-globals": "^6.0.0", - "cssom": "^0.4.4", - "cssstyle": "^2.3.0", - "data-urls": "^2.0.0", - "decimal.js": "^10.2.1", - "domexception": "^2.0.1", - "escodegen": "^2.0.0", - "form-data": "^3.0.0", - "html-encoding-sniffer": "^2.0.1", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.0", - "parse5": "6.0.1", - "saxes": "^5.0.1", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.0.0", - "w3c-hr-time": "^1.0.2", - "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.1.0", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.5.0", - "ws": "^7.4.6", - "xml-name-validator": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "canvas": "^2.5.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" - }, - "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==" - }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsonpath": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/jsonpath/-/jsonpath-1.1.1.tgz", - "integrity": "sha512-l6Cg7jRpixfbgoWgkrl77dgEj8RPvND0wMH6TwQmi9Qs4TFfS9u5cUFnbeKTwj5ga5Y3BTGGNI28k117LJ009w==", - "dependencies": { - "esprima": "1.2.2", - "static-eval": "2.0.2", - "underscore": "1.12.1" - } - }, - "node_modules/jsonpath/node_modules/esprima": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.2.tgz", - "integrity": "sha512-+JpPZam9w5DuJ3Q67SqsMGtiHKENSMRVoxvArfJZK01/BfLEObtZ6orJa/MtoGNR/rfMgp5837T41PAmTwAv/A==", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/jsonpointer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", - "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jsx-ast-utils": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "engines": { - "node": ">=6" - } - }, - "node_modules/klona": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", - "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/language-subtag-registry": { - "version": "0.3.23", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", - "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==" - }, - "node_modules/language-tags": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", - "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", - "dependencies": { - "language-subtag-registry": "^0.3.20" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/launch-editor": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.1.tgz", - "integrity": "sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw==", - "dependencies": { - "picocolors": "^1.0.0", - "shell-quote": "^1.8.1" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", - "engines": { - "node": ">=10" - } - }, - "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==" - }, - "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "engines": { - "node": ">=6.11.5" - } - }, - "node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "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==", - "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==" - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" - }, - "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==" - }, - "node_modules/lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" - }, - "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==" - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/magic-string": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", - "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", - "dependencies": { - "sourcemap-codec": "^1.4.8" - } - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/mdn-data": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", - "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/memfs": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", - "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", - "dependencies": { - "fs-monkey": "^1.0.4" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" - }, - "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==" - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "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==", - "engines": { - "node": ">=6" - } - }, - "node_modules/mini-css-extract-plugin": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.0.tgz", - "integrity": "sha512-Zs1YsZVfemekSZG+44vBsYTLQORkPMwnlv+aehcxK/NLKC+EGhDB39/YePYYqx/sTk6NnYpuqikhSn7+JIevTA==", - "dependencies": { - "schema-utils": "^4.0.0", - "tapable": "^2.2.1" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/multicast-dns": { - "version": "7.2.5", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", - "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", - "dependencies": { - "dns-packet": "^5.2.2", - "thunky": "^1.0.2" - }, - "bin": { - "multicast-dns": "cli.js" - } - }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" - }, - "node_modules/natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==" - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "engines": { - "node": ">= 0.6" - } - }, - "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==" - }, - "node_modules/no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "dependencies": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, - "node_modules/node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", - "engines": { - "node": ">= 6.13.0" - } - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==" - }, - "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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==", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/nwsapi": { - "version": "2.2.10", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.10.tgz", - "integrity": "sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==" - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", - "dependencies": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.entries": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", - "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", - "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.getownpropertydescriptors": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.8.tgz", - "integrity": "sha512-qkHIGe4q0lSYMv0XI4SsBTJz3WaURhLvd0lKSgtVuOsJ2krg4SgMw3PIRQFMp07yi++UR3se2mkcLqsBNpBb/A==", - "dependencies": { - "array.prototype.reduce": "^1.0.6", - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0", - "gopd": "^1.0.1", - "safe-array-concat": "^1.1.2" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.groupby": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", - "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.hasown": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.4.tgz", - "integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==", - "dependencies": { - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.values": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", - "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "engines": { - "node": ">= 0.8" - } - }, - "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==", - "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==", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/open": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", - "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", - "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "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==", - "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==", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-retry": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", - "dependencies": { - "@types/retry": "0.12.0", - "retry": "^0.13.1" - }, - "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==", - "engines": { - "node": ">=6" - } - }, - "node_modules/param-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", - "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "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==", - "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==", - "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/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/pascal-case": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", - "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "engines": { - "node": ">=0.10.0" - } - }, - "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==", - "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==" - }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "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/path-scurry/node_modules/lru-cache": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", - "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", - "engines": { - "node": "14 || >=16.14" - } - }, - "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" - }, - "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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" - }, - "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-up": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", - "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", - "dependencies": { - "find-up": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-up/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up/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==", - "engines": { - "node": ">=4" - } - }, - "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-attribute-case-insensitive": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz", - "integrity": "sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==", - "dependencies": { - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-browser-comments": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-browser-comments/-/postcss-browser-comments-4.0.0.tgz", - "integrity": "sha512-X9X9/WN3KIvY9+hNERUqX9gncsgBA25XaeR+jshHz2j8+sYyHktHw1JdKuMjeLpGktXidqDhA7b/qm1mrBDmgg==", - "engines": { - "node": ">=8" - }, - "peerDependencies": { - "browserslist": ">=4", - "postcss": ">=8" - } - }, - "node_modules/postcss-calc": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", - "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", - "dependencies": { - "postcss-selector-parser": "^6.0.9", - "postcss-value-parser": "^4.2.0" - }, - "peerDependencies": { - "postcss": "^8.2.2" - } - }, - "node_modules/postcss-clamp": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz", - "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=7.6.0" - }, - "peerDependencies": { - "postcss": "^8.4.6" - } - }, - "node_modules/postcss-color-functional-notation": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz", - "integrity": "sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-color-hex-alpha": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz", - "integrity": "sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-color-rebeccapurple": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz", - "integrity": "sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-colormin": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.1.tgz", - "integrity": "sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==", - "dependencies": { - "browserslist": "^4.21.4", - "caniuse-api": "^3.0.0", - "colord": "^2.9.1", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-convert-values": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz", - "integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==", - "dependencies": { - "browserslist": "^4.21.4", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-custom-media": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz", - "integrity": "sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.3" - } - }, - "node_modules/postcss-custom-properties": { - "version": "12.1.11", - "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.11.tgz", - "integrity": "sha512-0IDJYhgU8xDv1KY6+VgUwuQkVtmYzRwu+dMjnmdMafXYv86SWqfxkc7qdDvWS38vsjaEtv8e0vGOUQrAiMBLpQ==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-custom-selectors": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz", - "integrity": "sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==", - "dependencies": { - "postcss-selector-parser": "^6.0.4" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.3" - } - }, - "node_modules/postcss-dir-pseudo-class": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz", - "integrity": "sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA==", - "dependencies": { - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-discard-comments": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", - "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-discard-duplicates": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", - "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-discard-empty": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", - "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-discard-overridden": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", - "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-double-position-gradients": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz", - "integrity": "sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ==", - "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-env-function": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz", - "integrity": "sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-flexbugs-fixes": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-5.0.2.tgz", - "integrity": "sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==", - "peerDependencies": { - "postcss": "^8.1.4" - } - }, - "node_modules/postcss-focus-visible": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz", - "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==", - "dependencies": { - "postcss-selector-parser": "^6.0.9" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-focus-within": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz", - "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==", - "dependencies": { - "postcss-selector-parser": "^6.0.9" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-font-variant": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", - "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-gap-properties": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", - "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==", - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-image-set-function": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz", - "integrity": "sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-import": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", - "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", - "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-initial": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", - "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-js": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", - "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", - "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.4.21" - } - }, - "node_modules/postcss-lab-function": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz", - "integrity": "sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==", - "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-load-config": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", - "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "lilconfig": "^3.0.0", - "yaml": "^2.3.4" - }, - "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/postcss-load-config/node_modules/lilconfig": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", - "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/postcss-load-config/node_modules/yaml": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.3.tgz", - "integrity": "sha512-sntgmxj8o7DE7g/Qi60cqpLBA3HG3STcDA0kO+WfB05jEKhZMbY7umNm2rBpQvsmZ16/lPXCJGW2672dgOUkrg==", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/postcss-loader": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", - "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", - "dependencies": { - "cosmiconfig": "^7.0.0", - "klona": "^2.0.5", - "semver": "^7.3.5" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "postcss": "^7.0.0 || ^8.0.1", - "webpack": "^5.0.0" - } - }, - "node_modules/postcss-logical": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", - "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-media-minmax": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", - "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-merge-longhand": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz", - "integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==", - "dependencies": { - "postcss-value-parser": "^4.2.0", - "stylehacks": "^5.1.1" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-merge-rules": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz", - "integrity": "sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==", - "dependencies": { - "browserslist": "^4.21.4", - "caniuse-api": "^3.0.0", - "cssnano-utils": "^3.1.0", - "postcss-selector-parser": "^6.0.5" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-minify-font-values": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", - "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-minify-gradients": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", - "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", - "dependencies": { - "colord": "^2.9.1", - "cssnano-utils": "^3.1.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-minify-params": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz", - "integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==", - "dependencies": { - "browserslist": "^4.21.4", - "cssnano-utils": "^3.1.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-minify-selectors": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz", - "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==", - "dependencies": { - "postcss-selector-parser": "^6.0.5" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-modules-extract-imports": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", - "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-local-by-default": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz", - "integrity": "sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==", - "dependencies": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-scope": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz", - "integrity": "sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==", - "dependencies": { - "postcss-selector-parser": "^6.0.4" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", - "dependencies": { - "icss-utils": "^5.0.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-nested": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", - "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", - "dependencies": { - "postcss-selector-parser": "^6.0.11" - }, - "engines": { - "node": ">=12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.2.14" - } - }, - "node_modules/postcss-nesting": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.2.0.tgz", - "integrity": "sha512-EwMkYchxiDiKUhlJGzWsD9b2zvq/r2SSubcRrgP+jujMXFzqvANLt16lJANC+5uZ6hjI7lpRmI6O8JIl+8l1KA==", - "dependencies": { - "@csstools/selector-specificity": "^2.0.0", - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-normalize": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize/-/postcss-normalize-10.0.1.tgz", - "integrity": "sha512-+5w18/rDev5mqERcG3W5GZNMJa1eoYYNGo8gB7tEwaos0ajk3ZXAI4mHGcNT47NE+ZnZD1pEpUOFLvltIwmeJA==", - "dependencies": { - "@csstools/normalize.css": "*", - "postcss-browser-comments": "^4", - "sanitize.css": "*" - }, - "engines": { - "node": ">= 12" - }, - "peerDependencies": { - "browserslist": ">= 4", - "postcss": ">= 8" - } - }, - "node_modules/postcss-normalize-charset": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", - "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-display-values": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", - "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-positions": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz", - "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-repeat-style": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz", - "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-string": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", - "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-timing-functions": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", - "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-unicode": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz", - "integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==", - "dependencies": { - "browserslist": "^4.21.4", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-url": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", - "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", - "dependencies": { - "normalize-url": "^6.0.1", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-whitespace": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", - "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-opacity-percentage": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.3.tgz", - "integrity": "sha512-An6Ba4pHBiDtyVpSLymUUERMo2cU7s+Obz6BTrS+gxkbnSBNKSuD0AVUc+CpBMrpVPKKfoVz0WQCX+Tnst0i4A==", - "funding": [ - { - "type": "kofi", - "url": "https://ko-fi.com/mrcgrtz" - }, - { - "type": "liberapay", - "url": "https://liberapay.com/mrcgrtz" - } - ], - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-ordered-values": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz", - "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==", - "dependencies": { - "cssnano-utils": "^3.1.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-overflow-shorthand": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz", - "integrity": "sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-page-break": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", - "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", - "peerDependencies": { - "postcss": "^8" - } - }, - "node_modules/postcss-place": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.5.tgz", - "integrity": "sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-preset-env": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.8.3.tgz", - "integrity": "sha512-T1LgRm5uEVFSEF83vHZJV2z19lHg4yJuZ6gXZZkqVsqv63nlr6zabMH3l4Pc01FQCyfWVrh2GaUeCVy9Po+Aag==", - "dependencies": { - "@csstools/postcss-cascade-layers": "^1.1.1", - "@csstools/postcss-color-function": "^1.1.1", - "@csstools/postcss-font-format-keywords": "^1.0.1", - "@csstools/postcss-hwb-function": "^1.0.2", - "@csstools/postcss-ic-unit": "^1.0.1", - "@csstools/postcss-is-pseudo-class": "^2.0.7", - "@csstools/postcss-nested-calc": "^1.0.0", - "@csstools/postcss-normalize-display-values": "^1.0.1", - "@csstools/postcss-oklab-function": "^1.1.1", - "@csstools/postcss-progressive-custom-properties": "^1.3.0", - "@csstools/postcss-stepped-value-functions": "^1.0.1", - "@csstools/postcss-text-decoration-shorthand": "^1.0.0", - "@csstools/postcss-trigonometric-functions": "^1.0.2", - "@csstools/postcss-unset-value": "^1.0.2", - "autoprefixer": "^10.4.13", - "browserslist": "^4.21.4", - "css-blank-pseudo": "^3.0.3", - "css-has-pseudo": "^3.0.4", - "css-prefers-color-scheme": "^6.0.3", - "cssdb": "^7.1.0", - "postcss-attribute-case-insensitive": "^5.0.2", - "postcss-clamp": "^4.1.0", - "postcss-color-functional-notation": "^4.2.4", - "postcss-color-hex-alpha": "^8.0.4", - "postcss-color-rebeccapurple": "^7.1.1", - "postcss-custom-media": "^8.0.2", - "postcss-custom-properties": "^12.1.10", - "postcss-custom-selectors": "^6.0.3", - "postcss-dir-pseudo-class": "^6.0.5", - "postcss-double-position-gradients": "^3.1.2", - "postcss-env-function": "^4.0.6", - "postcss-focus-visible": "^6.0.4", - "postcss-focus-within": "^5.0.4", - "postcss-font-variant": "^5.0.0", - "postcss-gap-properties": "^3.0.5", - "postcss-image-set-function": "^4.0.7", - "postcss-initial": "^4.0.1", - "postcss-lab-function": "^4.2.1", - "postcss-logical": "^5.0.4", - "postcss-media-minmax": "^5.0.0", - "postcss-nesting": "^10.2.0", - "postcss-opacity-percentage": "^1.1.2", - "postcss-overflow-shorthand": "^3.0.4", - "postcss-page-break": "^3.0.4", - "postcss-place": "^7.0.5", - "postcss-pseudo-class-any-link": "^7.1.6", - "postcss-replace-overflow-wrap": "^4.0.0", - "postcss-selector-not": "^6.0.1", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-pseudo-class-any-link": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz", - "integrity": "sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==", - "dependencies": { - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-reduce-initial": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz", - "integrity": "sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==", - "dependencies": { - "browserslist": "^4.21.4", - "caniuse-api": "^3.0.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-reduce-transforms": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", - "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-replace-overflow-wrap": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", - "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", - "peerDependencies": { - "postcss": "^8.0.3" - } - }, - "node_modules/postcss-selector-not": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-6.0.1.tgz", - "integrity": "sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ==", - "dependencies": { - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.0.tgz", - "integrity": "sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-svgo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", - "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", - "dependencies": { - "postcss-value-parser": "^4.2.0", - "svgo": "^2.7.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-svgo/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "engines": { - "node": ">= 10" - } - }, - "node_modules/postcss-svgo/node_modules/css-tree": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", - "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", - "dependencies": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/postcss-svgo/node_modules/mdn-data": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" - }, - "node_modules/postcss-svgo/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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-svgo/node_modules/svgo": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", - "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", - "dependencies": { - "@trysound/sax": "0.2.0", - "commander": "^7.2.0", - "css-select": "^4.1.3", - "css-tree": "^1.1.3", - "csso": "^4.2.0", - "picocolors": "^1.0.0", - "stable": "^0.1.8" - }, - "bin": { - "svgo": "bin/svgo" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/postcss-unique-selectors": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", - "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", - "dependencies": { - "postcss-selector-parser": "^6.0.5" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.1.tgz", - "integrity": "sha512-7CAwy5dRsxs8PHXT3twixW9/OEll8MLE0VRPCJyl7CkS6VHGPSlsVaWTiASPTyGyYRyApxlaWTzwUxVNrhcwDg==", - "dev": true, - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/pretty-bytes": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", - "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pretty-error": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", - "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", - "dependencies": { - "lodash": "^4.17.20", - "renderkid": "^3.0.0" - } - }, - "node_modules/pretty-format": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", - "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", - "dependencies": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "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==" - }, - "node_modules/promise": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", - "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", - "dependencies": { - "asap": "~2.0.6" - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/prop-types/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/proxy-addr/node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "engines": { - "node": ">=6" - } - }, - "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==", - "engines": { - "node": ">=0.6.0", - "teleport": ">=0.2.0" - } - }, - "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" - }, - "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==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/raf": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", - "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", - "dependencies": { - "performance-now": "^2.1.0" - } - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/raw-body/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/raw-body/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", - "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-app-polyfill": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz", - "integrity": "sha512-sZ41cxiU5llIB003yxxQBYrARBqe0repqPTTYBTmMqTz9szeBbE37BehCE891NZsmdZqqP+xWKdT3eo3vOzN8w==", - "dependencies": { - "core-js": "^3.19.2", - "object-assign": "^4.1.1", - "promise": "^8.1.0", - "raf": "^3.4.1", - "regenerator-runtime": "^0.13.9", - "whatwg-fetch": "^3.6.2" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/react-app-polyfill/node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" - }, - "node_modules/react-dev-utils": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", - "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==", - "dependencies": { - "@babel/code-frame": "^7.16.0", - "address": "^1.1.2", - "browserslist": "^4.18.1", - "chalk": "^4.1.2", - "cross-spawn": "^7.0.3", - "detect-port-alt": "^1.1.6", - "escape-string-regexp": "^4.0.0", - "filesize": "^8.0.6", - "find-up": "^5.0.0", - "fork-ts-checker-webpack-plugin": "^6.5.0", - "global-modules": "^2.0.0", - "globby": "^11.0.4", - "gzip-size": "^6.0.0", - "immer": "^9.0.7", - "is-root": "^2.1.0", - "loader-utils": "^3.2.0", - "open": "^8.4.0", - "pkg-up": "^3.1.0", - "prompts": "^2.4.2", - "react-error-overlay": "^6.0.11", - "recursive-readdir": "^2.2.2", - "shell-quote": "^1.7.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/react-dev-utils/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/react-dev-utils/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/react-dev-utils/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/react-dev-utils/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==" - }, - "node_modules/react-dev-utils/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-dev-utils/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-dev-utils/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/react-dev-utils/node_modules/loader-utils": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.3.1.tgz", - "integrity": "sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==", - "engines": { - "node": ">= 12.13.0" - } - }, - "node_modules/react-dev-utils/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-dev-utils/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-dev-utils/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-dev-utils/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" - }, - "peerDependencies": { - "react": "^18.3.1" - } - }, - "node_modules/react-error-overlay": { - "version": "6.0.11", - "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", - "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" - }, - "node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" - }, - "node_modules/react-refresh": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", - "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-scripts": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", - "integrity": "sha512-8VAmEm/ZAwQzJ+GOMLbBsTdDKOpuZh7RPs0UymvBR2vRk4iZWCskjbFnxqjrzoIvlNNRZ3QJFx6/qDSi6zSnaQ==", - "dependencies": { - "@babel/core": "^7.16.0", - "@pmmmwh/react-refresh-webpack-plugin": "^0.5.3", - "@svgr/webpack": "^5.5.0", - "babel-jest": "^27.4.2", - "babel-loader": "^8.2.3", - "babel-plugin-named-asset-import": "^0.3.8", - "babel-preset-react-app": "^10.0.1", - "bfj": "^7.0.2", - "browserslist": "^4.18.1", - "camelcase": "^6.2.1", - "case-sensitive-paths-webpack-plugin": "^2.4.0", - "css-loader": "^6.5.1", - "css-minimizer-webpack-plugin": "^3.2.0", - "dotenv": "^10.0.0", - "dotenv-expand": "^5.1.0", - "eslint": "^8.3.0", - "eslint-config-react-app": "^7.0.1", - "eslint-webpack-plugin": "^3.1.1", - "file-loader": "^6.2.0", - "fs-extra": "^10.0.0", - "html-webpack-plugin": "^5.5.0", - "identity-obj-proxy": "^3.0.0", - "jest": "^27.4.3", - "jest-resolve": "^27.4.2", - "jest-watch-typeahead": "^1.0.0", - "mini-css-extract-plugin": "^2.4.5", - "postcss": "^8.4.4", - "postcss-flexbugs-fixes": "^5.0.2", - "postcss-loader": "^6.2.1", - "postcss-normalize": "^10.0.1", - "postcss-preset-env": "^7.0.1", - "prompts": "^2.4.2", - "react-app-polyfill": "^3.0.0", - "react-dev-utils": "^12.0.1", - "react-refresh": "^0.11.0", - "resolve": "^1.20.0", - "resolve-url-loader": "^4.0.0", - "sass-loader": "^12.3.0", - "semver": "^7.3.5", - "source-map-loader": "^3.0.0", - "style-loader": "^3.3.1", - "tailwindcss": "^3.0.2", - "terser-webpack-plugin": "^5.2.5", - "webpack": "^5.64.4", - "webpack-dev-server": "^4.6.0", - "webpack-manifest-plugin": "^4.0.2", - "workbox-webpack-plugin": "^6.4.1" - }, - "bin": { - "react-scripts": "bin/react-scripts.js" - }, - "engines": { - "node": ">=14.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - }, - "peerDependencies": { - "react": ">= 16", - "typescript": "^3.2.1 || ^4" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", - "dependencies": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": ">=16.6.0", - "react-dom": ">=16.6.0" - } - }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "dependencies": { - "pify": "^2.3.0" - } - }, - "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==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/recursive-readdir": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", - "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", - "dependencies": { - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/reflect.getprototypeof": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", - "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.1", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "globalthis": "^1.0.3", - "which-builtin-type": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" - }, - "node_modules/regenerate-unicode-properties": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", - "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", - "dependencies": { - "regenerate": "^1.4.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" - }, - "node_modules/regenerator-transform": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", - "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", - "dependencies": { - "@babel/runtime": "^7.8.4" - } - }, - "node_modules/regex-parser": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.3.0.tgz", - "integrity": "sha512-TVILVSz2jY5D47F4mA4MppkBrafEaiUWJO/TcZHEIuI13AqoZMkK1WMA4Om1YkYbTx+9Ki1/tSUXbceyr9saRg==" - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", - "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", - "dependencies": { - "call-bind": "^1.0.6", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "set-function-name": "^2.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regexpu-core": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", - "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", - "dependencies": { - "@babel/regjsgen": "^0.8.0", - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsparser": "^0.9.1", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", - "dependencies": { - "jsesc": "~0.5.0" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", - "bin": { - "jsesc": "bin/jsesc" - } - }, - "node_modules/relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/renderkid": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", - "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", - "dependencies": { - "css-select": "^4.1.3", - "dom-converter": "^0.2.0", - "htmlparser2": "^6.1.0", - "lodash": "^4.17.21", - "strip-ansi": "^6.0.1" - } - }, - "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==", - "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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-url-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz", - "integrity": "sha512-05VEMczVREcbtT7Bz+C+96eUO5HDNvdthIiMB34t7FcF8ehcu4wC0sSgPUubs3XW2Q3CNLJk/BJrCU9wVRymiA==", - "dependencies": { - "adjust-sourcemap-loader": "^4.0.0", - "convert-source-map": "^1.7.0", - "loader-utils": "^2.0.0", - "postcss": "^7.0.35", - "source-map": "0.6.1" - }, - "engines": { - "node": ">=8.9" - }, - "peerDependencies": { - "rework": "1.0.1", - "rework-visit": "1.0.0" - }, - "peerDependenciesMeta": { - "rework": { - "optional": true - }, - "rework-visit": { - "optional": true - } - } - }, - "node_modules/resolve-url-loader/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" - }, - "node_modules/resolve-url-loader/node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" - }, - "node_modules/resolve-url-loader/node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", - "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/resolve-url-loader/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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve.exports": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz", - "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==", - "engines": { - "node": ">=10" - } - }, - "node_modules/retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rollup": { - "version": "2.79.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", - "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=10.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/rollup-plugin-terser": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", - "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", - "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser", - "dependencies": { - "@babel/code-frame": "^7.10.4", - "jest-worker": "^26.2.1", - "serialize-javascript": "^4.0.0", - "terser": "^5.0.0" - }, - "peerDependencies": { - "rollup": "^2.0.0" - } - }, - "node_modules/rollup-plugin-terser/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/rollup-plugin-terser/node_modules/jest-worker": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", - "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/rollup-plugin-terser/node_modules/serialize-javascript": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", - "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/rollup-plugin-terser/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "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==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-array-concat": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", - "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", - "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safe-regex-test": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-regex": "^1.1.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/sanitize.css": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", - "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==" - }, - "node_modules/sass-loader": { - "version": "12.6.0", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", - "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==", - "dependencies": { - "klona": "^2.0.4", - "neo-async": "^2.6.2" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "fibers": ">= 3.1.0", - "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", - "sass": "^1.3.0", - "sass-embedded": "*", - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "fibers": { - "optional": true - }, - "node-sass": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - } - } - }, - "node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, - "node_modules/saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", - "dependencies": { - "xmlchars": "^2.2.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "dependencies": { - "loose-envify": "^1.1.0" - } - }, - "node_modules/schema-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", - "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/schema-utils/node_modules/ajv": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.16.0.tgz", - "integrity": "sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.4.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/schema-utils/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/schema-utils/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==" - }, - "node_modules/select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==" - }, - "node_modules/selfsigned": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", - "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", - "dependencies": { - "@types/node-forge": "^1.3.0", - "node-forge": "^1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/send/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==" - }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", - "dependencies": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/serve-index/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/serve-index/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-index/node_modules/http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-index/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" - }, - "node_modules/serve-index/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/serve-index/node_modules/setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" - }, - "node_modules/serve-index/node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "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==", - "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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/shell-quote": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", - "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==" - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/sockjs": { - "version": "0.3.24", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", - "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", - "dependencies": { - "faye-websocket": "^0.11.3", - "uuid": "^8.3.2", - "websocket-driver": "^0.7.4" - } - }, - "node_modules/source-list-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", - "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==" - }, - "node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-loader": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.2.tgz", - "integrity": "sha512-BokxPoLjyl3iOrgkWaakaxqnelAJSS+0V+De0kKIq6lyWrXuiPgYTGp6z3iHmqljKAaLXwZa+ctD8GccRJeVvg==", - "dependencies": { - "abab": "^2.0.5", - "iconv-lite": "^0.6.3", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-support/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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "deprecated": "Please use @jridgewell/sourcemap-codec instead" - }, - "node_modules/spdy": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", - "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", - "dependencies": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/spdy-transport": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", - "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", - "dependencies": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" - }, - "node_modules/stable": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", - "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", - "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility" - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/stackframe": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", - "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==" - }, - "node_modules/static-eval": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.0.2.tgz", - "integrity": "sha512-N/D219Hcr2bPjLxPiV+TQE++Tsmrady7TqAJugLy7Xk1EumfDWS/f5dtBbkRCGE7wKKXuYockQoj8Rm2/pVKyg==", - "dependencies": { - "escodegen": "^1.8.1" - } - }, - "node_modules/static-eval/node_modules/escodegen": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", - "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=4.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/static-eval/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/static-eval/node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/static-eval/node_modules/optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/static-eval/node_modules/prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/static-eval/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==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-eval/node_modules/type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", - "dependencies": { - "prelude-ls": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-natural-compare": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz", - "integrity": "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==" - }, - "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==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs": { - "name": "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==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/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==" - }, - "node_modules/string-width/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==" - }, - "node_modules/string.prototype.matchall": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", - "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "regexp.prototype.flags": "^1.5.2", - "set-function-name": "^2.0.2", - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", - "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", - "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/stringify-object": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", - "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", - "dependencies": { - "get-own-enumerable-property-symbols": "^3.0.0", - "is-obj": "^1.0.1", - "is-regexp": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "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==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz", - "integrity": "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==", - "engines": { - "node": ">=10" - } - }, - "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==", - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/style-loader": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.4.tgz", - "integrity": "sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==", - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/stylehacks": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", - "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==", - "dependencies": { - "browserslist": "^4.21.4", - "postcss-selector-parser": "^6.0.4" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/stylis": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", - "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" - }, - "node_modules/sucrase": { - "version": "3.35.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", - "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "glob": "^10.3.10", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/sucrase/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/sucrase/node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/sucrase/node_modules/glob": { - "version": "10.4.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.1.tgz", - "integrity": "sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/sucrase/node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "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==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/supports-hyperlinks": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", - "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", - "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "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==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/svg-parser": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", - "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" - }, - "node_modules/svgo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", - "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", - "deprecated": "This SVGO version is no longer supported. Upgrade to v2.x.x.", - "dependencies": { - "chalk": "^2.4.1", - "coa": "^2.0.2", - "css-select": "^2.0.0", - "css-select-base-adapter": "^0.1.1", - "css-tree": "1.0.0-alpha.37", - "csso": "^4.0.2", - "js-yaml": "^3.13.1", - "mkdirp": "~0.5.1", - "object.values": "^1.1.0", - "sax": "~1.2.4", - "stable": "^0.1.8", - "unquote": "~1.1.1", - "util.promisify": "~1.0.0" - }, - "bin": { - "svgo": "bin/svgo" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/svgo/node_modules/css-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", - "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^3.2.1", - "domutils": "^1.7.0", - "nth-check": "^1.0.2" - } - }, - "node_modules/svgo/node_modules/css-what": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", - "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/svgo/node_modules/dom-serializer": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", - "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", - "dependencies": { - "domelementtype": "^2.0.1", - "entities": "^2.0.0" - } - }, - "node_modules/svgo/node_modules/domutils": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", - "dependencies": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "node_modules/svgo/node_modules/domutils/node_modules/domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" - }, - "node_modules/svgo/node_modules/nth-check": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", - "dependencies": { - "boolbase": "~1.0.0" - } - }, - "node_modules/symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" - }, - "node_modules/tailwindcss": { - "version": "3.4.4", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.4.tgz", - "integrity": "sha512-ZoyXOdJjISB7/BcLTR6SEsLgKtDStYyYZVLsUtWChO4Ps20CBad7lfJKVDiejocV4ME1hLmyY0WJE3hSDcmQ2A==", - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.5.3", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.3.0", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.21.0", - "lilconfig": "^2.1.0", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.23", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.1", - "postcss-nested": "^6.0.1", - "postcss-selector-parser": "^6.0.11", - "resolve": "^1.22.2", - "sucrase": "^3.32.0" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/temp-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", - "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/tempy": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.6.0.tgz", - "integrity": "sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==", - "dependencies": { - "is-stream": "^2.0.0", - "temp-dir": "^2.0.0", - "type-fest": "^0.16.0", - "unique-string": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tempy/node_modules/type-fest": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", - "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/terminal-link": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", - "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", - "dependencies": { - "ansi-escapes": "^4.2.1", - "supports-hyperlinks": "^2.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/terser": { - "version": "5.31.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.1.tgz", - "integrity": "sha512-37upzU1+viGvuFtBo9NPufCb9dwM0+l9hMxYyWfBA+fbwrPqNJAhbZ6W47bBFnZHKHTUBnMvi87434qq+qnxOg==", - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "5.3.10", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", - "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.20", - "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.26.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, - "node_modules/terser-webpack-plugin/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/throat": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz", - "integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==" - }, - "node_modules/thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==" - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "engines": { - "node": ">=4" - } - }, - "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==", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tough-cookie": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", - "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", - "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tough-cookie/node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/tr46": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", - "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", - "dependencies": { - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tryer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", - "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==" - }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" - }, - "node_modules/tsconfig-paths": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "node_modules/tsconfig-paths/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/tsconfig-paths/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==", - "engines": { - "node": ">=4" - } - }, - "node_modules/tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typed-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", - "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", - "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, - "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/underscore": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", - "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==" - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" - }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "dependencies": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", - "engines": { - "node": ">=4" - } - }, - "node_modules/unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "dependencies": { - "crypto-random-string": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/unquote": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", - "integrity": "sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==" - }, - "node_modules/upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", - "engines": { - "node": ">=4", - "yarn": "*" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", - "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.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==" - }, - "node_modules/util.promisify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", - "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.2", - "has-symbols": "^1.0.1", - "object.getownpropertydescriptors": "^2.1.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/utila": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==" - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/v8-to-istanbul": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", - "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0", - "source-map": "^0.7.3" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/v8-to-istanbul/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", - "deprecated": "Use your platform's native performance.now() and performance.timeOrigin.", - "dependencies": { - "browser-process-hrtime": "^1.0.0" - } - }, - "node_modules/w3c-xmlserializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", - "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", - "dependencies": { - "xml-name-validator": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/watchpack": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", - "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", - "dependencies": { - "minimalistic-assert": "^1.0.0" - } - }, - "node_modules/web-vitals": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-2.1.4.tgz", - "integrity": "sha512-sVWcwhU5mX6crfI5Vd2dC4qchyTqxV8URinzt25XqVh+bHEPGH4C3NPrNionCP7Obx59wrYEbNlw4Z8sjALzZg==" - }, - "node_modules/webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", - "engines": { - "node": ">=10.4" - } - }, - "node_modules/webpack": { - "version": "5.91.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.91.0.tgz", - "integrity": "sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw==", - "dependencies": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^1.0.5", - "@webassemblyjs/ast": "^1.12.1", - "@webassemblyjs/wasm-edit": "^1.12.1", - "@webassemblyjs/wasm-parser": "^1.12.1", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.9.0", - "browserslist": "^4.21.10", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.16.0", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.10", - "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-dev-middleware": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", - "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==", - "dependencies": { - "colorette": "^2.0.10", - "memfs": "^3.4.3", - "mime-types": "^2.1.31", - "range-parser": "^1.2.1", - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/webpack-dev-server": { - "version": "4.15.2", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz", - "integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==", - "dependencies": { - "@types/bonjour": "^3.5.9", - "@types/connect-history-api-fallback": "^1.3.5", - "@types/express": "^4.17.13", - "@types/serve-index": "^1.9.1", - "@types/serve-static": "^1.13.10", - "@types/sockjs": "^0.3.33", - "@types/ws": "^8.5.5", - "ansi-html-community": "^0.0.8", - "bonjour-service": "^1.0.11", - "chokidar": "^3.5.3", - "colorette": "^2.0.10", - "compression": "^1.7.4", - "connect-history-api-fallback": "^2.0.0", - "default-gateway": "^6.0.3", - "express": "^4.17.3", - "graceful-fs": "^4.2.6", - "html-entities": "^2.3.2", - "http-proxy-middleware": "^2.0.3", - "ipaddr.js": "^2.0.1", - "launch-editor": "^2.6.0", - "open": "^8.0.9", - "p-retry": "^4.5.0", - "rimraf": "^3.0.2", - "schema-utils": "^4.0.0", - "selfsigned": "^2.1.1", - "serve-index": "^1.9.1", - "sockjs": "^0.3.24", - "spdy": "^4.0.2", - "webpack-dev-middleware": "^5.3.4", - "ws": "^8.13.0" - }, - "bin": { - "webpack-dev-server": "bin/webpack-dev-server.js" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.37.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "webpack": { - "optional": true - }, - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-dev-server/node_modules/ws": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz", - "integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/webpack-manifest-plugin": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-4.1.1.tgz", - "integrity": "sha512-YXUAwxtfKIJIKkhg03MKuiFAD72PlrqCiwdwO4VEXdRO5V0ORCNwaOwAZawPZalCbmH9kBDmXnNeQOw+BIEiow==", - "dependencies": { - "tapable": "^2.0.0", - "webpack-sources": "^2.2.0" - }, - "engines": { - "node": ">=12.22.0" - }, - "peerDependencies": { - "webpack": "^4.44.2 || ^5.47.0" - } - }, - "node_modules/webpack-manifest-plugin/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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-manifest-plugin/node_modules/webpack-sources": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz", - "integrity": "sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==", - "dependencies": { - "source-list-map": "^2.0.1", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/webpack/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/webpack/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "dependencies": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/whatwg-encoding": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", - "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", - "dependencies": { - "iconv-lite": "0.4.24" - } - }, - "node_modules/whatwg-encoding/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/whatwg-fetch": { - "version": "3.6.20", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", - "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==" - }, - "node_modules/whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" - }, - "node_modules/whatwg-url": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", - "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", - "dependencies": { - "lodash": "^4.7.0", - "tr46": "^2.1.0", - "webidl-conversions": "^6.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-builtin-type": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz", - "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", - "dependencies": { - "function.prototype.name": "^1.1.5", - "has-tostringtag": "^1.0.0", - "is-async-function": "^2.0.0", - "is-date-object": "^1.0.5", - "is-finalizationregistry": "^1.0.2", - "is-generator-function": "^1.0.10", - "is-regex": "^1.1.4", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.9" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-collection": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", - "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", - "dependencies": { - "is-map": "^2.0.3", - "is-set": "^2.0.3", - "is-weakmap": "^2.0.2", - "is-weakset": "^2.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/workbox-background-sync": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-6.6.0.tgz", - "integrity": "sha512-jkf4ZdgOJxC9u2vztxLuPT/UjlH7m/nWRQ/MgGL0v8BJHoZdVGJd18Kck+a0e55wGXdqyHO+4IQTk0685g4MUw==", - "dependencies": { - "idb": "^7.0.1", - "workbox-core": "6.6.0" - } - }, - "node_modules/workbox-broadcast-update": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-6.6.0.tgz", - "integrity": "sha512-nm+v6QmrIFaB/yokJmQ/93qIJ7n72NICxIwQwe5xsZiV2aI93MGGyEyzOzDPVz5THEr5rC3FJSsO3346cId64Q==", - "dependencies": { - "workbox-core": "6.6.0" - } - }, - "node_modules/workbox-build": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-6.6.0.tgz", - "integrity": "sha512-Tjf+gBwOTuGyZwMz2Nk/B13Fuyeo0Q84W++bebbVsfr9iLkDSo6j6PST8tET9HYA58mlRXwlMGpyWO8ETJiXdQ==", - "dependencies": { - "@apideck/better-ajv-errors": "^0.3.1", - "@babel/core": "^7.11.1", - "@babel/preset-env": "^7.11.0", - "@babel/runtime": "^7.11.2", - "@rollup/plugin-babel": "^5.2.0", - "@rollup/plugin-node-resolve": "^11.2.1", - "@rollup/plugin-replace": "^2.4.1", - "@surma/rollup-plugin-off-main-thread": "^2.2.3", - "ajv": "^8.6.0", - "common-tags": "^1.8.0", - "fast-json-stable-stringify": "^2.1.0", - "fs-extra": "^9.0.1", - "glob": "^7.1.6", - "lodash": "^4.17.20", - "pretty-bytes": "^5.3.0", - "rollup": "^2.43.1", - "rollup-plugin-terser": "^7.0.0", - "source-map": "^0.8.0-beta.0", - "stringify-object": "^3.3.0", - "strip-comments": "^2.0.1", - "tempy": "^0.6.0", - "upath": "^1.2.0", - "workbox-background-sync": "6.6.0", - "workbox-broadcast-update": "6.6.0", - "workbox-cacheable-response": "6.6.0", - "workbox-core": "6.6.0", - "workbox-expiration": "6.6.0", - "workbox-google-analytics": "6.6.0", - "workbox-navigation-preload": "6.6.0", - "workbox-precaching": "6.6.0", - "workbox-range-requests": "6.6.0", - "workbox-recipes": "6.6.0", - "workbox-routing": "6.6.0", - "workbox-strategies": "6.6.0", - "workbox-streams": "6.6.0", - "workbox-sw": "6.6.0", - "workbox-window": "6.6.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/workbox-build/node_modules/@apideck/better-ajv-errors": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.6.tgz", - "integrity": "sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==", - "dependencies": { - "json-schema": "^0.4.0", - "jsonpointer": "^5.0.0", - "leven": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "ajv": ">=8" - } - }, - "node_modules/workbox-build/node_modules/ajv": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.16.0.tgz", - "integrity": "sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.4.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/workbox-build/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/workbox-build/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==" - }, - "node_modules/workbox-build/node_modules/source-map": { - "version": "0.8.0-beta.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", - "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", - "dependencies": { - "whatwg-url": "^7.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/workbox-build/node_modules/tr46": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/workbox-build/node_modules/webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" - }, - "node_modules/workbox-build/node_modules/whatwg-url": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", - "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", - "dependencies": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - }, - "node_modules/workbox-cacheable-response": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-6.6.0.tgz", - "integrity": "sha512-JfhJUSQDwsF1Xv3EV1vWzSsCOZn4mQ38bWEBR3LdvOxSPgB65gAM6cS2CX8rkkKHRgiLrN7Wxoyu+TuH67kHrw==", - "deprecated": "workbox-background-sync@6.6.0", - "dependencies": { - "workbox-core": "6.6.0" - } - }, - "node_modules/workbox-core": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-6.6.0.tgz", - "integrity": "sha512-GDtFRF7Yg3DD859PMbPAYPeJyg5gJYXuBQAC+wyrWuuXgpfoOrIQIvFRZnQ7+czTIQjIr1DhLEGFzZanAT/3bQ==" - }, - "node_modules/workbox-expiration": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-6.6.0.tgz", - "integrity": "sha512-baplYXcDHbe8vAo7GYvyAmlS4f6998Jff513L4XvlzAOxcl8F620O91guoJ5EOf5qeXG4cGdNZHkkVAPouFCpw==", - "dependencies": { - "idb": "^7.0.1", - "workbox-core": "6.6.0" - } - }, - "node_modules/workbox-google-analytics": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-6.6.0.tgz", - "integrity": "sha512-p4DJa6OldXWd6M9zRl0H6vB9lkrmqYFkRQ2xEiNdBFp9U0LhsGO7hsBscVEyH9H2/3eZZt8c97NB2FD9U2NJ+Q==", - "deprecated": "It is not compatible with newer versions of GA starting with v4, as long as you are using GAv3 it should be ok, but the package is not longer being maintained", - "dependencies": { - "workbox-background-sync": "6.6.0", - "workbox-core": "6.6.0", - "workbox-routing": "6.6.0", - "workbox-strategies": "6.6.0" - } - }, - "node_modules/workbox-navigation-preload": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-6.6.0.tgz", - "integrity": "sha512-utNEWG+uOfXdaZmvhshrh7KzhDu/1iMHyQOV6Aqup8Mm78D286ugu5k9MFD9SzBT5TcwgwSORVvInaXWbvKz9Q==", - "dependencies": { - "workbox-core": "6.6.0" - } - }, - "node_modules/workbox-precaching": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-6.6.0.tgz", - "integrity": "sha512-eYu/7MqtRZN1IDttl/UQcSZFkHP7dnvr/X3Vn6Iw6OsPMruQHiVjjomDFCNtd8k2RdjLs0xiz9nq+t3YVBcWPw==", - "dependencies": { - "workbox-core": "6.6.0", - "workbox-routing": "6.6.0", - "workbox-strategies": "6.6.0" - } - }, - "node_modules/workbox-range-requests": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-6.6.0.tgz", - "integrity": "sha512-V3aICz5fLGq5DpSYEU8LxeXvsT//mRWzKrfBOIxzIdQnV/Wj7R+LyJVTczi4CQ4NwKhAaBVaSujI1cEjXW+hTw==", - "dependencies": { - "workbox-core": "6.6.0" - } - }, - "node_modules/workbox-recipes": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-6.6.0.tgz", - "integrity": "sha512-TFi3kTgYw73t5tg73yPVqQC8QQjxJSeqjXRO4ouE/CeypmP2O/xqmB/ZFBBQazLTPxILUQ0b8aeh0IuxVn9a6A==", - "dependencies": { - "workbox-cacheable-response": "6.6.0", - "workbox-core": "6.6.0", - "workbox-expiration": "6.6.0", - "workbox-precaching": "6.6.0", - "workbox-routing": "6.6.0", - "workbox-strategies": "6.6.0" - } - }, - "node_modules/workbox-routing": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-6.6.0.tgz", - "integrity": "sha512-x8gdN7VDBiLC03izAZRfU+WKUXJnbqt6PG9Uh0XuPRzJPpZGLKce/FkOX95dWHRpOHWLEq8RXzjW0O+POSkKvw==", - "dependencies": { - "workbox-core": "6.6.0" - } - }, - "node_modules/workbox-strategies": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-6.6.0.tgz", - "integrity": "sha512-eC07XGuINAKUWDnZeIPdRdVja4JQtTuc35TZ8SwMb1ztjp7Ddq2CJ4yqLvWzFWGlYI7CG/YGqaETntTxBGdKgQ==", - "dependencies": { - "workbox-core": "6.6.0" - } - }, - "node_modules/workbox-streams": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-6.6.0.tgz", - "integrity": "sha512-rfMJLVvwuED09CnH1RnIep7L9+mj4ufkTyDPVaXPKlhi9+0czCu+SJggWCIFbPpJaAZmp2iyVGLqS3RUmY3fxg==", - "dependencies": { - "workbox-core": "6.6.0", - "workbox-routing": "6.6.0" - } - }, - "node_modules/workbox-sw": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-6.6.0.tgz", - "integrity": "sha512-R2IkwDokbtHUE4Kus8pKO5+VkPHD2oqTgl+XJwh4zbF1HyjAbgNmK/FneZHVU7p03XUt9ICfuGDYISWG9qV/CQ==" - }, - "node_modules/workbox-webpack-plugin": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/workbox-webpack-plugin/-/workbox-webpack-plugin-6.6.0.tgz", - "integrity": "sha512-xNZIZHalboZU66Wa7x1YkjIqEy1gTR+zPM+kjrYJzqN7iurYZBctBLISyScjhkJKYuRrZUP0iqViZTh8rS0+3A==", - "dependencies": { - "fast-json-stable-stringify": "^2.1.0", - "pretty-bytes": "^5.4.1", - "upath": "^1.2.0", - "webpack-sources": "^1.4.3", - "workbox-build": "6.6.0" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "webpack": "^4.4.0 || ^5.9.0" - } - }, - "node_modules/workbox-webpack-plugin/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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/workbox-webpack-plugin/node_modules/webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "dependencies": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - } - }, - "node_modules/workbox-window": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-6.6.0.tgz", - "integrity": "sha512-L4N9+vka17d16geaJXXRjENLFldvkWy7JyGxElRD0JvBxvFEd8LOhr+uXCcar/NzAmIBRv9EZ+M+Qr4mOoBITw==", - "dependencies": { - "@types/trusted-types": "^2.0.2", - "workbox-core": "6.6.0" - } - }, - "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==", - "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/wrap-ansi-cjs": { - "name": "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==", - "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/wrap-ansi-cjs/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi-cjs/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==" - }, - "node_modules/wrap-ansi/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi/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==" - }, - "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==" - }, - "node_modules/write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dependencies": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" - }, - "node_modules/xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "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==", - "engines": { - "node": ">=10" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/examples/ratchet-moondream/package.json b/examples/ratchet-moondream/package.json deleted file mode 100644 index 2192cb8d..00000000 --- a/examples/ratchet-moondream/package.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "name": "ratchet-moondream", - "version": "0.1.0", - "private": true, - "dependencies": { - "@emotion/react": "^11.11.4", - "@emotion/styled": "^11.11.5", - "@mui/icons-material": "^5.15.19", - "@mui/material": "^5.15.19", - "@ratchet-ml/ratchet-web": "file:../../target/pkg/ratchet-web", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "react-scripts": "5.0.1", - "web-vitals": "^2.1.4" - }, - "scripts": { - "start": "react-scripts start", - "build": "react-scripts build", - "test": "react-scripts test", - "eject": "react-scripts eject" - }, - "eslintConfig": { - "extends": [ - "react-app", - "react-app/jest" - ] - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - }, - "devDependencies": { - "prettier": "3.3.1" - } -} diff --git a/examples/ratchet-moondream/public/index.html b/examples/ratchet-moondream/public/index.html deleted file mode 100644 index 5d1e5bc2..00000000 --- a/examples/ratchet-moondream/public/index.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - Ratchet Moondream - - - -
    - - - diff --git a/examples/ratchet-moondream/public/manifest.json b/examples/ratchet-moondream/public/manifest.json deleted file mode 100644 index 080d6c77..00000000 --- a/examples/ratchet-moondream/public/manifest.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "short_name": "React App", - "name": "Create React App Sample", - "icons": [ - { - "src": "favicon.ico", - "sizes": "64x64 32x32 24x24 16x16", - "type": "image/x-icon" - }, - { - "src": "logo192.png", - "type": "image/png", - "sizes": "192x192" - }, - { - "src": "logo512.png", - "type": "image/png", - "sizes": "512x512" - } - ], - "start_url": ".", - "display": "standalone", - "theme_color": "#000000", - "background_color": "#ffffff" -} diff --git a/examples/ratchet-moondream/public/robots.txt b/examples/ratchet-moondream/public/robots.txt deleted file mode 100644 index e9e57dc4..00000000 --- a/examples/ratchet-moondream/public/robots.txt +++ /dev/null @@ -1,3 +0,0 @@ -# https://www.robotstxt.org/robotstxt.html -User-agent: * -Disallow: diff --git a/examples/ratchet-moondream/src/App.css b/examples/ratchet-moondream/src/App.css deleted file mode 100644 index 74b5e053..00000000 --- a/examples/ratchet-moondream/src/App.css +++ /dev/null @@ -1,38 +0,0 @@ -.App { - text-align: center; -} - -.App-logo { - height: 40vmin; - pointer-events: none; -} - -@media (prefers-reduced-motion: no-preference) { - .App-logo { - animation: App-logo-spin infinite 20s linear; - } -} - -.App-header { - background-color: #282c34; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); - color: white; -} - -.App-link { - color: #61dafb; -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} diff --git a/examples/ratchet-moondream/src/App.js b/examples/ratchet-moondream/src/App.js deleted file mode 100644 index 340e48be..00000000 --- a/examples/ratchet-moondream/src/App.js +++ /dev/null @@ -1,242 +0,0 @@ -import "./App.css"; -import { Model, Quantization, default as init } from "@ratchet-ml/ratchet-web"; -import { styled } from "@mui/material/styles"; -import { useState, useEffect } from "react"; -import { - LinearProgress, - TextField, - Button, - Container, - Card, - CardMedia, - Stack, - Box, - Dialog, - DialogActions, - DialogContentText, - DialogTitle, - DialogContent, - Typography, - CardActions, - InputAdornment, - IconButton, -} from "@mui/material"; -import SendIcon from "@mui/icons-material/Send"; - -const VisuallyHiddenInput = styled("input")({ - clip: "rect(0 0 0 0)", - clipPath: "inset(50%)", - height: 1, - overflow: "hidden", - position: "absolute", - bottom: 0, - left: 0, - whiteSpace: "nowrap", - width: 1, -}); - -function App() { - const [question, setQuestion] = useState(""); - const [generatedText, setGeneratedText] = useState(""); - const [image, setImage] = useState(new Uint8Array()); - const [progress, setProgress] = useState(0); - const [isLoading, setIsLoading] = useState(true); - const [accepted, setAccepted] = useState(false); - const [isSupportedBrowser, setIsSupportedBrowser] = useState(true); - const [ratchetDBExists, setRatchetDBExists] = useState(false); - const [model, setModel] = useState(null); - const [isRunning, setIsRunning] = useState(false); - - useEffect(() => { - (async () => { - await init(); - setRatchetDBExists( - (await window.indexedDB.databases()) - .map((db) => db.name) - .includes("ratchet"), - ); - await setImage( - new Uint8Array( - await ( - await fetch( - "https://raw.githubusercontent.com/vikhyat/moondream/main/assets/demo-1.jpg", - ) - ).arrayBuffer(), - ), - ); - })(); - }, []); - - async function loadModel() { - setAccepted(true); - setProgress(2); - setModel( - await Model.load("Moondream", Quantization.Q8_0, (p) => setProgress(p)), - ); - setProgress(100); - setIsLoading(false); - } - - async function runModel() { - if (!model || isRunning) { - return; - } - - setGeneratedText(""); - - let cb = (s) => { - setGeneratedText((prevText) => { - return prevText + s; - }); - }; - - setIsRunning(true); - await model.run({ question: question, image_bytes: image, callback: cb }); - setIsRunning(false); - } - - async function handleUpload(e) { - if (e.target.files.length == 0) { - return; - } - setImage(new Uint8Array(await e.target.files[0].arrayBuffer())); - } - - async function keypress(e) { - if (e.key === "Enter") { - runModel(); - e.preventDefault(); - } - } - - async function deleteWeights() { - setAccepted(false); - setProgress(0); - setModel(null); - await window.indexedDB.deleteDatabase("ratchet"); - setIsLoading(true); - } - - return ( -
    - - - - {navigator.gpu ? "Load Model" : "Unsupported Browser"} - - - - {navigator.gpu - ? "This app requires downloading a 2.2GB model which may take a few minutes. If the model has been previously downloaded, it will be loaded from cache." - : "This app requires a browser that supports webgpu"} - - - {navigator.gpu ? ( - - - - ) : ( - <> - )} - - - - - Moondream by{" "} - Vikhyat running - on WebGpu via{" "} - Ratchet - - - - - - - - - - - - - setQuestion(e.target.value)} - onKeyDown={keypress} - InputProps={{ - endAdornment: ( - - - - - - ), - }} - /> - -
    - -
    - {isLoading && progress < 99 ? ( - - Downloading Weights... - - ) : ( - <> - )} - {isLoading && progress > 99 ? ( - - Preparing Weights... - - ) : ( - <> - )} -
    - {generatedText} -
    -
    -
    -
    - ); -} - -export default App; diff --git a/examples/ratchet-moondream/src/index.css b/examples/ratchet-moondream/src/index.css deleted file mode 100644 index 4a1df4db..00000000 --- a/examples/ratchet-moondream/src/index.css +++ /dev/null @@ -1,13 +0,0 @@ -body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", - "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", - monospace; -} diff --git a/examples/ratchet-moondream/src/index.js b/examples/ratchet-moondream/src/index.js deleted file mode 100644 index df868170..00000000 --- a/examples/ratchet-moondream/src/index.js +++ /dev/null @@ -1,11 +0,0 @@ -import React from "react"; -import ReactDOM from "react-dom/client"; -import "./index.css"; -import App from "./App"; - -const root = ReactDOM.createRoot(document.getElementById("root")); -root.render( - - - , -); From f562655c3c62ea067b54bcd5e213995541a5edc8 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 9 Mar 2025 00:58:20 -0700 Subject: [PATCH 126/590] Update pnpm-lock.yaml --- pnpm-lock.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d3f58d4b..563bdcbd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -26,9 +26,9 @@ importers: '@ffmpeg/util': specifier: ^0.12.1 version: 0.12.1 - '@ratchet-ml/ratchet-web-train': - specifier: link:../../target/pkg/ratchet-web-train - version: link:../../target/pkg/ratchet-web-train + '@ratchet-ml/ratchet-web': + specifier: link:../../target/pkg/ratchet-web + version: link:../../target/pkg/ratchet-web fix-webm-duration: specifier: ^1.0.5 version: 1.0.5 From 0e5800c510eb54bf1d1cbe9eac18bf0287c05af0 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 9 Mar 2025 00:58:33 -0700 Subject: [PATCH 127/590] Update vite version on ratchet-train-toy --- examples/ratchet-train-toy/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/ratchet-train-toy/package.json b/examples/ratchet-train-toy/package.json index 375b32e4..4a2d12b2 100644 --- a/examples/ratchet-train-toy/package.json +++ b/examples/ratchet-train-toy/package.json @@ -36,7 +36,7 @@ "tailwindcss": "^4.0.0", "typescript": "^5.0.0", "typescript-eslint": "^8.20.0", - "vite": "^6.1.0", + "vite": "^6.2.0", "vite-plugin-top-level-await": "^1.4.4", "vite-plugin-wasm": "^3.4.1" } From 66791dd898e88e28c8a2170be3ffd3bd461dd9c8 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 9 Mar 2025 01:03:19 -0700 Subject: [PATCH 128/590] Add using cache graph --- .../ratchet-train-toy/src/routes/+page.svelte | 58 ++++++++++++++++++- examples/ratchet-train-toy/src/trainWorker.ts | 7 +++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/examples/ratchet-train-toy/src/routes/+page.svelte b/examples/ratchet-train-toy/src/routes/+page.svelte index 5063d0c8..3336010a 100644 --- a/examples/ratchet-train-toy/src/routes/+page.svelte +++ b/examples/ratchet-train-toy/src/routes/+page.svelte @@ -17,11 +17,13 @@ let memoryChart: Chart; let lrChart: Chart; let accuracyChart: Chart; + let usingCacheChart: Chart; let canvas: HTMLCanvasElement; let speedCanvas: HTMLCanvasElement; let memoryCanvas: HTMLCanvasElement; let lrCanvas: HTMLCanvasElement; let accuracyCanvas: HTMLCanvasElement; + let usingCacheCanvas: HTMLCanvasElement; let attentionCanvases: HTMLCanvasElement[][] = []; let showAdvanced = false; let runCount = 0; @@ -63,7 +65,7 @@ // Track last parameter values to detect actual changes let lastParams: Record | null = null; - let lastTaskParams: Record | null = null; + let lastTaskParams: Record | null = null; // Add reactive statement to restart training when parameters change $: { @@ -380,6 +382,16 @@ pointStyle: false }); + // Add dataset to using cache chart + usingCacheChart.data.datasets.push({ + label: '', // Will be set by updateAllLabels + data: [], + borderColor: colors[runCount % colors.length], + tension: 0.1, + order: -runCount, + pointStyle: false + }); + // Add dataset to memory chart memoryChart.data.datasets.push({ label: '', // Will be set by updateAllLabels @@ -524,12 +536,15 @@ const currentLossDataset = chart.data.datasets[chart.data.datasets.length - 1]; const currentSpeedDataset = speedChart.data.datasets[speedChart.data.datasets.length - 1]; + const currentUsingCacheDataset = + usingCacheChart.data.datasets[usingCacheChart.data.datasets.length - 1]; const currentMemoryDataset = memoryChart.data.datasets[memoryChart.data.datasets.length - 1]; const currentLrDataset = lrChart.data.datasets[lrChart.data.datasets.length - 1]; currentLossDataset.data.push(loss); currentSpeedDataset.data.push(stepsPerSecond); + currentUsingCacheDataset.data.push(data.usingCache * 1); currentMemoryDataset.data.push(Number(data.usage_bytes) / (1024 * 1024)); // Convert bigint to number then to MB currentLrDataset.data.push(data.learning_rate); @@ -551,6 +566,7 @@ const labels = Array.from({ length: maxLength }, (_, i) => i.toString()); chart.data.labels = labels; speedChart.data.labels = labels; + usingCacheChart.data.labels = labels; memoryChart.data.labels = labels; lrChart.data.labels = labels; accuracyChart.data.labels = labels; @@ -615,6 +631,7 @@ chart.update(); speedChart.update(); + usingCacheChart.update(); memoryChart.update(); lrChart.update(); accuracyChart.update(); @@ -698,6 +715,42 @@ } }); + // Initialize using cache chart + usingCacheChart = new Chart(usingCacheCanvas, { + type: 'line', + data: { + labels: [], + datasets: [] + }, + options: { + responsive: true, + animation: false, + aspectRatio: 4, + scales: { + y: { + beginAtZero: true + } + }, + plugins: { + legend: { + display: false + }, + title: { + display: true, + text: 'Using Cache', + font: { + weight: 'bold' + } + } + }, + datasets: { + line: { + order: -1 + } + } + } + }); + // Initialize memory chart memoryChart = new Chart(memoryCanvas, { type: 'line', @@ -975,6 +1028,9 @@
    +
    + +
    diff --git a/examples/ratchet-train-toy/src/trainWorker.ts b/examples/ratchet-train-toy/src/trainWorker.ts index 76ed8d13..2b4453b3 100644 --- a/examples/ratchet-train-toy/src/trainWorker.ts +++ b/examples/ratchet-train-toy/src/trainWorker.ts @@ -21,6 +21,9 @@ export interface TrainerConfig { seed?: number; layernorm_position: string; label_smoothing: number; + caching_enabled: boolean; + inplace_support: boolean; + debug_selection: string; optimizer: { optimizer_type: string; lr: number; @@ -94,6 +97,9 @@ async function trainingLoop( // Train on the batch const result = await trainer.train_on_batch(input, target); + + const logOutput = await trainer.take_step_log(); + const usingCache = logOutput?.cached ?? false; const logits = result.get('logits') as Map; const loss = result.get('loss') as Map; const attn_masks = result.get('attn_masks') as Map; @@ -171,6 +177,7 @@ async function trainingLoop( if (sessionId === currentSession) { self.postMessage({ type: 'step', + usingCache, input: input.map((x) => x.map((t) => tokenizer.ids[t])), target: target.map((t) => t.map((t) => tokenizer.ids[t])), accuracy: winCount === null ? null : winCount / EVAL_TRIAL_COUNT, From 0460541ced5cabdc006689f7ec852516384f30ac Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 9 Mar 2025 01:07:43 -0700 Subject: [PATCH 129/590] Move trainWorker and tasks to $lib --- .../ratchet-train-toy/src/lib/trainWorker.ts | 227 ++++++++++++++++++ .../ratchet-train-toy/src/routes/+page.svelte | 4 +- 2 files changed, 229 insertions(+), 2 deletions(-) create mode 100644 examples/ratchet-train-toy/src/lib/trainWorker.ts diff --git a/examples/ratchet-train-toy/src/lib/trainWorker.ts b/examples/ratchet-train-toy/src/lib/trainWorker.ts new file mode 100644 index 00000000..d0ec1f29 --- /dev/null +++ b/examples/ratchet-train-toy/src/lib/trainWorker.ts @@ -0,0 +1,227 @@ +import { Trainer } from '@ratchet-ml/ratchet-web-train'; +import { type TaskConfigMap, tasks, type TaskSpec, type SimpleTokenizer } from '$lib/tasks'; + +let trainer: Trainer; +let sessionCounter = 0; +let currentSession = 0; +const trainingSessions: Record = {}; + +export interface TrainerConfig { + vocab_size: number; + n_embd: number; + n_layer: number; + n_head: number; + block_size: number; + batch_size: number; + dataset: keyof typeof tasks; + task_parameters: TaskConfigMap[keyof TaskConfigMap]; + activation: string; + attention_only: boolean; + position_encoding: string; + seed?: number; + layernorm_position: string; + label_smoothing: number; + caching_enabled: boolean; + inplace_support: boolean; + debug_selection: string; + optimizer: { + optimizer_type: string; + lr: number; + beta1: number; + beta2: number; + eps: number; + weight_decay: number; + momentum: number; + scheduler_type: string; + scheduler_factor: number; + scheduler_steps: number; + scheduler_eta_min: number; + }; +} + +function markStopTraining() { + Object.keys(trainingSessions).forEach((key) => { + trainingSessions[Number(key)] = false; + }); +} + +async function initializeTrainer(config: TrainerConfig) { + // Stop all existing training sessions + markStopTraining(); + + const task = tasks[config.dataset] as TaskSpec; + const tokenizer = task.createTokenizer(config.task_parameters); + + // Get the appropriate task generator + if (!task) { + console.error(`Unknown dataset type: ${config.dataset}`); + return; + } + + trainer = await new Trainer({ + ...config, + // Add 1 to the vocab size to account for the end-of-sequence token + vocab_size: tokenizer.lastToken + }); + self.postMessage({ type: 'modelReady' }); + currentSession = sessionCounter++; + trainingSessions[currentSession] = true; + trainingLoop(currentSession, config, task, tokenizer); +} + +async function trainingLoop( + sessionId: number, + config: TrainerConfig, + task: TaskSpec, + tokenizer: SimpleTokenizer +) { + if (!trainer) { + console.error('Trainer not initialized'); + return; + } + + let step = 0; + while (trainingSessions[sessionId]) { + // Skip if this isn't the current session anymore + if (sessionId !== currentSession) { + break; + } + + try { + // Combine batch size with task-specific parameters before invoking the generator + const taskConfig = { + batchSize: config.batch_size, + ...config.task_parameters + }; + const [input, target] = task.trainBatch(taskConfig); + + // Train on the batch + const result = await trainer.train_on_batch(input, target); + + const logOutput = await trainer.take_step_log(); + const usingCache = logOutput?.cached ?? false; + const logits = result.get('logits') as Map; + const loss = result.get('loss') as Map; + const attn_masks = result.get('attn_masks') as Map; + const usage_bytes = trainer.usage_bytes(); + + let winCount = null; + const EVAL_TRIAL_COUNT = 5; + + const { example: evalExample, metric: evalMetric } = task.eval(config.task_parameters); + + // After every 10 steps, evaluate the model + if (step % 10 === 0) { + const [streamingSequence, streamingTarget] = evalExample(); + let streamingExampleCompletion: number[] | null = null; + const streamingExampleLogits: number[] = []; + let streamingExampleMetric: boolean | null = null; + + // For the first eval, we'll stream it to the frontend + await trainer.generate( + streamingSequence, + /* max_tokens= */ streamingTarget.length + 1, + (tokens: number[], logitsObj: { shape: number[]; data: number[] }) => { + // For the first call (prompt), just store logits + if (streamingExampleCompletion === null) { + streamingExampleCompletion = []; + streamingExampleLogits.push(...logitsObj.data); + return; + } + + // For each subsequent token, append it and evaluate + streamingExampleCompletion.push(...tokens); + streamingExampleLogits.push(...logitsObj.data); + + // Run the metric on the current completion + const evalResult = evalMetric( + streamingExampleCompletion, + streamingTarget, + streamingSequence + ); + + streamingExampleMetric = evalResult.every((result) => result); + + // Send the streaming eval result to the frontend + if (sessionId === currentSession) { + self.postMessage({ + type: 'evalStreaming', + sequence: streamingSequence.map((t) => tokenizer.ids[t]), + completion: streamingExampleCompletion.map((t) => tokenizer.ids[t]), + target: streamingTarget.map((t) => tokenizer.ids[t]), + evalResult, + logits: { + data: streamingExampleLogits, + shape: logitsObj.shape + } + }); + } + } + ); + + winCount = streamingExampleMetric === true ? 1 : 0; + + // We'll do best of 5 evals, so we'll do the rest non-streaming + for (let i = 0; i < EVAL_TRIAL_COUNT - 1; i++) { + const [sequence, target] = evalExample(); + const result = await trainer.generate(sequence, target.length + 1); + const tokens = result.get('tokens') as number[]; + const evalResult = evalMetric(tokens, target, sequence); + if (evalResult.every((result) => result)) { + winCount++; + } + } + } + + // Only send message if this is still the current session + if (sessionId === currentSession) { + self.postMessage({ + type: 'step', + usingCache, + input: input.map((x) => x.map((t) => tokenizer.ids[t])), + target: target.map((t) => t.map((t) => tokenizer.ids[t])), + accuracy: winCount === null ? null : winCount / EVAL_TRIAL_COUNT, + loss: { + total: loss.get('total'), + tokens: loss.get('tokens') + }, + learning_rate: result.get('learning_rate'), + usage_bytes, + attn_masks: { + data: attn_masks.get('data'), + shape: attn_masks.get('shape') + }, + logits: { + data: logits.get('data'), + shape: logits.get('shape') + } + }); + } + } catch (error: Error | unknown) { + console.error('Training error in worker:', error); + if (sessionId === currentSession) { + self.postMessage({ + type: 'error', + error: error instanceof Error ? error.message : String(error) + }); + } + break; + } + step++; + // Yield to keep the worker responsive + await new Promise((resolve) => setTimeout(resolve, 0)); + } +} + +self.onmessage = async (e: MessageEvent) => { + if (e.data.type === 'stop') { + markStopTraining(); + return; + } + // If we receive a configuration object, initialize a new trainer + if (typeof e.data === 'object') { + await initializeTrainer(e.data); + } +}; + +self.postMessage({ type: 'ready' }); diff --git a/examples/ratchet-train-toy/src/routes/+page.svelte b/examples/ratchet-train-toy/src/routes/+page.svelte index 3336010a..6a17dc20 100644 --- a/examples/ratchet-train-toy/src/routes/+page.svelte +++ b/examples/ratchet-train-toy/src/routes/+page.svelte @@ -1,12 +1,12 @@ + + + sequence toy + + + + + + + + + + + + + + + + +{@render children()} diff --git a/examples/piston-train-toy/src/routes/+layout.ts b/examples/piston-train-toy/src/routes/+layout.ts new file mode 100644 index 00000000..189f71e2 --- /dev/null +++ b/examples/piston-train-toy/src/routes/+layout.ts @@ -0,0 +1 @@ +export const prerender = true; From 54fe741225ea321cdbbb2b913d96cf596bf4dedf Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 17:40:55 -0600 Subject: [PATCH 416/590] Remove blanket SVG ignore rule --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index f0499edf..647364f7 100644 --- a/.gitignore +++ b/.gitignore @@ -14,7 +14,6 @@ Cargo.lock *.pdb .python-version -**/*.svg fixtures/ **/.DS_Store From 65db945823a47bf94cc1b3f0dad9e8748964116f Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 17:56:39 -0600 Subject: [PATCH 417/590] Improve typing for optimizers --- packages/web/src/optim/adamw.ts | 2 +- packages/web/src/optim/muon.ts | 2 +- packages/web/src/optim/optimizer.ts | 38 +++++++++++++++++------------ packages/web/src/optim/sgd.ts | 2 +- 4 files changed, 26 insertions(+), 18 deletions(-) diff --git a/packages/web/src/optim/adamw.ts b/packages/web/src/optim/adamw.ts index cfe95fa0..152ee1aa 100644 --- a/packages/web/src/optim/adamw.ts +++ b/packages/web/src/optim/adamw.ts @@ -35,7 +35,7 @@ export interface AdamWConfig { * The original Adam algorithm was proposed in "Adam: A Method for Stochastic Optimization". * The AdamW variant was proposed in "Decoupled Weight Decay Regularization". */ -export class AdamW extends Optimizer { +export class AdamW extends Optimizer { /** * Creates a new AdamW optimizer * diff --git a/packages/web/src/optim/muon.ts b/packages/web/src/optim/muon.ts index 7a8bede2..385e3134 100644 --- a/packages/web/src/optim/muon.ts +++ b/packages/web/src/optim/muon.ts @@ -121,7 +121,7 @@ function muonUpdate( * Muon is a memory-efficient optimizer that uses Newton-Schulz orthogonalization * for preconditioning gradients. It's particularly effective for transformer models. */ -export class Muon extends Optimizer { +export class Muon extends Optimizer { /** * Creates a new Muon optimizer * diff --git a/packages/web/src/optim/optimizer.ts b/packages/web/src/optim/optimizer.ts index a1ffde52..9b39d120 100644 --- a/packages/web/src/optim/optimizer.ts +++ b/packages/web/src/optim/optimizer.ts @@ -50,8 +50,8 @@ export interface OptimizerParamState { /** * Type for optimizer state dictionary serialization */ -export interface StateDict { - state: Record; +export interface StateDict { + state: Record; paramGroups: ParamGroupConfig[]; } @@ -62,11 +62,15 @@ export interface StateDict { * Subclasses should implement the `step` method which performs the actual * parameter updates. */ -export class Optimizer { - defaults: Record; +export class Optimizer< + TState extends OptimizerParamState = OptimizerParamState, + TConfig = unknown, + TParamGroup extends ParamGroup = ParamGroup, +> { + defaults: TConfig; device: Device; state: Map; - paramGroups: ParamGroup[]; + paramGroups: TParamGroup[]; private _optimizerStepPreHooks: Map; private _optimizerStepPostHooks: Map; @@ -76,11 +80,7 @@ export class Optimizer * @param params - Iterable of parameters or parameter groups * @param defaults - Default optimization options */ - constructor( - params: Parameter[] | ParamGroup[], - device: Device, - defaults: Record, - ) { + constructor(params: Parameter[] | ParamGroup[], device: Device, defaults: TConfig) { this.defaults = defaults; this.device = device; this.state = new Map(); @@ -113,7 +113,11 @@ export class Optimizer // Return a proxy to intercept method calls like 'step' return new Proxy(this, { - get: (target: Optimizer, prop: string | symbol, receiver: unknown): unknown => { + get: ( + target: Optimizer, + prop: string | symbol, + receiver: unknown, + ): unknown => { // Intercept the 'step' method if (prop === "step" && typeof target.step === "function") { const originalStep = Reflect.get(target, prop, receiver) as ( @@ -133,7 +137,7 @@ export class Optimizer // Default behavior for other properties and methods return Reflect.get(target, prop, receiver); }, - }) as Optimizer; + }) as Optimizer; } /** @@ -178,7 +182,11 @@ export class Optimizer } // Create a new parameter group with defaults - const group: ParamGroup = { ...this.defaults, ...paramGroup, params: [] }; + const group = { + ...this.defaults, + ...paramGroup, + params: [], + } as unknown as TParamGroup; // Check parameters for (const param of params) { @@ -208,7 +216,7 @@ export class Optimizer * * @returns State dictionary */ - stateDict(): StateDict { + stateDict(): StateDict { // Map parameters to indices const paramMappings = new Map(); let startIndex = 0; @@ -292,7 +300,7 @@ export class Optimizer const savedGroup = savedGroups[i]; for (const key of Object.keys(savedGroup)) { if (key !== "params") { - group[key] = savedGroup[key]; + (group as Record)[key] = savedGroup[key]; } } }); diff --git a/packages/web/src/optim/sgd.ts b/packages/web/src/optim/sgd.ts index e2fb6bdd..c0ff75f5 100644 --- a/packages/web/src/optim/sgd.ts +++ b/packages/web/src/optim/sgd.ts @@ -32,7 +32,7 @@ export interface SGDConfig { * Nesterov momentum is based on the paper "On the importance of initialization and momentum in deep * learning". */ -export class SGD extends Optimizer { +export class SGD extends Optimizer { /** * Creates a new SGD optimizer * From 59757bbffc7496eaec3d9937b95840cb97158601 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 17:57:51 -0600 Subject: [PATCH 418/590] Slim down optim type assertions --- packages/web/src/optim/adamw.ts | 16 ++++++++-------- packages/web/src/optim/muon.ts | 26 +++++++++++++------------- packages/web/src/optim/sgd.ts | 14 +++++++------- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/packages/web/src/optim/adamw.ts b/packages/web/src/optim/adamw.ts index 152ee1aa..3b6a55ab 100644 --- a/packages/web/src/optim/adamw.ts +++ b/packages/web/src/optim/adamw.ts @@ -104,21 +104,21 @@ export class AdamW extends Optimizer // Perform optimization for each parameter group for (const group of this.paramGroups) { const { - lr = this.defaults.lr as number, - weightDecay = this.defaults.weightDecay as number, - momentum = this.defaults.momentum as number, - nsSteps = this.defaults.nsSteps as number, - nesterov = this.defaults.nesterov as boolean, - } = group as MuonParamGroup; + lr = this.defaults.lr, + weightDecay = this.defaults.weightDecay, + momentum = this.defaults.momentum, + nsSteps = this.defaults.nsSteps, + nesterov = this.defaults.nesterov, + } = group; for (const param of group.params) { if (!param.grad) { @@ -194,7 +194,7 @@ export class Muon extends Optimizer param.grad = pin(zerosLike(param)); } - const grad = param.grad as Tensor; + const grad = param.grad; // Get parameter state let state = this.state.get(param); @@ -205,7 +205,7 @@ export class Muon extends Optimizer this.state.set(param, state); } - const update = muonUpdate(grad, state.momentumBuffer!, momentum, nsSteps, nesterov); + const update = muonUpdate(grad, state.momentumBuffer as Tensor, momentum, nsSteps, nesterov); // Apply weight decay if (weightDecay !== 0) { diff --git a/packages/web/src/optim/sgd.ts b/packages/web/src/optim/sgd.ts index c0ff75f5..c835e5d5 100644 --- a/packages/web/src/optim/sgd.ts +++ b/packages/web/src/optim/sgd.ts @@ -95,19 +95,19 @@ export class SGD extends Optimizer { // Perform optimization for each parameter group for (const group of this.paramGroups) { const { - lr = this.defaults.lr as number, - momentum = this.defaults.momentum as number, - dampening = this.defaults.dampening as number, - weightDecay = this.defaults.weightDecay as number, - nesterov = this.defaults.nesterov as boolean, - } = group as SGDParamGroup; + lr = this.defaults.lr, + momentum = this.defaults.momentum, + dampening = this.defaults.dampening, + weightDecay = this.defaults.weightDecay, + nesterov = this.defaults.nesterov, + } = group; for (const param of group.params) { if (!param.grad) { continue; } - let grad = param.grad as Tensor; + let grad = param.grad; // Apply weight decay if (weightDecay !== 0) { From 787a57ef0147a6e1a3e25d28321def546e7d97f7 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 17:58:20 -0600 Subject: [PATCH 419/590] Make adamw closer to PyTorch impl --- packages/web/src/optim/adamw.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/web/src/optim/adamw.ts b/packages/web/src/optim/adamw.ts index 3b6a55ab..a40a0a7e 100644 --- a/packages/web/src/optim/adamw.ts +++ b/packages/web/src/optim/adamw.ts @@ -143,26 +143,26 @@ export class AdamW extends Optimizer Date: Fri, 24 Oct 2025 17:59:14 -0600 Subject: [PATCH 420/590] Finalize serialization for lr schedulers --- packages/web/src/optim/lrScheduler.ts | 34 +++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/packages/web/src/optim/lrScheduler.ts b/packages/web/src/optim/lrScheduler.ts index a69b4a1a..959d01df 100644 --- a/packages/web/src/optim/lrScheduler.ts +++ b/packages/web/src/optim/lrScheduler.ts @@ -5,6 +5,7 @@ import { Optimizer } from "./optimizer"; */ export type SchedulerStateDict = { lastEpoch: number; + lastLr?: number[]; } & T; /** @@ -81,7 +82,12 @@ export abstract class LRScheduler { // Initialize base learning rates if (lastEpoch === -1) { - this.baseLrs = this.optimizer.paramGroups.map((group) => group.lr!); + this.baseLrs = this.optimizer.paramGroups.map((group) => { + const initial = group.lr!; + // Persist initialLr on the param group for resume consistency + group.initialLr = initial; + return initial; + }); } else { this.baseLrs = this.optimizer.paramGroups.map((group) => { if (!group.initialLr) { @@ -112,11 +118,20 @@ export abstract class LRScheduler { * Return the state of the scheduler as a dictionary */ stateDict(): SchedulerStateDict { - const state: Record = { lastEpoch: this.lastEpoch }; + const state: Record = { + lastEpoch: this.lastEpoch, + lastLr: this.lastLr.slice(), + }; // Add any additional state from subclasses for (const [key, value] of Object.entries(this)) { - if (key !== "optimizer" && key !== "baseLrs" && key !== "lastLr") { + if ( + key !== "optimizer" && + key !== "baseLrs" && + key !== "lastLr" && + key !== "_initialized" && + key !== "stepCount" + ) { state[key] = value; } } @@ -130,7 +145,18 @@ export abstract class LRScheduler { * @param stateDict - Scheduler state dictionary */ loadStateDict(stateDict: SchedulerStateDict): void { - Object.assign(this, stateDict); + const { lastLr, ...rest } = stateDict; + Object.assign(this, rest); + + // Restore lastLr for correct chainable updates; if missing, sync from optimizer groups + if (Array.isArray(lastLr)) { + this.lastLr = lastLr.slice(); + } else { + this.lastLr = this.optimizer.paramGroups.map((g) => g.lr!); + } + + // Mark initialized to avoid performing an extra initial step after loading state + this._initialized = true; } /** From a518fea86b51723a95110e5f693d4596c65b1ba2 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 17:59:26 -0600 Subject: [PATCH 421/590] Remove old print statement --- packages/web/src/optim/lrScheduler.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/web/src/optim/lrScheduler.ts b/packages/web/src/optim/lrScheduler.ts index 959d01df..bf3a0650 100644 --- a/packages/web/src/optim/lrScheduler.ts +++ b/packages/web/src/optim/lrScheduler.ts @@ -281,12 +281,6 @@ export class LinearLR extends LRScheduler { getLr(): number[] { if (this.lastEpoch === 0) { - console.log( - "startFactor", - this.startFactor, - this.getLastLr(), - this.getLastLr().map((lr) => lr * this.startFactor), - ); return this.getLastLr().map((lr) => lr * this.startFactor); } From 5f409db016ae5b81e46733bd37f2595837cdb4a2 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 17:59:44 -0600 Subject: [PATCH 422/590] Add SequentialLR impl --- packages/web/src/optim/lrScheduler.ts | 129 ++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/packages/web/src/optim/lrScheduler.ts b/packages/web/src/optim/lrScheduler.ts index bf3a0650..e692cea0 100644 --- a/packages/web/src/optim/lrScheduler.ts +++ b/packages/web/src/optim/lrScheduler.ts @@ -426,3 +426,132 @@ export class CosineAnnealingLR extends LRScheduler { ); } } + +/** + * Sequentially applies a list of schedulers at specified milestone steps + * + * Example: warmup for N steps then cosine → milestones: [N] + */ +export interface SequentialConfig { + milestones: number[]; + currentIndex: number; + inner: SchedulerStateDict[]; +} + +export class SequentialLR extends LRScheduler { + private schedulers: LRScheduler[]; + private milestones: number[]; + private currentIndex: number = 0; + + constructor( + optimizer: Optimizer, + schedulers: LRScheduler[], + milestones: number[], + lastEpoch: number = -1, + ) { + super(optimizer, lastEpoch); + + if (!Array.isArray(schedulers) || schedulers.length === 0) { + throw new Error("SequentialLR requires at least one scheduler"); + } + if (!Array.isArray(milestones)) { + throw new Error("SequentialLR milestones must be an array"); + } + if (schedulers.length !== milestones.length + 1) { + throw new Error("SequentialLR: milestones length must be exactly schedulers.length - 1"); + } + for (let i = 1; i < milestones.length; i++) { + if (!(milestones[i] > milestones[i - 1])) { + throw new Error("SequentialLR milestones must be strictly increasing"); + } + } + + this.schedulers = schedulers; + this.milestones = milestones.slice(); + this.currentIndex = this.indexForEpoch(this.lastEpoch); + } + + private indexForEpoch(epoch: number): number { + let idx = 0; + for (let i = 0; i < this.milestones.length; i++) { + if (epoch >= this.milestones[i]) idx += 1; + else break; + } + return idx; + } + + private syncIncomingScheduler(index: number): void { + const incoming = this.schedulers[index]; + // Ensure continuity by seeding the incoming scheduler with current LR + const current = this.lastLr?.length + ? this.lastLr.slice() + : this.optimizer.paramGroups.map((g) => g.lr!); + incoming.loadStateDict({ lastEpoch: -1, lastLr: current }); + } + + step(): void { + // Avoid base recursion init path issues + // Note: base.ensureInitialized() calls step() once; safe with this override + if ((this as unknown as { _initialized?: boolean })._initialized !== true) { + (this as unknown as { _initialized: boolean })._initialized = true; + this.stepCount = 0; + } + + this.stepCount += 1; + this.lastEpoch += 1; + + const prevIndex = this.currentIndex; + const nextIndex = this.indexForEpoch(this.lastEpoch); + if (nextIndex !== prevIndex) { + this.syncIncomingScheduler(nextIndex); + this.currentIndex = nextIndex; + } + + // Step the active scheduler exactly once + const active = this.schedulers[this.currentIndex]; + active.step(); + + const values = active.getLastLr().slice(); + for (let i = 0; i < this.optimizer.paramGroups.length; i++) { + this.optimizer.paramGroups[i].lr = values[i]; + } + this.lastLr = values; + } + + getLr(): number[] { + // No additional computation; delegate to tracked lastLr + return this.lastLr.slice(); + } + + stateDict(): SchedulerStateDict { + return { + lastEpoch: this.lastEpoch, + lastLr: this.lastLr.slice(), + milestones: this.milestones.slice(), + currentIndex: this.currentIndex, + inner: this.schedulers.map((s) => s.stateDict()), + }; + } + + loadStateDict(stateDict: SchedulerStateDict): void { + const { lastEpoch, lastLr, milestones, currentIndex, inner } = stateDict; + + // Initialize base epoch/lr and mark initialized; provide full shape to satisfy generic + super.loadStateDict({ + lastEpoch, + lastLr, + milestones: this.milestones, + currentIndex: this.currentIndex, + inner: [], + }); + + this.milestones = Array.isArray(milestones) ? milestones.slice() : []; + this.currentIndex = Math.min(Math.max(0, currentIndex | 0), this.schedulers.length - 1); + + if (Array.isArray(inner)) { + for (let i = 0; i < Math.min(inner.length, this.schedulers.length); i++) { + this.schedulers[i].loadStateDict(inner[i]); + } + } + } +} From 4287633f9678f855ba78559c6ec4510d81d0d3fe Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 17:59:55 -0600 Subject: [PATCH 423/590] Nit; more like PyTorch --- packages/web/src/optim/adamw.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web/src/optim/adamw.ts b/packages/web/src/optim/adamw.ts index a40a0a7e..9abdb30a 100644 --- a/packages/web/src/optim/adamw.ts +++ b/packages/web/src/optim/adamw.ts @@ -120,7 +120,7 @@ export class AdamW extends Optimizer Date: Fri, 24 Oct 2025 18:00:08 -0600 Subject: [PATCH 424/590] Clean up SGD imports --- packages/web/src/optim/sgd.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web/src/optim/sgd.ts b/packages/web/src/optim/sgd.ts index c835e5d5..3ca6fa7d 100644 --- a/packages/web/src/optim/sgd.ts +++ b/packages/web/src/optim/sgd.ts @@ -4,7 +4,7 @@ import { Optimizer } from "@/optim/optimizer"; import { OptimizerParamState, ParamGroup } from "@/optim/optimizer"; import { Tensor } from "@/tensor"; import { pin } from "@/utils/weak"; -import { _popFunctionMode, _pushFunctionMode, Device } from "@/wasm"; +import { Device } from "@/wasm"; interface SGDParamState extends OptimizerParamState { momentumBuffer?: Tensor | null; From 3b43612490aa9faf97b869de8645a206288de0cb Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 18:00:27 -0600 Subject: [PATCH 425/590] Add to SGD grad in place --- packages/web/src/optim/sgd.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web/src/optim/sgd.ts b/packages/web/src/optim/sgd.ts index 3ca6fa7d..a966afc1 100644 --- a/packages/web/src/optim/sgd.ts +++ b/packages/web/src/optim/sgd.ts @@ -111,7 +111,7 @@ export class SGD extends Optimizer { // Apply weight decay if (weightDecay !== 0) { - grad = grad.add(param.mul(weightDecay)); + grad.add_(param.mul(weightDecay)); } // Apply momentum From 18476afd4d05ed032976621df4294ae64211f7da Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 18:00:47 -0600 Subject: [PATCH 426/590] Add Adam implementation --- packages/web/src/optim/adam.ts | 181 ++++++++++++++++++++++++++++++++ packages/web/src/optim/index.ts | 1 + 2 files changed, 182 insertions(+) create mode 100644 packages/web/src/optim/adam.ts diff --git a/packages/web/src/optim/adam.ts b/packages/web/src/optim/adam.ts new file mode 100644 index 00000000..e93695ab --- /dev/null +++ b/packages/web/src/optim/adam.ts @@ -0,0 +1,181 @@ +import { zerosLike } from "@/globals"; +import { Parameter } from "@/nn/parameter"; +import { Optimizer } from "@/optim/optimizer"; +import { OptimizerParamState, ParamGroup } from "@/optim/optimizer"; +import { Tensor } from "@/tensor"; +import { pin } from "@/utils/weak"; +import { Device } from "@/wasm"; + +interface AdamParamState extends OptimizerParamState { + step: number; + expAvg: Tensor | null; + expAvgSq: Tensor | null; + maxExpAvgSq?: Tensor | null; +} + +export interface AdamParamGroup extends ParamGroup { + lr?: number; + betas?: [number, number]; + eps?: number; + weightDecay?: number; + amsgrad?: boolean; +} + +export interface AdamConfig { + lr: number; + betas: [number, number]; + eps: number; + weightDecay: number; + amsgrad: boolean; +} + +/** + * Implementation of the Adam optimizer (based on original PyTorch implementation) + * + * The original Adam algorithm was proposed in "Adam: A Method for Stochastic Optimization". + */ +export class Adam extends Optimizer { + /** + * Creates a new Adam optimizer + * + * @param params - Parameters or parameter groups to optimize + * @param device - The device to perform computations on + * @param config - Configuration options for the optimizer: + * - `lr` (number): Learning rate (default: 1e-3) + * - `betas` ([number, number]): Coefficients for computing running averages of gradient and its + * square (default: [0.9, 0.999]) + * - `eps` (number): Term added to denominator for numerical stability (default: 1e-8) + * - `weightDecay` (number): Weight decay (L2 penalty) (default: 0) + * - `amsgrad` (boolean): Whether to use the AMSGrad variant (default: false) + */ + constructor(params: Parameter[] | ParamGroup[], device: Device, config?: Partial) { + const { + lr = 1e-3, + betas = [0.9, 0.999], + eps = 1e-8, + weightDecay = 0, + amsgrad = false, + } = config ?? {}; + + // Validate parameters + if (!(lr >= 0)) { + throw new Error(`Invalid learning rate: ${lr}`); + } + if (!(eps >= 0)) { + throw new Error(`Invalid epsilon value: ${eps}`); + } + if (!(betas[0] >= 0 && betas[0] < 1)) { + throw new Error(`Invalid beta parameter at index 0: ${betas[0]}`); + } + if (!(betas[1] >= 0 && betas[1] < 1)) { + throw new Error(`Invalid beta parameter at index 1: ${betas[1]}`); + } + if (!(weightDecay >= 0)) { + throw new Error(`Invalid weight_decay value: ${weightDecay}`); + } + + // Initialize with default options + const defaults = { + lr, + betas, + eps, + weightDecay, + amsgrad, + }; + + super(params, device, defaults); + } + + /** + * Performs a single optimization step + * + * @param closure - Closure that reevaluates the model and returns the loss + * @returns The loss value if closure is provided + */ + async step(closure?: () => number): Promise { + let loss: number | undefined; + + // Call closure if provided + if (closure) { + loss = closure(); + } + + // Perform optimization for each parameter group + for (const group of this.paramGroups) { + const { + lr = this.defaults.lr, + betas = this.defaults.betas, + eps = this.defaults.eps, + weightDecay = this.defaults.weightDecay, + amsgrad = this.defaults.amsgrad, + } = group; + + const [beta1, beta2] = betas; + + for (const param of group.params) { + if (!param.grad) { + continue; + } + + const grad = param.grad; + + // Get parameter state + let state = this.state.get(param); + if (!state) { + state = { + step: 0, + expAvg: pin(zerosLike(param)), + expAvgSq: pin(zerosLike(param)), + }; + + if (amsgrad) { + state.maxExpAvgSq = pin(zerosLike(param)); + } + + this.state.set(param, state); + } + + const expAvg = state.expAvg as Tensor; + const expAvgSq = state.expAvgSq as Tensor; + + // Increment step counter + state.step++; + + // Compute bias correction terms + const biasCorrection1 = 1 - Math.pow(beta1, state.step); + const biasCorrection2 = 1 - Math.pow(beta2, state.step); + + // Apply weight decay + if (weightDecay !== 0) { + grad.add_(param.mul(weightDecay)); + } + + // Decay the first and second moment running average coefficient + expAvg.mul_(beta1).add_(grad.mul(1 - beta1)); + expAvgSq.mul_(beta2).addcmul_(grad, grad, 1 - beta2); + + // Use the max avg squared if using amsgrad + let denom; + if (amsgrad) { + const maxExpAvgSq = state.maxExpAvgSq as Tensor; + // Update max exponential moving average of squared gradient + maxExpAvgSq.maximum_(expAvgSq); + // Use the max for normalization + denom = maxExpAvgSq.sqrt().div(Math.sqrt(biasCorrection2)).add(eps); + } else { + denom = expAvgSq.sqrt().div(Math.sqrt(biasCorrection2)).add(eps); + } + + // Compute step size + const stepSize = lr / biasCorrection1; + + // Update parameters + param.addcdiv_(expAvg, denom, -stepSize); + } + } + + await this.device.markStep(); + + return loss; + } +} diff --git a/packages/web/src/optim/index.ts b/packages/web/src/optim/index.ts index 02a23df5..b6ff2769 100644 --- a/packages/web/src/optim/index.ts +++ b/packages/web/src/optim/index.ts @@ -1,3 +1,4 @@ +export * from "./adam"; export * from "./adamw"; export * from "./lrScheduler"; export * from "./muon"; From ed4f0e3d54637f848e012f2fb20d709c7e3219c3 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 18:07:12 -0600 Subject: [PATCH 427/590] Add generic types to nn modules --- packages/web/src/nn/activation.ts | 2 +- packages/web/src/nn/alibi.ts | 2 +- packages/web/src/nn/embedding.ts | 2 +- packages/web/src/nn/linear.ts | 2 +- packages/web/src/nn/rope.ts | 2 +- packages/web/src/nn/sinusoidal.ts | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/web/src/nn/activation.ts b/packages/web/src/nn/activation.ts index 931cc03e..a4ff66a1 100644 --- a/packages/web/src/nn/activation.ts +++ b/packages/web/src/nn/activation.ts @@ -1,7 +1,7 @@ import { Module } from "@/nn/module"; import { Tensor } from "@/tensor"; -export class LogSoftmax extends Module { +export class LogSoftmax extends Module<[Tensor], Tensor> { constructor(private dim: number = 1) { super(); } diff --git a/packages/web/src/nn/alibi.ts b/packages/web/src/nn/alibi.ts index 8a73fe45..878625f0 100644 --- a/packages/web/src/nn/alibi.ts +++ b/packages/web/src/nn/alibi.ts @@ -11,7 +11,7 @@ import { Tensor } from "@/tensor"; * For more details see "Train Short, Test Long: Attention with Linear Biases Enables Input Length * Extrapolation" (https://arxiv.org/abs/2108.12409). */ -export class AlibiEmbedding extends Module { +export class AlibiEmbedding extends Module<[Tensor], Tensor> { private maxBias: number; /** diff --git a/packages/web/src/nn/embedding.ts b/packages/web/src/nn/embedding.ts index d520752b..f63a9156 100644 --- a/packages/web/src/nn/embedding.ts +++ b/packages/web/src/nn/embedding.ts @@ -3,7 +3,7 @@ import { Module } from "@/nn/module"; import { Parameter } from "@/nn/parameter"; import { Tensor } from "@/tensor"; -export class Embedding extends Module { +export class Embedding extends Module<[Tensor], Tensor> { weight: Parameter; hiddenSize: number; /** diff --git a/packages/web/src/nn/linear.ts b/packages/web/src/nn/linear.ts index dc10cb6f..bc3919c7 100644 --- a/packages/web/src/nn/linear.ts +++ b/packages/web/src/nn/linear.ts @@ -3,7 +3,7 @@ import { Module } from "@/nn/module"; import { Parameter } from "@/nn/parameter"; import { Tensor } from "@/tensor"; -export class Linear extends Module { +export class Linear extends Module<[Tensor], Tensor> { weight: Parameter; bias?: Parameter; /** diff --git a/packages/web/src/nn/rope.ts b/packages/web/src/nn/rope.ts index 41dc37f0..b5814c14 100644 --- a/packages/web/src/nn/rope.ts +++ b/packages/web/src/nn/rope.ts @@ -11,7 +11,7 @@ import { Tensor } from "@/tensor"; * For more details see `RoFormer: Enhanced Transformer with Rotary Position Embedding * `. */ -export class RotaryEmbedding extends Module { +export class RotaryEmbedding extends Module<[Tensor], Tensor> { private dim: number; private base: number; diff --git a/packages/web/src/nn/sinusoidal.ts b/packages/web/src/nn/sinusoidal.ts index 7cdf9507..3997e688 100644 --- a/packages/web/src/nn/sinusoidal.ts +++ b/packages/web/src/nn/sinusoidal.ts @@ -7,7 +7,7 @@ import { Device } from "@/wasm"; /** * Implements sinusoidal positional encodings as described in "Attention Is All You Need". */ -export class SinusoidalEmbedding extends Module { +export class SinusoidalEmbedding extends Module<[Tensor], Tensor> { private dim: number; private invFreq: Buffer; From 893347b464a2ae348cbc8a64ee24fe82791e5fa6 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 18:10:19 -0600 Subject: [PATCH 428/590] Formatting stuff in alibi --- packages/web/src/nn/alibi.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/web/src/nn/alibi.ts b/packages/web/src/nn/alibi.ts index 878625f0..8df55e66 100644 --- a/packages/web/src/nn/alibi.ts +++ b/packages/web/src/nn/alibi.ts @@ -4,10 +4,10 @@ import { Tensor } from "@/tensor"; /** * Implements ALiBi (Attention with Linear Biases) positional encoding. - * + * * ALiBi applies position-dependent biases to attention weights, allowing models to extrapolate to * longer sequences than they were trained on. - * + * * For more details see "Train Short, Test Long: Attention with Linear Biases Enables Input Length * Extrapolation" (https://arxiv.org/abs/2108.12409). */ @@ -33,9 +33,9 @@ export class AlibiEmbedding extends Module<[Tensor], Tensor> { // Create zeros tensor with the input shape (up to 3 dimensions for broadcasting) const zerosShape = input.shape.slice(0, 3); const alibiBase = zeros(zerosShape, { device: input.device }); - + // Apply ALiBi bias and add to input const alibiBias = alibiBase.alibi({ maxBias: this.maxBias }).unsqueeze(3); return input.add(alibiBias); } -} \ No newline at end of file +} From c99a0343d87916f9a8480b8a252be8132daac29e Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 18:16:07 -0600 Subject: [PATCH 429/590] Use config objects for layernorms --- packages/web/src/nn/normalization.ts | 30 ++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/packages/web/src/nn/normalization.ts b/packages/web/src/nn/normalization.ts index 74507525..c176ed94 100644 --- a/packages/web/src/nn/normalization.ts +++ b/packages/web/src/nn/normalization.ts @@ -3,17 +3,24 @@ import { Module } from "@/nn/module"; import { Parameter } from "@/nn/parameter"; import { Tensor } from "@/tensor"; +export interface LayerNormConfig { + eps?: number; + bias?: boolean; +} + export class LayerNorm extends Module<[Tensor], Tensor> { weight: Parameter; - bias: Parameter; + bias?: Parameter; + eps: number; - constructor( - normalizedShape: number, - private eps = 1e-5, - ) { + constructor(normalizedShape: number, config: LayerNormConfig = {}) { super(); + this.eps = config.eps ?? 1e-5; this.weight = ones([normalizedShape], { device: gpu, requiresGrad: true }); - this.bias = zeros([normalizedShape], { device: gpu, requiresGrad: true }); + this.bias = + (config.bias ?? true) + ? zeros([normalizedShape], { device: gpu, requiresGrad: true }) + : undefined; } forward(input: Tensor) { @@ -21,14 +28,17 @@ export class LayerNorm extends Module<[Tensor], Tensor> { } } +export interface RMSNormConfig { + eps?: number; +} + export class RMSNorm extends Module<[Tensor], Tensor> { weight: Parameter; + eps: number; - constructor( - normalizedShape: number, - private eps = 1e-5, - ) { + constructor(normalizedShape: number, config: RMSNormConfig = {}) { super(); + this.eps = config.eps ?? 1e-5; this.weight = ones([normalizedShape], { device: gpu, requiresGrad: true }); } From 29dc7b539afe1f980b7a6f4f0e174d5707a3bb70 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 18:17:51 -0600 Subject: [PATCH 430/590] Make nn module init. closer to PyTorch --- packages/web/src/nn/embedding.ts | 10 ++++++++-- packages/web/src/nn/linear.ts | 17 +++++++++++++++-- packages/web/src/nn/normalization.ts | 27 ++++++++++++++++++++++++++- 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/packages/web/src/nn/embedding.ts b/packages/web/src/nn/embedding.ts index f63a9156..bfe41870 100644 --- a/packages/web/src/nn/embedding.ts +++ b/packages/web/src/nn/embedding.ts @@ -1,4 +1,4 @@ -import { gpu, randn } from "@/core"; +import { gpu, initNormal_, zeros } from "@/core"; import { Module } from "@/nn/module"; import { Parameter } from "@/nn/parameter"; import { Tensor } from "@/tensor"; @@ -13,11 +13,17 @@ export class Embedding extends Module<[Tensor], Tensor> { */ constructor(numEmbeddings: number, embeddingDim: number) { super(); - this.weight = randn([numEmbeddings, embeddingDim], { + this.weight = zeros([numEmbeddings, embeddingDim], { device: gpu, requiresGrad: true, }); this.hiddenSize = embeddingDim; + + this.resetParameters(); + } + + resetParameters() { + initNormal_(this.weight); } forward(input: Tensor) { diff --git a/packages/web/src/nn/linear.ts b/packages/web/src/nn/linear.ts index bc3919c7..bef26819 100644 --- a/packages/web/src/nn/linear.ts +++ b/packages/web/src/nn/linear.ts @@ -1,8 +1,10 @@ -import { gpu, randn, zeros } from "@/globals"; +import { gpu, initKaimingUniform_, initUniform_, zeros } from "@/globals"; import { Module } from "@/nn/module"; import { Parameter } from "@/nn/parameter"; import { Tensor } from "@/tensor"; +import { calculateFanInAndFanOut } from "./utils"; + export class Linear extends Module<[Tensor], Tensor> { weight: Parameter; bias?: Parameter; @@ -14,11 +16,22 @@ export class Linear extends Module<[Tensor], Tensor> { */ constructor(inFeatures: number, outFeatures: number, bias = true) { super(); - this.weight = randn([outFeatures, inFeatures], { device: gpu, requiresGrad: true }); + this.weight = zeros([outFeatures, inFeatures], { device: gpu, requiresGrad: true }); if (bias) { this.bias = zeros([outFeatures], { device: gpu, requiresGrad: true }); } + + this.resetParameters(); + } + + resetParameters() { + initKaimingUniform_(this.weight, { a: Math.sqrt(5) }); + if (this.bias) { + const [fanIn] = calculateFanInAndFanOut(this.weight); + const bound = fanIn > 0 ? Math.sqrt(1 / fanIn) : 0; + initUniform_(this.bias, { low: -bound, high: bound }); + } } forward(input: Tensor) { diff --git a/packages/web/src/nn/normalization.ts b/packages/web/src/nn/normalization.ts index c176ed94..b3ce54a1 100644 --- a/packages/web/src/nn/normalization.ts +++ b/packages/web/src/nn/normalization.ts @@ -1,10 +1,11 @@ -import { gpu, ones, zeros } from "@/globals"; +import { gpu, initOnes_, initZeros_, ones, zeros } from "@/globals"; import { Module } from "@/nn/module"; import { Parameter } from "@/nn/parameter"; import { Tensor } from "@/tensor"; export interface LayerNormConfig { eps?: number; + elementwiseAffine?: boolean; bias?: boolean; } @@ -12,15 +13,28 @@ export class LayerNorm extends Module<[Tensor], Tensor> { weight: Parameter; bias?: Parameter; eps: number; + elementwiseAffine: boolean; constructor(normalizedShape: number, config: LayerNormConfig = {}) { super(); this.eps = config.eps ?? 1e-5; + this.elementwiseAffine = config.elementwiseAffine ?? true; this.weight = ones([normalizedShape], { device: gpu, requiresGrad: true }); this.bias = (config.bias ?? true) ? zeros([normalizedShape], { device: gpu, requiresGrad: true }) : undefined; + + this.resetParameters(); + } + + resetParameters() { + if (this.elementwiseAffine) { + initOnes_(this.weight); + if (this.bias) { + initZeros_(this.bias); + } + } } forward(input: Tensor) { @@ -30,16 +44,27 @@ export class LayerNorm extends Module<[Tensor], Tensor> { export interface RMSNormConfig { eps?: number; + elementwiseAffine?: boolean; } export class RMSNorm extends Module<[Tensor], Tensor> { weight: Parameter; eps: number; + elementwiseAffine: boolean; constructor(normalizedShape: number, config: RMSNormConfig = {}) { super(); this.eps = config.eps ?? 1e-5; + this.elementwiseAffine = config.elementwiseAffine ?? true; this.weight = ones([normalizedShape], { device: gpu, requiresGrad: true }); + + this.resetParameters(); + } + + resetParameters() { + if (this.elementwiseAffine) { + initOnes_(this.weight); + } } forward(input: Tensor) { From 84182185f27ba5784349306e7d5bbd9653c660bc Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 22:32:16 -0600 Subject: [PATCH 431/590] Add from_tensor to TensorOptions --- crates/piston-core/src/tensor.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/crates/piston-core/src/tensor.rs b/crates/piston-core/src/tensor.rs index 3d20fd19..dbd86865 100644 --- a/crates/piston-core/src/tensor.rs +++ b/crates/piston-core/src/tensor.rs @@ -1539,6 +1539,14 @@ impl TensorOptions { } } + pub fn from_tensor(tensor: &Tensor) -> Self { + Self { + device: Some(tensor.device()), + dtype: Some(tensor.dtype()), + requires_grad: Some(tensor.requires_grad()), + } + } + /// Set the device for tensor creation pub fn device(mut self, device: Device) -> Self { self.device = Some(device); From 55c7341958b0e65bcf30d63b579117309ade3769 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 25 Oct 2025 12:57:40 -0600 Subject: [PATCH 432/590] Expose a bunch of ops at global level --- packages/web/src/globals.ts | 81 +++++++++++++++++++++++++++++++++++++ packages/web/src/wasm.ts | 34 ++++++++++++++++ 2 files changed, 115 insertions(+) diff --git a/packages/web/src/globals.ts b/packages/web/src/globals.ts index 4cb72f4b..b09fe368 100644 --- a/packages/web/src/globals.ts +++ b/packages/web/src/globals.ts @@ -7,25 +7,52 @@ import { TensorOptions, } from "@/types"; import { + abs as abs_wasm, + add as add_wasm, + addcdiv as addcdiv_wasm, + addcmul as addcmul_wasm, arange as arange_wasm, bernoulli as bernoulli_wasm, cat as cat_wasm, + ceil as ceil_wasm, + cos as cos_wasm, cpu_wasm, Device, DType, + eq as eq_wasm, + exp as exp_wasm, float16_wasm, float32_wasm, + floor as floor_wasm, fromData, full as full_wasm, + ge as ge_wasm, + gelu as gelu_wasm, gpu_wasm, + gt as gt_wasm, int32_wasm, + le as le_wasm, + log as log_wasm, + lt as lt_wasm, + ne as ne_wasm, + neg as neg_wasm, ones as ones_wasm, onesLike as onesLike_wasm, rand as rand_wasm, randint as randint_wasm, randn as randn_wasm, + recip as recip_wasm, + relu2 as relu2_wasm, + relu as relu_wasm, seed_wasm, + sigmoid as sigmoid_wasm, + silu as silu_wasm, + sin as sin_wasm, + sqrt as sqrt_wasm, + square as square_wasm, stack as stack_wasm, + swiglu as swiglu_wasm, + tanh as tanh_wasm, Tensor_wasm, uint32_wasm, zeros as zeros_wasm, @@ -61,6 +88,33 @@ export let zeros: typeof zeros_wasm; export let zerosLike: typeof zerosLike_wasm; export let ones: typeof ones_wasm; export let onesLike: typeof onesLike_wasm; +export let add: typeof add_wasm; +export let addcdiv: typeof addcdiv_wasm; +export let addcmul: typeof addcmul_wasm; +export let eq: typeof eq_wasm; +export let ne: typeof ne_wasm; +export let gt: typeof gt_wasm; +export let ge: typeof ge_wasm; +export let lt: typeof lt_wasm; +export let le: typeof le_wasm; +export let gelu: typeof gelu_wasm; +export let tanh: typeof tanh_wasm; +export let exp: typeof exp_wasm; +export let log: typeof log_wasm; +export let sin: typeof sin_wasm; +export let cos: typeof cos_wasm; +export let abs: typeof abs_wasm; +export let sqrt: typeof sqrt_wasm; +export let relu: typeof relu_wasm; +export let relu2: typeof relu2_wasm; +export let floor: typeof floor_wasm; +export let ceil: typeof ceil_wasm; +export let neg: typeof neg_wasm; +export let sigmoid: typeof sigmoid_wasm; +export let swiglu: typeof swiglu_wasm; +export let silu: typeof silu_wasm; +export let square: typeof square_wasm; +export let recip: typeof recip_wasm; export let tensor: { ( @@ -203,6 +257,33 @@ export async function initGlobals() { randint = wrapWithParam(wrapWithLibTensor(randint_wasm)); randn = wrapWithParam(wrapWithLibTensor(randn_wasm)); rand = wrapWithParam(wrapWithLibTensor(rand_wasm)); + add = wrapWithLibTensor(add_wasm); + addcdiv = wrapWithLibTensor(addcdiv_wasm); + addcmul = wrapWithLibTensor(addcmul_wasm); + eq = wrapWithLibTensor(eq_wasm); + ne = wrapWithLibTensor(ne_wasm); + gt = wrapWithLibTensor(gt_wasm); + ge = wrapWithLibTensor(ge_wasm); + lt = wrapWithLibTensor(lt_wasm); + le = wrapWithLibTensor(le_wasm); + gelu = wrapWithLibTensor(gelu_wasm); + tanh = wrapWithLibTensor(tanh_wasm); + exp = wrapWithLibTensor(exp_wasm); + log = wrapWithLibTensor(log_wasm); + sin = wrapWithLibTensor(sin_wasm); + cos = wrapWithLibTensor(cos_wasm); + abs = wrapWithLibTensor(abs_wasm); + sqrt = wrapWithLibTensor(sqrt_wasm); + relu = wrapWithLibTensor(relu_wasm); + relu2 = wrapWithLibTensor(relu2_wasm); + floor = wrapWithLibTensor(floor_wasm); + ceil = wrapWithLibTensor(ceil_wasm); + neg = wrapWithLibTensor(neg_wasm); + sigmoid = wrapWithLibTensor(sigmoid_wasm); + swiglu = wrapWithLibTensor(swiglu_wasm); + silu = wrapWithLibTensor(silu_wasm); + square = wrapWithLibTensor(square_wasm); + recip = wrapWithLibTensor(recip_wasm); bernoulli = wrapWithLibTensor(bernoulli_wasm); zeros = wrapWithParam(wrapWithLibTensor(zeros_wasm)); zerosLike = wrapWithParam(wrapWithLibTensor(zerosLike_wasm)); diff --git a/packages/web/src/wasm.ts b/packages/web/src/wasm.ts index 258683e1..79291837 100644 --- a/packages/web/src/wasm.ts +++ b/packages/web/src/wasm.ts @@ -5,26 +5,60 @@ export { _setFunctionModeConstructor, _setPistonWebModule, _setTensorConstructor, + abs, + add, + addcdiv, + addcmul, arange, bernoulli, cat, + ceil, + cos, cpu as cpu_wasm, Device, + div, DType, + eq, + exp, float16 as float16_wasm, float32 as float32_wasm, + floor, fromData, full, + ge, + gelu, gpu as gpu_wasm, + gt, int32 as int32_wasm, + le, + log, + lt, + maximum, + minimum, + mul, + ne, + neg, + oneHot, ones, onesLike, + pow, rand, randint, randn, + recip, + relu, + relu2, save as save_wasm, seed as seed_wasm, + sigmoid, + silu, + sin, + sqrt, + square, stack, + sub, + swiglu, + tanh, Tensor as Tensor_wasm, Trainer as Trainer_wasm, uint32 as uint32_wasm, From ad3577c60d56cb01fdf742fb1ebd78ebb18cf5aa Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 22:51:35 -0600 Subject: [PATCH 433/590] Add eye op --- crates/piston-core/src/backprop.rs | 2 + crates/piston-core/src/cpu/mod.rs | 1 + crates/piston-core/src/op.rs | 8 +- crates/piston-core/src/ops/eye.rs | 186 +++++++++++++++++++++++++++++ crates/piston-core/src/ops/mod.rs | 2 + crates/piston-core/src/tensor.rs | 62 ++++++++++ crates/piston-web/src/tensor.rs | 5 + packages/web/src/globals.ts | 3 + packages/web/src/wasm.ts | 1 + 9 files changed, 269 insertions(+), 1 deletion(-) create mode 100644 crates/piston-core/src/ops/eye.rs diff --git a/crates/piston-core/src/backprop.rs b/crates/piston-core/src/backprop.rs index 762fa99a..1ab74310 100644 --- a/crates/piston-core/src/backprop.rs +++ b/crates/piston-core/src/backprop.rs @@ -299,6 +299,7 @@ impl Tensor { }) | LazyOp::FillConstant(_) | LazyOp::FillRandn(_) + | LazyOp::Eye(_) | LazyOp::Bernoulli(_) | LazyOp::Arange(_) | LazyOp::Cache(_) @@ -911,6 +912,7 @@ impl Tensor { }) | LazyOp::FillConstant(_) | LazyOp::FillRandn(_) + | LazyOp::Eye(_) | LazyOp::Bernoulli(_) | LazyOp::Arange(_) => {} LazyOp::View(View { src: arg, .. }) => { diff --git a/crates/piston-core/src/cpu/mod.rs b/crates/piston-core/src/cpu/mod.rs index 85b5b71c..5f18aca5 100644 --- a/crates/piston-core/src/cpu/mod.rs +++ b/crates/piston-core/src/cpu/mod.rs @@ -47,6 +47,7 @@ pub async fn apply_operation(op: LazyOp, dst: OpTensor) -> Result todo!(), LazyOp::FillConstant(_f) => todo!(), LazyOp::FillRandn(_f) => todo!(), + LazyOp::Eye(_e) => todo!(), LazyOp::Bernoulli(_b) => todo!(), LazyOp::Arange(_a) => todo!(), LazyOp::IndexAdd(_i) => todo!(), diff --git a/crates/piston-core/src/op.rs b/crates/piston-core/src/op.rs index 63f04b4f..ead10dd2 100644 --- a/crates/piston-core/src/op.rs +++ b/crates/piston-core/src/op.rs @@ -59,6 +59,7 @@ pub enum LazyOp { IndexAdd(IndexAdd), ScatterAdd(ScatterAdd), Trilu(TriluOp), + Eye(Eye), Arange(Arange), Copy(TensorCopy), Detach(Box), //Because the entire graph is lazy, you can't actually detach something without computing the graph in parts @@ -91,6 +92,7 @@ impl LazyOp { LazyOp::Trilu(t) => t.name(), LazyOp::FillConstant(f) => f.name(), LazyOp::FillRandn(f) => f.name(), + LazyOp::Eye(e) => e.name(), LazyOp::Bernoulli(b) => b.name(), LazyOp::Arange(a) => a.name(), LazyOp::RoPE(r) => r.name(), @@ -130,6 +132,7 @@ impl LazyOp { LazyOp::IndexAdd(ia) => ia.srcs(), LazyOp::ScatterAdd(sa) => sa.srcs(), LazyOp::Trilu(t) => t.srcs(), + LazyOp::Eye(e) => e.srcs(), LazyOp::Cache(c) => c.srcs(), LazyOp::Detach(d) => d.srcs(), LazyOp::View(v) => v.srcs(), @@ -169,6 +172,7 @@ impl LazyOp { LazyOp::Trilu(t) => t.supports_inplace(), LazyOp::FillConstant(fc) => fc.supports_inplace(), LazyOp::FillRandn(fr) => fr.supports_inplace(), + LazyOp::Eye(e) => e.supports_inplace(), LazyOp::Bernoulli(b) => b.supports_inplace(), LazyOp::Arange(a) => a.supports_inplace(), LazyOp::Cache(c) => c.supports_inplace(), @@ -189,7 +193,7 @@ impl LazyOp { pub fn can_be_parameter(&self) -> bool { matches!( self, - LazyOp::Const | LazyOp::FillConstant(_) | LazyOp::FillRandn(_) | LazyOp::Arange(_) + LazyOp::Const | LazyOp::FillConstant(_) | LazyOp::FillRandn(_) | LazyOp::Eye(_) | LazyOp::Arange(_) ) } @@ -226,6 +230,7 @@ impl LazyOp { LazyOp::Trilu(t) => t.check_invariants(), LazyOp::FillConstant(f) => f.check_invariants(), LazyOp::FillRandn(f) => f.check_invariants(), + LazyOp::Eye(e) => e.check_invariants(), LazyOp::Bernoulli(b) => b.check_invariants(), LazyOp::Arange(a) => a.check_invariants(), LazyOp::Cache(c) => c.check_invariants(), @@ -264,6 +269,7 @@ impl LazyOp { LazyOp::Trilu(t) => t.ir(), LazyOp::FillConstant(f) => f.ir(), LazyOp::FillRandn(f) => f.ir(), + LazyOp::Eye(e) => e.ir(), LazyOp::Bernoulli(b) => b.ir(), LazyOp::Arange(a) => a.ir(), LazyOp::Cache(c) => c.ir(), diff --git a/crates/piston-core/src/ops/eye.rs b/crates/piston-core/src/ops/eye.rs new file mode 100644 index 00000000..71c2d673 --- /dev/null +++ b/crates/piston-core/src/ops/eye.rs @@ -0,0 +1,186 @@ +use derive_new::new; +use half::f16; +use inline_wgsl::wgsl; +use piston_macros::IrFields; + +use crate::{ + Array, BindingMode, BuiltIn, DType, DynKernelMetadata, GPUOperation, Kernel, KernelElement, + KernelRenderable, KernelSource, OpGuards, OpTensor, Operation, OperationError, RVec, Scalar, + Shape, StorageView, Stride, WgslKernelBuilder, WgslPrimitive, WorkgroupSize, Workload, + gpu::{BindGroupLayoutDescriptor, dtype::WgslDType}, + rvec, +}; + +#[derive(new, Debug, Clone, IrFields)] +pub struct Eye { + pub shape: Shape, +} + +impl Operation for Eye { + fn name(&self) -> &'static str { + "Eye" + } + + fn compute_view(&self) -> Result { + let shape: Shape = self.shape.clone(); + let stride = Stride::from(&shape); + Ok(StorageView::new(shape, DType::F32, stride)) + } + + fn srcs(&self) -> RVec<&OpTensor> { + rvec![] + } + + fn supports_inplace(&self) -> bool { + false + } +} + +impl OpGuards for Eye { + fn check_shapes(&self) { + assert!( + self.shape.dim() == 2, + "Eye expects a 2D shape, got {:?}", + self.shape + ); + } + + fn check_dtypes(&self) {} +} + +pub enum EyeKernels { + Standard(Eye), +} + +impl GPUOperation for Eye { + type KernelEnum = EyeKernels; + + fn select_kernel(&self) -> Self::KernelEnum { + EyeKernels::Standard(self.clone()) + } +} + +impl KernelRenderable for EyeKernels { + fn register_bindings( + &self, + builder: &mut WgslKernelBuilder, + _: bool, + ) -> Result<(), OperationError> { + builder.register_storage("Y", BindingMode::ReadWrite, Array::

    ::default()); + builder.register_uniform(); + Ok(()) + } + + fn render( + &self, + _: bool, + dst: &OpTensor, + workgroup_size: &WorkgroupSize, + ) -> Result { + let device = dst.device().try_gpu()?; + let mut kernel_builder = WgslKernelBuilder::new( + workgroup_size.clone(), + rvec![ + BuiltIn::WorkgroupId, + BuiltIn::LocalInvocationIndex, + BuiltIn::NumWorkgroups + ], + device.compute_features().clone(), + ); + + self.register_bindings::

    (&mut kernel_builder, false)?; + kernel_builder.render_metadata(&self.metadata(dst, &self.kernel_element(dst))?); + + let N = (P::W as u32).render(); + let dtype = P::render_type(); + + kernel_builder.write_main(wgsl! { + let x_offset = workgroup_id.x * 64u; + let index = (workgroup_id.y * num_workgroups.x * 64u) + x_offset + local_invocation_index; + if (index >= metadata.numel / 'N) { + return; + } + + // Scalar-only kernel element, so 'N == 1 + let cols = metadata.cols; + let row = index / cols; + let col = index - row * cols; + if (row == col) { + Y[index] = 'dtype(1); + } else { + Y[index] = 'dtype(0); + } + }); + + Ok(kernel_builder.build()?) + } +} + +impl Kernel for EyeKernels { + type Metadata = DynKernelMetadata; + + fn kernel_name(&self) -> String { + match self { + EyeKernels::Standard(_) => "eye".to_string(), + } + } + + fn kernel_element(&self, _dst: &OpTensor) -> KernelElement { + // Keep scalar for correctness and simplicity. + KernelElement::Scalar + } + + fn calculate_dispatch(&self, dst: &OpTensor) -> Result { + Ok(Workload::std(dst.shape().numel(), self.kernel_element(dst))) + } + + fn storage_bind_group_layout( + &self, + _inplace: bool, + ) -> Result { + Ok(BindGroupLayoutDescriptor::unary_inplace()) + } + + fn metadata( + &self, + dst: &OpTensor, + _: &KernelElement, + ) -> Result { + let EyeKernels::Standard(op) = self; + let mut dyn_meta = DynKernelMetadata::new(); + dyn_meta.add_field("numel", dst.shape().numel() as u32); + let rows = op.shape[0] as u32; + let cols = op.shape[1] as u32; + dyn_meta.add_field("rows", rows); + dyn_meta.add_field("cols", cols); + Ok(dyn_meta) + } + + fn build_kernel( + &self, + inplace: bool, + dst: &OpTensor, + workgroup_size: &WorkgroupSize, + ) -> Result { + let kernel_element = self.kernel_element(dst); + match (dst.dtype(), &kernel_element) { + (DType::F32, KernelElement::Scalar) => { + self.render::>(inplace, dst, workgroup_size) + } + (DType::F16, KernelElement::Scalar) => { + self.render::>(inplace, dst, workgroup_size) + } + (DType::I32, KernelElement::Scalar) => { + self.render::>(inplace, dst, workgroup_size) + } + (DType::U32, KernelElement::Scalar) => { + self.render::>(inplace, dst, workgroup_size) + } + _ => Err(OperationError::CompileError(format!( + "Unsupported dtype {:?} or kernel element {:?}", + dst.dtype(), + kernel_element + ))), + } + } +} diff --git a/crates/piston-core/src/ops/mod.rs b/crates/piston-core/src/ops/mod.rs index cb4982fc..676d2e12 100644 --- a/crates/piston-core/src/ops/mod.rs +++ b/crates/piston-core/src/ops/mod.rs @@ -10,6 +10,7 @@ mod concat; mod conv; mod fill_constant; mod fill_randn; +mod eye; mod gather; mod index_add; mod index_write; @@ -43,6 +44,7 @@ pub use concat::*; pub use conv::*; pub use fill_constant::*; pub use fill_randn::*; +pub use eye::*; pub use gather::*; pub use index_add::*; pub use index_write::*; diff --git a/crates/piston-core/src/tensor.rs b/crates/piston-core/src/tensor.rs index dbd86865..3055a97a 100644 --- a/crates/piston-core/src/tensor.rs +++ b/crates/piston-core/src/tensor.rs @@ -1940,6 +1940,66 @@ pub fn rand>( ) } + +#[tensor_op(variants = [function])] +pub fn eye(n: usize, m: Option, options: TensorOptions) -> Result { + let m = m.unwrap_or(n); + let shape = Shape::from(vec![n, m]); + let device = options.device_or_default(); + let dtype = options.dtype_or_default(); + + if device.is_cpu() { + match dtype { + DType::F32 => { + let mut data = vec![0f32; n * m]; + let d = n.min(m); + for i in 0..d { + data[i * m + i] = 1.0; + } + return OpTensor::from_data(data, shape, options); + } + DType::F16 => { + let mut data = vec![half::f16::from_f32(0.0); n * m]; + let d = n.min(m); + for i in 0..d { + data[i * m + i] = half::f16::from_f32(1.0); + } + return OpTensor::from_data(data, shape, options); + } + DType::I32 => { + let mut data = vec![0i32; n * m]; + let d = n.min(m); + for i in 0..d { + data[i * m + i] = 1; + } + return OpTensor::from_data(data, shape, options); + } + DType::U32 => { + let mut data = vec![0u32; n * m]; + let d = n.min(m); + for i in 0..d { + data[i * m + i] = 1u32; + } + return OpTensor::from_data(data, shape, options); + } + _ => anyhow::bail!("dtype {:?} not supported for eye", dtype), + } + } + + let meta = StorageView { + shape: shape.clone(), + dtype, + stride: Stride::from(&shape), + }; + OpTensor::new( + LazyOp::Eye(Eye { shape }), + meta, + None, + device, + options.requires_grad_or_default(), + ) +} + /// Private implementation for full fn full_impl>( shape: &Shape, @@ -2463,6 +2523,7 @@ impl OpTensor { LazyOp::FillRandn(f) => f.create_gpu_compile_key(self, can_inplace, uniform).ok(), LazyOp::Bernoulli(b) => b.create_gpu_compile_key(self, can_inplace, uniform).ok(), LazyOp::Arange(a) => a.create_gpu_compile_key(self, can_inplace, uniform).ok(), + LazyOp::Eye(e) => e.create_gpu_compile_key(self, can_inplace, uniform).ok(), LazyOp::Copy(_) | LazyOp::View(_) | LazyOp::Const => None, } } @@ -2764,6 +2825,7 @@ pub fn compile_gpu_for_op( LazyOp::Gather(g) => g.compile_gpu(gpu_compile_key, gpu_device, debug).ok(), LazyOp::FillConstant(f) => f.compile_gpu(gpu_compile_key, gpu_device, debug).ok(), LazyOp::FillRandn(f) => f.compile_gpu(gpu_compile_key, gpu_device, debug).ok(), + LazyOp::Eye(e) => e.compile_gpu(gpu_compile_key, gpu_device, debug).ok(), LazyOp::Bernoulli(b) => b.compile_gpu(gpu_compile_key, gpu_device, debug).ok(), LazyOp::Arange(a) => a.compile_gpu(gpu_compile_key, gpu_device, debug).ok(), LazyOp::View(_) | LazyOp::Const => None, diff --git a/crates/piston-web/src/tensor.rs b/crates/piston-web/src/tensor.rs index 0c330ee7..0cdf2c61 100644 --- a/crates/piston-web/src/tensor.rs +++ b/crates/piston-web/src/tensor.rs @@ -925,6 +925,11 @@ pub fn rand( piston::rand(shape, lo, up, options) } +#[js_tensor_web_op(name = "Eye", variants = [function])] +pub fn eye(n: usize, m: Option, options: TensorOptions) -> anyhow::Result { + piston::eye(n, m, options) +} + #[js_tensor_web_op(name = "Zeros", variants = [function])] pub fn zeros(shape: Shape, options: TensorOptions) -> anyhow::Result { piston::zeros(shape, options) diff --git a/packages/web/src/globals.ts b/packages/web/src/globals.ts index b09fe368..901a93a2 100644 --- a/packages/web/src/globals.ts +++ b/packages/web/src/globals.ts @@ -21,6 +21,7 @@ import { DType, eq as eq_wasm, exp as exp_wasm, + eye as eye_wasm, float16_wasm, float32_wasm, floor as floor_wasm, @@ -115,6 +116,7 @@ export let swiglu: typeof swiglu_wasm; export let silu: typeof silu_wasm; export let square: typeof square_wasm; export let recip: typeof recip_wasm; +export let eye: typeof eye_wasm; export let tensor: { ( @@ -257,6 +259,7 @@ export async function initGlobals() { randint = wrapWithParam(wrapWithLibTensor(randint_wasm)); randn = wrapWithParam(wrapWithLibTensor(randn_wasm)); rand = wrapWithParam(wrapWithLibTensor(rand_wasm)); + eye = wrapWithLibTensor(eye_wasm); add = wrapWithLibTensor(add_wasm); addcdiv = wrapWithLibTensor(addcdiv_wasm); addcmul = wrapWithLibTensor(addcmul_wasm); diff --git a/packages/web/src/wasm.ts b/packages/web/src/wasm.ts index 79291837..199e7a7a 100644 --- a/packages/web/src/wasm.ts +++ b/packages/web/src/wasm.ts @@ -20,6 +20,7 @@ export { DType, eq, exp, + eye, float16 as float16_wasm, float32 as float32_wasm, floor, From 8937ca0351a1f232d8c54f88ff1ab049c75d498c Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 18:27:51 -0600 Subject: [PATCH 434/590] Remove old PyTorchy signature comments --- crates/piston-web/src/tensor.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/crates/piston-web/src/tensor.rs b/crates/piston-web/src/tensor.rs index 0cdf2c61..4bd4ba60 100644 --- a/crates/piston-web/src/tensor.rs +++ b/crates/piston-web/src/tensor.rs @@ -483,7 +483,6 @@ pub fn sum( } } -// mean(Tensor tensor, Dims? dim=None, bool keepdim=False) #[js_tensor_web_op(name = Mean, variants = [method])] pub fn mean( input: Tensor, @@ -497,7 +496,6 @@ pub fn mean( } } -// var(Tensor tensor, Dims? dim=None, bool keepdim=False) #[js_tensor_web_op(name = Var, variants = [method])] pub fn var( input: Tensor, @@ -511,7 +509,6 @@ pub fn var( } } -// max(Tensor tensor, Dims? dim=None, bool keepdim=False) #[js_tensor_web_op(name = Max, variants = [method])] pub fn max( input: Tensor, @@ -525,7 +522,6 @@ pub fn max( } } -// min(Tensor tensor, Dims? dim=None, bool keepdim=False) #[js_tensor_web_op(name = Min, variants = [method])] pub fn min( input: Tensor, @@ -613,7 +609,6 @@ pub fn slice( #[js_tensor_web_op(name = View, variants = [method])] pub fn view(input: Tensor, shape: ShapeWithOneHole) -> JsTensorResult {} -// unsqueeze(Tensor tensor, int dim) #[js_tensor_web_op(name = Unsqueeze, variants = [method])] pub fn unsqueeze(input: Tensor, dim: Dim) -> JsTensorResult {} From d3baff649fe9af0614a9600c341a5647b974338d Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 18:30:12 -0600 Subject: [PATCH 435/590] Add some missing op variants --- crates/piston-web/src/tensor.rs | 36 ++++++++++++++++----------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/crates/piston-web/src/tensor.rs b/crates/piston-web/src/tensor.rs index 4bd4ba60..4f47f5a8 100644 --- a/crates/piston-web/src/tensor.rs +++ b/crates/piston-web/src/tensor.rs @@ -300,21 +300,21 @@ impl JsTensor { macro_rules! impl_binary_op { ($op:ident, $Name:ident) => { // Web free-function exports for method and inplace (place outside paste!) - #[js_tensor_web_op(name = $Name, variants = [method, method_inplace])] + #[js_tensor_web_op(name = $Name, variants = [method, method_inplace, function])] pub fn $op(input: Tensor, other: TensorOrScalar) -> anyhow::Result {} }; } macro_rules! impl_binary_op_tensor_only { ($op:ident, $Name:ident) => { - #[js_tensor_web_op(name = $Name, variants = [method, method_inplace])] + #[js_tensor_web_op(name = $Name, variants = [method, method_inplace, function])] pub fn $op(input: Tensor, other: Tensor) -> anyhow::Result {} }; } macro_rules! impl_ternary_op { ($op:ident, $Name:ident) => { - #[js_tensor_web_op(name = $Name, variants = [method, method_inplace])] + #[js_tensor_web_op(name = $Name, variants = [method, method_inplace, function])] pub fn $op( input: Tensor, tensor1: Tensor, @@ -327,14 +327,14 @@ macro_rules! impl_ternary_op { macro_rules! impl_cmp_op { ($op:ident, $Name:ident) => { - #[js_tensor_web_op(name = $Name, variants = [method, method_inplace])] + #[js_tensor_web_op(name = $Name, variants = [method, method_inplace, function])] pub fn $op(input: Tensor, other: TensorOrScalar) -> anyhow::Result {} }; } macro_rules! impl_unary_op { ($op:ident, $Name:ident) => { - #[js_tensor_web_op(name = $Name, variants = [method, method_inplace])] + #[js_tensor_web_op(name = $Name, variants = [method, method_inplace, function])] pub fn $op(input: Tensor) -> anyhow::Result {} }; } @@ -575,7 +575,7 @@ pub fn norm( } } -#[js_tensor_web_op(name = Flatten, variants = [method])] +#[js_tensor_web_op(name = Flatten, variants = [method, function])] pub fn flatten( input: Tensor, #[op(default = 0)] start_dim: Dim, @@ -609,10 +609,10 @@ pub fn slice( #[js_tensor_web_op(name = View, variants = [method])] pub fn view(input: Tensor, shape: ShapeWithOneHole) -> JsTensorResult {} -#[js_tensor_web_op(name = Unsqueeze, variants = [method])] +#[js_tensor_web_op(name = Unsqueeze, variants = [method, function])] pub fn unsqueeze(input: Tensor, dim: Dim) -> JsTensorResult {} -#[js_tensor_web_op(name = Squeeze, variants = [method])] +#[js_tensor_web_op(name = Squeeze, variants = [method, function])] pub fn squeeze(input: Tensor, dim: Option) -> JsTensorResult { match dim { Some(dims) => input.squeeze(dims), @@ -620,13 +620,13 @@ pub fn squeeze(input: Tensor, dim: Option) -> JsTensorResult { } } -#[js_tensor_web_op(name = Permute, variants = [method])] +#[js_tensor_web_op(name = Permute, variants = [method, function])] pub fn permute(input: Tensor, dims: Dims) -> JsTensorResult {} -#[js_tensor_web_op(name = Transpose, variants = [method])] +#[js_tensor_web_op(name = Transpose, variants = [method, function])] pub fn transpose(input: Tensor, dim0: Dim, dim1: Dim) -> JsTensorResult {} -#[js_tensor_web_op(name = t, variants = [method])] +#[js_tensor_web_op(name = t, variants = [method, function])] pub fn t(input: Tensor) -> JsTensorResult {} #[js_tensor_web_op(getter, name = TUpper, variants = [method], target = T)] @@ -652,25 +652,25 @@ pub fn index_select(input: Tensor, indices: Tensor, dim: Dim) -> JsTensorResult #[js_tensor_web_op(name = IndexWrite, variants = [method])] pub fn index_write(input: Tensor, src: Tensor, write_start: Dims) -> JsTensorResult {} -#[js_tensor_web_op(name = Where, variants = [method], js_name = "where")] +#[js_tensor_web_op(name = Where, variants = [method, function], js_name = "where")] pub fn where_cond(input: Tensor, condition: Tensor, on_false: TensorOrScalar) -> JsTensorResult {} -#[js_tensor_web_op(name = ScatterAdd, variants = [method])] +#[js_tensor_web_op(name = ScatterAdd, variants = [method, function])] pub fn scatter_add(input: Tensor, indices: Tensor, source: Tensor, dim: Dim) -> JsTensorResult {} -#[js_tensor_web_op(name = IndexAdd, variants = [method_inplace])] +#[js_tensor_web_op(name = IndexAdd, variants = [method_inplace, function])] pub fn index_add(input: Tensor, indices: Tensor, source: Tensor, dim: Dim) -> JsTensorResult {} -#[js_tensor_web_op(name = Gather, variants = [method])] +#[js_tensor_web_op(name = Gather, variants = [method, function])] pub fn gather(input: Tensor, dim: Dim, index: Tensor) -> JsTensorResult {} -#[js_tensor_web_op(name = Triu, variants = [method, method_inplace])] +#[js_tensor_web_op(name = Triu, variants = [method, method_inplace, function])] pub fn triu(input: Tensor, k: Option) -> JsTensorResult {} -#[js_tensor_web_op(name = Tril, variants = [method, method_inplace])] +#[js_tensor_web_op(name = Tril, variants = [method, method_inplace, function])] pub fn tril(input: Tensor, k: Option) -> JsTensorResult {} -#[js_tensor_web_op(name = Lerp, variants = [method, method_inplace])] +#[js_tensor_web_op(name = Lerp, variants = [method, method_inplace, function])] pub fn lerp(input: Tensor, end: Tensor, weight: TensorOrScalar) -> JsTensorResult {} #[js_tensor_web_op(name = Bernoulli, variants = [function, method, method_inplace])] From 6bf94e98545e43f57e4abbf98d63fc37ba36f93f Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 18:30:39 -0600 Subject: [PATCH 436/590] -comment --- crates/piston-web/src/tensor.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/piston-web/src/tensor.rs b/crates/piston-web/src/tensor.rs index 4f47f5a8..984660e6 100644 --- a/crates/piston-web/src/tensor.rs +++ b/crates/piston-web/src/tensor.rs @@ -299,7 +299,6 @@ impl JsTensor { macro_rules! impl_binary_op { ($op:ident, $Name:ident) => { - // Web free-function exports for method and inplace (place outside paste!) #[js_tensor_web_op(name = $Name, variants = [method, method_inplace, function])] pub fn $op(input: Tensor, other: TensorOrScalar) -> anyhow::Result {} }; From 0df9368b79fb54fa024bc32e47f56fe9051a196f Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 18:40:30 -0600 Subject: [PATCH 437/590] Add new logical cmp ops --- crates/piston-core/src/backprop.rs | 8 +-- crates/piston-core/src/ops/cmp.rs | 81 ++++++++++++++++++++++++++---- crates/piston-core/src/tensor.rs | 3 ++ crates/piston-web/src/tensor.rs | 3 ++ packages/web/src/globals.ts | 9 ++++ packages/web/src/wasm.ts | 3 ++ 6 files changed, 93 insertions(+), 14 deletions(-) diff --git a/crates/piston-core/src/backprop.rs b/crates/piston-core/src/backprop.rs index 1ab74310..e766405b 100644 --- a/crates/piston-core/src/backprop.rs +++ b/crates/piston-core/src/backprop.rs @@ -3,7 +3,7 @@ /// Methods for backpropagation of gradients. use crate::ops::{BinaryOp, TernaryOp, UnaryOp}; use crate::{ - Affine, Alibi, Binary, Broadcast, Cast, Cmp, Concat, Conv, DType, Gather, GroupNorm, IndexAdd, + Affine, Alibi, Binary, Broadcast, Cast, Concat, Conv, DType, Flip, Gather, GroupNorm, IndexAdd, IndexSelect, LazyOp, Matmul, Norm, NormOp, OpTensor, Permute, Powf, Reduce, ReduceOp, Reindex, RoPE, ScatterAdd, ScopePusher, Slice, Softmax, Tensor, TensorId, TensorOptions, TensorTypeOrScalar, TensorTypeOrScalarEnum, Ternary, Unary, View, WhereCond, cat, rvec, zeros, @@ -247,8 +247,7 @@ impl Tensor { input: _node, op: UnaryOp::Floor, }) => nodes, - LazyOp::Cmp(Cmp { lhs: node, .. }) - | LazyOp::Unary(Unary { input: node, .. }) + LazyOp::Unary(Unary { input: node, .. }) | LazyOp::Reduce(Reduce { input: node, op: ReduceOp::Min | ReduceOp::Sum | ReduceOp::Max | ReduceOp::Norm2, @@ -291,6 +290,7 @@ impl Tensor { LazyOp::IndexWrite(_) => todo!(), LazyOp::Copy(_) => todo!(), LazyOp::Detach(_) + | LazyOp::Cmp(_) | LazyOp::Const | LazyOp::Alibi(_) | LazyOp::Reduce(Reduce { @@ -1170,7 +1170,7 @@ impl Tensor { } LazyOp::Norm(_) => todo!(), LazyOp::Const => panic!("piston internal error - const node in backprop"), - LazyOp::Cmp(_) => todo!(), + LazyOp::Cmp(_) => todo!("cmp backprop"), LazyOp::Powf(_) => todo!(), LazyOp::RoPE(RoPE { input: arg, diff --git a/crates/piston-core/src/ops/cmp.rs b/crates/piston-core/src/ops/cmp.rs index 2fd1167e..86c18660 100644 --- a/crates/piston-core/src/ops/cmp.rs +++ b/crates/piston-core/src/ops/cmp.rs @@ -23,6 +23,9 @@ pub enum CmpOp { Ge, Lt, Gt, + LogicalAnd, + LogicalOr, + LogicalXor, } impl CmpOp { @@ -34,6 +37,9 @@ impl CmpOp { CmpOp::Ge => "ge", CmpOp::Lt => "lt", CmpOp::Gt => "gt", + CmpOp::LogicalAnd => "logical_and", + CmpOp::LogicalOr => "logical_or", + CmpOp::LogicalXor => "logical_xor", } } @@ -45,6 +51,9 @@ impl CmpOp { CmpOp::Ge => ">=", CmpOp::Lt => "<", CmpOp::Gt => ">", + CmpOp::LogicalAnd => "&&", + CmpOp::LogicalOr => "||", + CmpOp::LogicalXor => "!=", } } } @@ -125,23 +134,58 @@ impl KernelRenderable for CmpKernels { KernelElement::Vec4 => "vec4", }; let op = inner.op.op_str(); + let booleanify = matches!( + inner.op, + CmpOp::LogicalAnd | CmpOp::LogicalOr | CmpOp::LogicalXor + ); + + let casted_scalar_dtype = match self.kernel_element(dst) { + KernelElement::Scalar => inner.lhs.dtype().as_wgsl().to_string(), + KernelElement::Vec2 => { + format!("vec2<{}>", inner.lhs.dtype().as_wgsl()) + } + KernelElement::Vec4 => { + format!("vec4<{}>", inner.lhs.dtype().as_wgsl()) + } + }; + + let A_expr = if booleanify { + wgsl! { + (A[index] != 'casted_scalar_dtype(0.)) + } + } else { + wgsl! { + A[index] + } + }; let assignment_expr = match &inner.rhs { - TensorTypeOrScalarEnum::Tensor(_) => wgsl! { - 'out_dtype(A[index] 'op B[index]) - }, + TensorTypeOrScalarEnum::Tensor(_) => { + let B_expr = if booleanify { + wgsl! { + (B[index] != 'casted_scalar_dtype(0.)) + } + } else { + wgsl! { + B[index] + } + }; + wgsl! { + 'out_dtype('A_expr 'op 'B_expr) + } + } TensorTypeOrScalarEnum::Scalar(_) => { - let casted_scalar_dtype = match self.kernel_element(dst) { - KernelElement::Scalar => inner.lhs.dtype().as_wgsl().to_string(), - KernelElement::Vec2 => { - format!("vec2<{}>", inner.lhs.dtype().as_wgsl()) + let value_expr = if booleanify { + wgsl! { + metadata.value != 0.0 } - KernelElement::Vec4 => { - format!("vec4<{}>", inner.lhs.dtype().as_wgsl()) + } else { + wgsl! { + metadata.value } }; wgsl! { - 'out_dtype(A[index] 'op 'casted_scalar_dtype(metadata.value)) + 'out_dtype('A_expr 'op 'casted_scalar_dtype('value_expr)) } } }; @@ -191,6 +235,9 @@ impl Operation for Cmp { CmpOp::Ge => "Ge", CmpOp::Lt => "Lt", CmpOp::Gt => "Gt", + CmpOp::LogicalAnd => "LogicalAnd", + CmpOp::LogicalOr => "LogicalOr", + CmpOp::LogicalXor => "LogicalXor", } } @@ -404,6 +451,9 @@ def {kn}(a, scalar): CmpOp::Ge => a_gpu.ge(b_gpu)?, CmpOp::Lt => a_gpu.le(b_gpu)?, CmpOp::Gt => a_gpu.gt(b_gpu)?, + CmpOp::LogicalAnd => a_gpu.logical_and(b_gpu)?, + CmpOp::LogicalOr => a_gpu.logical_or(b_gpu)?, + CmpOp::LogicalXor => a_gpu.logical_xor(b_gpu)?, }; let d_gpu = c_gpu.to(&Device::CPU)?.cast(DType::F32)?; @@ -412,6 +462,14 @@ def {kn}(a, scalar): } fn run_cmp_scalar_trial(prob: CmpScalarProblem, device: Device) -> anyhow::Result<()> { + // We'll just skip these for now + if matches!( + prob.op, + CmpOp::LogicalAnd | CmpOp::LogicalOr | CmpOp::LogicalXor + ) { + return Ok(()); + } + let CmpScalarProblem { op, shape, scalar } = prob; let a = randn(shape, None, None, Default::default())?; let ground = ground_truth_scalar(&a, scalar, &op)?.cast(DType::F32)?; @@ -424,6 +482,9 @@ def {kn}(a, scalar): CmpOp::Ge => a_gpu.ge(scalar)?, CmpOp::Lt => a_gpu.lt(scalar)?, CmpOp::Gt => a_gpu.gt(scalar)?, + CmpOp::LogicalAnd => a_gpu.logical_and(scalar)?, + CmpOp::LogicalOr => a_gpu.logical_or(scalar)?, + CmpOp::LogicalXor => a_gpu.logical_xor(scalar)?, }; let d_gpu = c_gpu.to(&Device::CPU)?.cast(DType::F32)?; diff --git a/crates/piston-core/src/tensor.rs b/crates/piston-core/src/tensor.rs index 3055a97a..bb8b570f 100644 --- a/crates/piston-core/src/tensor.rs +++ b/crates/piston-core/src/tensor.rs @@ -653,6 +653,9 @@ impl_cmp_op!(le, CmpOp::Le); impl_cmp_op!(ge, CmpOp::Ge); impl_cmp_op!(lt, CmpOp::Lt); impl_cmp_op!(gt, CmpOp::Gt); +impl_cmp_op!(logical_and, CmpOp::LogicalAnd); +impl_cmp_op!(logical_or, CmpOp::LogicalOr); +impl_cmp_op!(logical_xor, CmpOp::LogicalXor); impl_unary_op!(gelu, UnaryOp::Gelu); impl_unary_op!(tanh, UnaryOp::Tanh); diff --git a/crates/piston-web/src/tensor.rs b/crates/piston-web/src/tensor.rs index 984660e6..f5630f21 100644 --- a/crates/piston-web/src/tensor.rs +++ b/crates/piston-web/src/tensor.rs @@ -355,6 +355,9 @@ impl_cmp_op!(gt, Gt); impl_cmp_op!(ge, Ge); impl_cmp_op!(lt, Lt); impl_cmp_op!(le, Le); +impl_cmp_op!(logical_and, LogicalAnd); +impl_cmp_op!(logical_or, LogicalOr); +impl_cmp_op!(logical_xor, LogicalXor); impl_unary_op!(gelu, Gelu); impl_unary_op!(tanh, Tanh); diff --git a/packages/web/src/globals.ts b/packages/web/src/globals.ts index 901a93a2..a64924e0 100644 --- a/packages/web/src/globals.ts +++ b/packages/web/src/globals.ts @@ -34,6 +34,9 @@ import { int32_wasm, le as le_wasm, log as log_wasm, + logicalAnd as logicalAnd_wasm, + logicalOr as logicalOr_wasm, + logicalXor as logicalXor_wasm, lt as lt_wasm, ne as ne_wasm, neg as neg_wasm, @@ -98,6 +101,9 @@ export let gt: typeof gt_wasm; export let ge: typeof ge_wasm; export let lt: typeof lt_wasm; export let le: typeof le_wasm; +export let logicalAnd: typeof logicalAnd_wasm; +export let logicalOr: typeof logicalOr_wasm; +export let logicalXor: typeof logicalXor_wasm; export let gelu: typeof gelu_wasm; export let tanh: typeof tanh_wasm; export let exp: typeof exp_wasm; @@ -287,6 +293,9 @@ export async function initGlobals() { silu = wrapWithLibTensor(silu_wasm); square = wrapWithLibTensor(square_wasm); recip = wrapWithLibTensor(recip_wasm); + logicalAnd = wrapWithLibTensor(logicalAnd_wasm); + logicalOr = wrapWithLibTensor(logicalOr_wasm); + logicalXor = wrapWithLibTensor(logicalXor_wasm); bernoulli = wrapWithLibTensor(bernoulli_wasm); zeros = wrapWithParam(wrapWithLibTensor(zeros_wasm)); zerosLike = wrapWithParam(wrapWithLibTensor(zerosLike_wasm)); diff --git a/packages/web/src/wasm.ts b/packages/web/src/wasm.ts index 199e7a7a..e850f4d4 100644 --- a/packages/web/src/wasm.ts +++ b/packages/web/src/wasm.ts @@ -33,6 +33,9 @@ export { int32 as int32_wasm, le, log, + logicalAnd, + logicalOr, + logicalXor, lt, maximum, minimum, From 06c5d12991583913b1f53256bf9bd3640fec1de3 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 18:49:45 -0600 Subject: [PATCH 438/590] Add logical not, isnan, isinf --- crates/piston-core/src/backprop.rs | 55 ++++-- crates/piston-core/src/cpu/unary.rs | 3 + crates/piston-core/src/ops/unary.rs | 270 +++++++++++++++++++++++++++- crates/piston-core/src/tensor.rs | 3 + crates/piston-web/src/tensor.rs | 3 + packages/web/src/globals.ts | 9 + packages/web/src/wasm.ts | 3 + 7 files changed, 325 insertions(+), 21 deletions(-) diff --git a/crates/piston-core/src/backprop.rs b/crates/piston-core/src/backprop.rs index e766405b..220979f2 100644 --- a/crates/piston-core/src/backprop.rs +++ b/crates/piston-core/src/backprop.rs @@ -240,14 +240,26 @@ impl Tensor { } } LazyOp::Unary(Unary { - input: _node, - op: UnaryOp::Ceil, + input: node, + op: + UnaryOp::Gelu + | UnaryOp::Tanh + | UnaryOp::Exp + | UnaryOp::Log + | UnaryOp::Sin + | UnaryOp::Cos + | UnaryOp::Abs + | UnaryOp::Square + | UnaryOp::Sqrt + | UnaryOp::Relu + | UnaryOp::Relu2 + | UnaryOp::Neg + | UnaryOp::Reciprocal + | UnaryOp::Silu + | UnaryOp::Sigmoid + | UnaryOp::Swiglu, + .. }) - | LazyOp::Unary(Unary { - input: _node, - op: UnaryOp::Floor, - }) => nodes, - LazyOp::Unary(Unary { input: node, .. }) | LazyOp::Reduce(Reduce { input: node, op: ReduceOp::Min | ReduceOp::Sum | ReduceOp::Max | ReduceOp::Norm2, @@ -291,6 +303,15 @@ impl Tensor { LazyOp::Copy(_) => todo!(), LazyOp::Detach(_) | LazyOp::Cmp(_) + | LazyOp::Unary(Unary { + op: + UnaryOp::IsInf + | UnaryOp::IsNan + | UnaryOp::Ceil + | UnaryOp::Floor + | UnaryOp::LogicalNot, + .. + }) | LazyOp::Const | LazyOp::Alibi(_) | LazyOp::Reduce(Reduce { @@ -790,6 +811,14 @@ impl Tensor { input: _, op: UnaryOp::Ceil, }) => Err(BackpropError::BackwardNotSupported { op: "ceil" })?, + LazyOp::Unary(Unary { + input: _, + op: UnaryOp::Floor, + }) => Err(BackpropError::BackwardNotSupported { op: "floor" })?, + LazyOp::Unary(Unary { + input: _, + op: UnaryOp::LogicalNot, + }) => Err(BackpropError::BackwardNotSupported { op: "logical_not" })?, LazyOp::Unary(Unary { input: arg, op: UnaryOp::Gelu, @@ -896,11 +925,7 @@ impl Tensor { let arg_grad = grad.clone().mul(node.clone())?.mul((1. - node.clone())?)?; ctx.add(&arg, arg_grad)?; } - LazyOp::Unary(Unary { - input: _, - op: UnaryOp::Floor, - }) - | LazyOp::Reduce(Reduce { + LazyOp::Reduce(Reduce { input: _, op: ReduceOp::ArgMax, .. @@ -914,7 +939,11 @@ impl Tensor { | LazyOp::FillRandn(_) | LazyOp::Eye(_) | LazyOp::Bernoulli(_) - | LazyOp::Arange(_) => {} + | LazyOp::Arange(_) + | LazyOp::Unary(Unary { + op: UnaryOp::IsInf | UnaryOp::IsNan, + .. + }) => {} LazyOp::View(View { src: arg, .. }) => { let arg = arg.wrap(); let arg_grad = grad.clone().view(arg.shape().clone())?; diff --git a/crates/piston-core/src/cpu/unary.rs b/crates/piston-core/src/cpu/unary.rs index 1cab5124..adb1f908 100644 --- a/crates/piston-core/src/cpu/unary.rs +++ b/crates/piston-core/src/cpu/unary.rs @@ -93,6 +93,9 @@ macro_rules! impl_unary_ops { UnaryOp::Silu => Self::silu(op.input(), dst).await, UnaryOp::Sigmoid => Self::sigmoid(op.input(), dst).await, UnaryOp::Swiglu => Self::swiglu(op.input(), dst).await, + UnaryOp::LogicalNot => todo!("logical_not"), + UnaryOp::IsNan => todo!("isnan"), + UnaryOp::IsInf => todo!("isinf"), } } } diff --git a/crates/piston-core/src/ops/unary.rs b/crates/piston-core/src/ops/unary.rs index 135bb388..5a405a47 100644 --- a/crates/piston-core/src/ops/unary.rs +++ b/crates/piston-core/src/ops/unary.rs @@ -10,8 +10,8 @@ use strum_macros::EnumIter; use crate::{ Array, BindingMode, BuiltIn, DType, GPUOperation, Kernel, KernelElement, KernelRenderable, - KernelSource, OpGuards, OpTensor, Operation, OperationError, RVec, Scalar, StorageView, Vec2, - Vec4, WgslKernelBuilder, WgslPrimitive, WorkgroupSize, Workload, + KernelSource, OpGuards, OpTensor, Operation, OperationError, RVec, Scalar, StorageView, Stride, + Vec2, Vec4, WgslKernelBuilder, WgslPrimitive, WorkgroupSize, Workload, gpu::{BindGroupLayoutDescriptor, dtype::WgslDType}, rvec, }; @@ -40,6 +40,9 @@ pub enum UnaryOp { Silu, Sigmoid, Swiglu, + LogicalNot, + IsNan, + IsInf, } impl UnaryOp { @@ -63,6 +66,9 @@ impl UnaryOp { UnaryOp::Silu => "silu".into(), UnaryOp::Sigmoid => "sigmoid".into(), UnaryOp::Swiglu => "swiglu".into(), + UnaryOp::LogicalNot => "logical_not".into(), + UnaryOp::IsNan => "isnan".into(), + UnaryOp::IsInf => "isinf".into(), } } @@ -92,7 +98,37 @@ impl KernelRenderable for UnaryKernels { builder.register_storage("X", BindingMode::ReadWrite, Array::

    ::default()); } else { builder.register_storage("X", BindingMode::ReadOnly, Array::

    ::default()); - builder.register_storage("Y", BindingMode::ReadWrite, Array::

    ::default()); + let UnaryKernels::Standard(inner) = self; + if matches!( + inner.op, + UnaryOp::LogicalNot | UnaryOp::IsNan | UnaryOp::IsInf + ) { + match self.kernel_element(&inner.input) { + KernelElement::Scalar => { + builder.register_storage( + "Y", + BindingMode::ReadWrite, + Array::>::default(), + ); + } + KernelElement::Vec2 => { + builder.register_storage( + "Y", + BindingMode::ReadWrite, + Array::>::default(), + ); + } + KernelElement::Vec4 => { + builder.register_storage( + "Y", + BindingMode::ReadWrite, + Array::>::default(), + ); + } + } + } else { + builder.register_storage("Y", BindingMode::ReadWrite, Array::

    ::default()); + } } builder.register_uniform(); Ok(()) @@ -149,6 +185,15 @@ impl KernelRenderable for UnaryKernels { kernel_builder.write_global(Unary::render_sigmoid::

    ()); kernel_builder.write_global(Unary::render_swiglu::

    ()); } + UnaryOp::LogicalNot => { + kernel_builder.write_global(Unary::render_logical_not::

    ()); + } + UnaryOp::IsNan => { + kernel_builder.write_global(Unary::render_isnan::

    (inner.input.dtype())); + } + UnaryOp::IsInf => { + kernel_builder.write_global(Unary::render_isinf::

    (inner.input.dtype())); + } _ => {} }; @@ -273,6 +318,102 @@ impl Unary { } } } + + // fn input_accessor(input_dtype: DType) -> String { + // match P::W { + // 1 => input_dtype.as_wgsl().to_string(), + // 2 => format!("vec2<{}>", input_dtype.as_wgsl()), + // 4 => format!("vec4<{}>", input_dtype.as_wgsl()), + // _ => panic!("Unsupported W for i32_equivalent_accessor: {:?}", P::W), + // } + // } + + fn equivalent_accessor(dtype: String) -> String { + match P::W { + 1 => dtype, + 2 => format!("vec2<{}>", dtype), + 4 => format!("vec4<{}>", dtype), + _ => panic!("Unsupported W for equivalent_accessor: {:?}", P::W), + } + } + + fn render_logical_not() -> String { + let accessor = P::render_type(); + let output_accessor = Self::equivalent_accessor::

    ("i32".to_string()); + + wgsl! { + fn logical_not(val: 'accessor) -> 'output_accessor { + return 'output_accessor(val == 'accessor(0.)); + } + } + } + + fn render_isnan(dtype: DType) -> String { + let accessor = P::render_type(); + let output_accessor = Self::equivalent_accessor::

    ("i32".to_string()); + + // wgsl! { + // let other_max + // fn isnan(val: 'accessor) -> 'output_accessor { + // return 'output_accessor(max()); + // } + // } + match dtype { + DType::F16 => { + wgsl! { + fn isnan(val: 'accessor) -> 'output_accessor { + let packed_val = pack2x16float(vec2(val, 0.0h)); + + let bits = packed_val & 0x0000ffffu; + + return 'output_accessor((bits & 0x7fffu) > 0x7C00u); + } + } + } + DType::F32 => { + let u32_accessor = Self::equivalent_accessor::

    ("u32".to_string()); + wgsl! { + fn isnan(val: 'accessor) -> 'output_accessor { + return 'output_accessor((bitcast<'u32_accessor>(val) & 'u32_accessor(0x7fffffffu)) > 'u32_accessor(0x7f800000u)); + } + } + } + _ => { + panic!("Unsupported dtype for isinf: {:?}", dtype); + } + } + } + + fn render_isinf(dtype: DType) -> String { + let accessor = P::render_type(); + let output_accessor = Self::equivalent_accessor::

    ("i32".to_string()); + + match dtype { + // TODO: F16 arm has never been tested + DType::F16 => { + wgsl! { + fn isinf(val: 'accessor) -> 'output_accessor { + let packed_val = pack2x16float(vec2(val, 0.0h)); + + let bits = packed_val & 0x0000ffffu; + + return 'output_accessor((bits & 0x7fffu) == 0x7C00u); + } + } + } + DType::F32 => { + let u32_accessor = Self::equivalent_accessor::

    ("u32".to_string()); + wgsl! { + fn isinf(val: 'accessor) -> 'output_accessor { + return 'output_accessor((bitcast<'u32_accessor>(val) & 'u32_accessor(0x7fffffffu)) == 'u32_accessor(0x7f800000u)); + } + } + } + _ => { + panic!("Unsupported dtype for isinf: {:?}", dtype); + } + } + } } #[derive(Debug, ShaderType, WgslMetadata)] @@ -307,11 +448,23 @@ impl Operation for Unary { UnaryOp::Silu => "Silu", UnaryOp::Sigmoid => "Sigmoid", UnaryOp::Swiglu => "Swiglu", + UnaryOp::LogicalNot => "LogicalNot", + UnaryOp::IsNan => "IsNan", + UnaryOp::IsInf => "IsInf", } } fn compute_view(&self) -> Result { - Ok(self.input.storage_view().clone()) + if matches!( + self.op, + UnaryOp::LogicalNot | UnaryOp::IsNan | UnaryOp::IsInf + ) { + let shape = self.input.shape().clone(); + let stride = Stride::from(&shape); + Ok(StorageView::new(shape, DType::I32, stride)) + } else { + Ok(self.input.storage_view().clone()) + } } #[inline] @@ -320,7 +473,10 @@ impl Operation for Unary { } fn supports_inplace(&self) -> bool { - true + !matches!( + self.op, + UnaryOp::LogicalNot | UnaryOp::IsNan | UnaryOp::IsInf + ) } } @@ -382,7 +538,20 @@ impl Kernel for UnaryKernels { workgroup_size: &WorkgroupSize, ) -> Result { let kernel_element = self.kernel_element(dst); - match (dst.dtype(), &kernel_element) { + let UnaryKernels::Standard(inner) = self; + match inner.op { + UnaryOp::LogicalNot | UnaryOp::IsNan | UnaryOp::IsInf => { + if dst.dtype() == DType::F32 { + panic!( + "Unsupported dtype for unary operation {:?} with boolean output: {:?}", + inner.name(), + dst.dtype() + ); + } + } + _ => {} + } + match (inner.input.dtype(), &kernel_element) { (DType::F32, KernelElement::Scalar) => { self.render::>(inplace, dst, workgroup_size) } @@ -401,6 +570,15 @@ impl Kernel for UnaryKernels { (DType::F16, KernelElement::Vec4) => { self.render::>(inplace, dst, workgroup_size) } + (DType::I32, KernelElement::Scalar) => { + self.render::>(inplace, dst, workgroup_size) + } + (DType::I32, KernelElement::Vec2) => { + self.render::>(inplace, dst, workgroup_size) + } + (DType::I32, KernelElement::Vec4) => { + self.render::>(inplace, dst, workgroup_size) + } _ => Err(OperationError::CompileError(format!( "Unsupported dtype {:?} or kernel element {:?}", dst.dtype(), @@ -424,7 +602,9 @@ impl Kernel for UnaryKernels { mod tests { use test_strategy::{Arbitrary, proptest}; - use crate::{Device, DeviceRequest, Tensor, UnaryOp, randn, test_util::run_py_prg}; + use crate::{ + Device, DeviceRequest, Tensor, TensorOptions, UnaryOp, randn, test_util::run_py_prg, + }; #[derive(Arbitrary, Debug)] struct UnaryProblem { @@ -454,8 +634,37 @@ def {kn}(a): "#, ); + let imp_with_cast_prg = format!( + r#" +import torch +def {kn}(a): + return torch.{kn}(torch.from_numpy(a), {args}).float().numpy() +"#, + ); + + let swiglu_prg = format!( + r#" +import torch +def {kn}(a): + x = torch.from_numpy(a) + return (x * x * torch.sigmoid(x)).numpy() +"#, + ); + + let relu2_prg = format!( + r#" +import torch +import torch.nn.functional as F +def {kn}(a): + return (F.relu(torch.from_numpy(a), {args})**2).numpy() +"#, + ); + let prg = match op { - UnaryOp::Gelu | UnaryOp::Silu | UnaryOp::Sigmoid => func_prg, + UnaryOp::Gelu | UnaryOp::Silu | UnaryOp::Sigmoid | UnaryOp::Relu => func_prg, + UnaryOp::Swiglu => swiglu_prg, + UnaryOp::Relu2 => relu2_prg, + UnaryOp::LogicalNot | UnaryOp::IsNan | UnaryOp::IsInf => imp_with_cast_prg, _ => imp_prg, }; @@ -463,6 +672,16 @@ def {kn}(a): } fn run_unary_trial(prob: UnaryProblem, device: Device) -> anyhow::Result<()> { + // Not implemented on CPU for now + if device.is_cpu() + && matches!( + prob.op, + UnaryOp::LogicalNot | UnaryOp::IsNan | UnaryOp::IsInf + ) + { + return Ok(()); + } + let UnaryProblem { op, B, M } = prob; let a = randn((B, M), None, None, Default::default())?; @@ -492,6 +711,9 @@ def {kn}(a): UnaryOp::Silu => a.silu()?, UnaryOp::Sigmoid => a.sigmoid()?, UnaryOp::Swiglu => a.swiglu()?, + UnaryOp::LogicalNot => a.logical_not()?.cast(crate::DType::F32)?, + UnaryOp::IsNan => a.isnan()?.cast(crate::DType::F32)?, + UnaryOp::IsInf => a.isinf()?.cast(crate::DType::F32)?, }; let (atol, rtol) = match op { @@ -517,4 +739,36 @@ def {kn}(a): let device = Device::request_device(DeviceRequest::CPU).unwrap(); run_unary_trial(prob, device).unwrap(); } + + #[test] + fn test_isnan_detection_f32() -> anyhow::Result<()> { + let device = Device::request_device(DeviceRequest::GPU).unwrap(); + let t = Tensor::from_data( + vec![0.0f32, 1.0, f32::NAN, f32::INFINITY, f32::NEG_INFINITY], + 5, + TensorOptions::new(), + )? + .to(&device)? + .isnan()?; + + let out = t.to(&Device::CPU)?.to_vec::()?; + assert_eq!(out, vec![0, 0, 1, 0, 0]); + Ok(()) + } + + #[test] + fn test_isinf_detection_f32() -> anyhow::Result<()> { + let device = Device::request_device(DeviceRequest::GPU).unwrap(); + let t = Tensor::from_data( + vec![0.0f32, 1.0, f32::NAN, f32::INFINITY, f32::NEG_INFINITY], + 5, + TensorOptions::new(), + )? + .to(&device)? + .isinf()?; + + let out = t.to(&Device::CPU)?.to_vec::()?; + assert_eq!(out, vec![0, 0, 0, 1, 1]); + Ok(()) + } } diff --git a/crates/piston-core/src/tensor.rs b/crates/piston-core/src/tensor.rs index bb8b570f..f0475748 100644 --- a/crates/piston-core/src/tensor.rs +++ b/crates/piston-core/src/tensor.rs @@ -675,6 +675,9 @@ impl_unary_op!(swiglu, UnaryOp::Swiglu); impl_unary_op!(silu, UnaryOp::Silu); impl_unary_op!(square, UnaryOp::Square); impl_unary_op!(recip, UnaryOp::Reciprocal); +impl_unary_op!(logical_not, UnaryOp::LogicalNot); +impl_unary_op!(isnan, UnaryOp::IsNan); +impl_unary_op!(isinf, UnaryOp::IsInf); #[tensor_op(variants = [function, method])] pub fn cast(input: OpTensor, dst_dtype: DType) -> Result { diff --git a/crates/piston-web/src/tensor.rs b/crates/piston-web/src/tensor.rs index f5630f21..04c50d67 100644 --- a/crates/piston-web/src/tensor.rs +++ b/crates/piston-web/src/tensor.rs @@ -377,6 +377,9 @@ impl_unary_op!(swiglu, Swiglu); impl_unary_op!(silu, Silu); impl_unary_op!(square, Square); impl_unary_op!(recip, Recip); +impl_unary_op!(logical_not, LogicalNot); +impl_unary_op!(isnan, IsNan); +impl_unary_op!(isinf, IsInf); #[js_tensor_web_op(name = Full, variants = [function])] pub fn full(shape: Shape, value: f32, options: TensorOptions) -> JsTensorResult { diff --git a/packages/web/src/globals.ts b/packages/web/src/globals.ts index a64924e0..53e38822 100644 --- a/packages/web/src/globals.ts +++ b/packages/web/src/globals.ts @@ -32,9 +32,12 @@ import { gpu_wasm, gt as gt_wasm, int32_wasm, + isinf as isinf_wasm, + isnan as isnan_wasm, le as le_wasm, log as log_wasm, logicalAnd as logicalAnd_wasm, + logicalNot as logicalNot_wasm, logicalOr as logicalOr_wasm, logicalXor as logicalXor_wasm, lt as lt_wasm, @@ -102,6 +105,7 @@ export let ge: typeof ge_wasm; export let lt: typeof lt_wasm; export let le: typeof le_wasm; export let logicalAnd: typeof logicalAnd_wasm; +export let logicalNot: typeof logicalNot_wasm; export let logicalOr: typeof logicalOr_wasm; export let logicalXor: typeof logicalXor_wasm; export let gelu: typeof gelu_wasm; @@ -123,6 +127,8 @@ export let silu: typeof silu_wasm; export let square: typeof square_wasm; export let recip: typeof recip_wasm; export let eye: typeof eye_wasm; +export let isnan: typeof isnan_wasm; +export let isinf: typeof isinf_wasm; export let tensor: { ( @@ -293,7 +299,10 @@ export async function initGlobals() { silu = wrapWithLibTensor(silu_wasm); square = wrapWithLibTensor(square_wasm); recip = wrapWithLibTensor(recip_wasm); + isnan = wrapWithLibTensor(isnan_wasm); + isinf = wrapWithLibTensor(isinf_wasm); logicalAnd = wrapWithLibTensor(logicalAnd_wasm); + logicalNot = wrapWithLibTensor(logicalNot_wasm); logicalOr = wrapWithLibTensor(logicalOr_wasm); logicalXor = wrapWithLibTensor(logicalXor_wasm); bernoulli = wrapWithLibTensor(bernoulli_wasm); diff --git a/packages/web/src/wasm.ts b/packages/web/src/wasm.ts index e850f4d4..77eb8d7b 100644 --- a/packages/web/src/wasm.ts +++ b/packages/web/src/wasm.ts @@ -31,9 +31,12 @@ export { gpu as gpu_wasm, gt, int32 as int32_wasm, + isinf, + isnan, le, log, logicalAnd, + logicalNot, logicalOr, logicalXor, lt, From 9c7ddd8e6d9f9f3db6d23d8e21c26ba263ccb4b5 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 18:53:22 -0600 Subject: [PATCH 439/590] Add flip op --- crates/piston-core/src/backprop.rs | 6 ++ crates/piston-core/src/cpu/reindex.rs | 70 +++++++++++++++++++++- crates/piston-core/src/op.rs | 1 + crates/piston-core/src/ops/reindex/flip.rs | 57 ++++++++++++++++++ crates/piston-core/src/ops/reindex/mod.rs | 30 ++++++++++ crates/piston-core/src/tensor.rs | 11 ++++ crates/piston-web/src/tensor.rs | 3 + 7 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 crates/piston-core/src/ops/reindex/flip.rs diff --git a/crates/piston-core/src/backprop.rs b/crates/piston-core/src/backprop.rs index 220979f2..0fb4a450 100644 --- a/crates/piston-core/src/backprop.rs +++ b/crates/piston-core/src/backprop.rs @@ -268,6 +268,7 @@ impl Tensor { | LazyOp::Reindex(Reindex::Permute(Permute { src: node, .. })) | LazyOp::Reindex(Reindex::Broadcast(Broadcast { src: node, .. })) | LazyOp::Reindex(Reindex::Slice(Slice { src: node, .. })) + | LazyOp::Reindex(Reindex::Flip(Flip { src: node, .. })) | LazyOp::Softmax(Softmax { input: node, .. }) | LazyOp::RoPE(RoPE { input: node, .. }) | LazyOp::Powf(Powf { src: node, .. }) => { @@ -683,6 +684,11 @@ impl Tensor { let arg_grad = grad.permute(inv_dims)?; ctx.add(&arg, arg_grad)?; } + LazyOp::Reindex(Reindex::Flip(Flip { src: arg, dims })) => { + let arg = arg.wrap(); + let arg_grad = grad.flip(dims.clone())?; + ctx.add(&arg, arg_grad)?; + } LazyOp::Reduce(Reduce { input: arg, reduced_shape, diff --git a/crates/piston-core/src/cpu/reindex.rs b/crates/piston-core/src/cpu/reindex.rs index c43e9f8f..d37a4363 100644 --- a/crates/piston-core/src/cpu/reindex.rs +++ b/crates/piston-core/src/cpu/reindex.rs @@ -1,6 +1,6 @@ use super::utils::cpu_store_result; use crate::{ - Broadcast, CPUOperation, DType, OpTensor, OperationError, Permute, Reindex, Shape, Slice, + Broadcast, CPUOperation, DType, Flip, OpTensor, OperationError, Permute, Reindex, Shape, Slice, Stride, TensorDType, }; use half::{bf16, f16}; @@ -15,6 +15,7 @@ impl CPUOperation for Reindex { Reindex::Permute(p) => p.apply_cpu(dst).await, Reindex::Slice(s) => s.apply_cpu(dst).await, Reindex::Broadcast(b) => b.apply_cpu(dst).await, + Reindex::Flip(f) => f.apply_cpu(dst).await, } } } @@ -158,6 +159,73 @@ impl CPUOperation for Broadcast { } } +#[maybe_async(AFIT)] +#[cfg_attr(target_arch = "wasm32", async_trait::async_trait)] +impl CPUOperation for Flip { + #[maybe_async] + async fn apply_cpu(&self, dst: OpTensor) -> Result { + match dst.dtype() { + DType::F32 => apply_flip::(self, dst).await, + DType::BF16 => apply_flip::(self, dst).await, + DType::F16 => apply_flip::(self, dst).await, + DType::I32 => apply_flip::(self, dst).await, + DType::U32 => apply_flip::(self, dst).await, + _ => todo!(), + } + } +} + +#[maybe_async] +async fn apply_flip(f: &Flip, dst: OpTensor) -> Result { + let result = flip_cpu::(&f.src.to_vec::().await?, f.src.shape(), &f.dims); + super::utils::cpu_store_result(&dst, &result); + Ok(dst) +} + +fn flip_cpu>( + src: &[T], + src_shape: SrcShape, + dims: &[usize], +) -> Vec { + let src_shape = &Shape::promote(src_shape.into(), 4); + let src_stride = &Stride::from(src_shape); + + let src_shape_arr: [usize; 4] = src_shape.try_into().unwrap(); + let src_stride_arr: [usize; 4] = src_stride.into(); + + let dst_numel = src_shape.numel(); + let mut result = vec![T::zero(); dst_numel]; + + // Build promoted flip mask + let pad_len = 0; + let mut mask = [false; 4]; + for &d in dims.iter() { + let pd = d + pad_len; + if pd < 4 { + mask[pd] = true; + } + } + + // dst_shape == src_shape for flip + let dst_stride_arr = src_stride_arr; + + (0..dst_numel).for_each(|i| { + let dst_index = offset_to_ndindex(i, dst_stride_arr); + let mut src_index = [0usize; 4]; + for ax in 0..4 { + src_index[ax] = if mask[ax] { + src_shape_arr[ax] - 1 - dst_index[ax] + } else { + dst_index[ax] + }; + } + let src_offset = nd_index_to_offset(src_index, src_stride_arr); + result[i] = src[src_offset]; + }); + + result +} + #[maybe_async] async fn apply_broadcast( b: &Broadcast, diff --git a/crates/piston-core/src/op.rs b/crates/piston-core/src/op.rs index ead10dd2..4326dd4d 100644 --- a/crates/piston-core/src/op.rs +++ b/crates/piston-core/src/op.rs @@ -211,6 +211,7 @@ impl LazyOp { Reindex::Permute(p) => p.check_invariants(), Reindex::Slice(s) => s.check_invariants(), Reindex::Broadcast(b) => b.check_invariants(), + Reindex::Flip(f) => f.check_invariants(), }, LazyOp::Concat(c) => c.check_invariants(), LazyOp::Norm(n) => n.check_invariants(), diff --git a/crates/piston-core/src/ops/reindex/flip.rs b/crates/piston-core/src/ops/reindex/flip.rs new file mode 100644 index 00000000..63a72dab --- /dev/null +++ b/crates/piston-core/src/ops/reindex/flip.rs @@ -0,0 +1,57 @@ +use encase::ShaderType; +use piston_macros::{IrFields, WgslMetadata}; + +use crate::{OpGuards, OpTensor, Operation, OperationError, RVec, StorageView, Stride, rvec}; + +#[derive(Debug, WgslMetadata, ShaderType, derive_new::new)] +pub struct FlipMeta { + src_shape: glam::UVec4, + dst_shape: glam::UVec4, + src_stride: glam::UVec4, + dst_stride: glam::UVec4, + src_numel: u32, + dst_numel: u32, + flip_mask: glam::UVec4, +} + +#[derive(derive_new::new, Debug, Clone, IrFields)] +pub struct Flip { + pub src: OpTensor, + pub dims: RVec, +} + +impl Flip { + /// Promote dims to 4D indexing space by offsetting with pad length + pub fn promote(&self) -> RVec { + let pad_len = 4 - self.src.shape().dim(); + self.dims.iter().map(|&d| d + pad_len).collect() + } +} + +impl OpGuards for Flip { + fn check_shapes(&self) { + let rank = self.src.shape().dim(); + assert!(self.dims.iter().all(|&d| d < rank)); + // No duplicate dims + let mut seen = std::collections::HashSet::new(); + assert!(self.dims.iter().all(|d| seen.insert(*d))); + } + + fn check_dtypes(&self) {} +} + +impl Operation for Flip { + fn name(&self) -> &'static str { + "Flip" + } + + fn compute_view(&self) -> Result { + let shape = self.src.shape().clone(); + let stride = Stride::from(&shape); + Ok(StorageView::new(shape, self.src.dtype(), stride)) + } + + fn srcs(&self) -> RVec<&OpTensor> { + rvec![&self.src] + } +} diff --git a/crates/piston-core/src/ops/reindex/mod.rs b/crates/piston-core/src/ops/reindex/mod.rs index 2c1a8dbf..bd459963 100644 --- a/crates/piston-core/src/ops/reindex/mod.rs +++ b/crates/piston-core/src/ops/reindex/mod.rs @@ -1,9 +1,12 @@ mod broadcast; +mod flip; mod permute; mod slice; pub use broadcast::Broadcast; use broadcast::BroadcastMeta; +pub use flip::Flip; +use flip::FlipMeta; use half::f16; pub use permute::Permute; use permute::PermuteMeta; @@ -28,6 +31,7 @@ pub enum Reindex { Permute(Permute), Slice(Slice), Broadcast(Broadcast), + Flip(Flip), } pub enum ReindexKernels { @@ -108,6 +112,10 @@ impl KernelRenderable for ReindexKernels { // Broadcasting is valid if dims are equal, or if one of the dims is 1 var src_index = select(dst_index, vec4(0u), metadata.src_shape == vec4(1u)); }, + Reindex::Flip(_) => wgsl! { + let flipped = (metadata.src_shape - vec4(1u)) - dst_index; + var src_index = select(dst_index, flipped, metadata.flip_mask != vec4(0u)); + }, }; kernel_builder.write_main(body); @@ -227,6 +235,20 @@ impl Kernel for ReindexKernels { Reindex::Broadcast(_) => Ok(ReindexMeta::Broadcast(BroadcastMeta::new( src_shape, dst_shape, src_stride, dst_stride, src_numel, dst_numel, ))), + Reindex::Flip(f) => { + // Build flip mask: 1 for flipped axes in promoted 4D space + let promoted = f.promote(); + let mut mask = [0u32; 4]; + for &d in promoted.iter() { + if d < 4 { + mask[d] = 1; + } + } + let flip_mask = UVec4::from(mask); + Ok(ReindexMeta::Flip(FlipMeta::new( + src_shape, dst_shape, src_stride, dst_stride, src_numel, dst_numel, flip_mask, + ))) + } } } @@ -242,6 +264,7 @@ pub enum ReindexMeta { Permute(PermuteMeta), Slice(SliceMeta), Broadcast(BroadcastMeta), + Flip(FlipMeta), } impl KernelMetadata for ReindexMeta { @@ -250,6 +273,7 @@ impl KernelMetadata for ReindexMeta { ReindexMeta::Permute(p) => p.render_meta(), ReindexMeta::Slice(s) => s.render_meta(), ReindexMeta::Broadcast(b) => b.render_meta(), + ReindexMeta::Flip(f) => f.render_meta(), } } @@ -258,6 +282,7 @@ impl KernelMetadata for ReindexMeta { ReindexMeta::Permute(p) => p.write(uniform), ReindexMeta::Slice(s) => s.write(uniform), ReindexMeta::Broadcast(b) => b.write(uniform), + ReindexMeta::Flip(f) => f.write(uniform), } } } @@ -268,6 +293,7 @@ impl OpGuards for Reindex { Reindex::Permute(p) => p.check_shapes(), Reindex::Slice(s) => s.check_shapes(), Reindex::Broadcast(b) => b.check_shapes(), + Reindex::Flip(f) => f.check_shapes(), } } @@ -276,6 +302,7 @@ impl OpGuards for Reindex { Reindex::Permute(p) => p.check_dtypes(), Reindex::Slice(s) => s.check_dtypes(), Reindex::Broadcast(b) => b.check_dtypes(), + Reindex::Flip(f) => f.check_dtypes(), } } } @@ -286,6 +313,7 @@ impl Operation for Reindex { Reindex::Permute(_) => "Permute", Reindex::Slice(_) => "Slice", Reindex::Broadcast(_) => "Broadcast", + Reindex::Flip(_) => "Flip", } } @@ -294,6 +322,7 @@ impl Operation for Reindex { Reindex::Permute(p) => p.compute_view(), Reindex::Slice(s) => s.compute_view(), Reindex::Broadcast(b) => b.compute_view(), + Reindex::Flip(f) => f.compute_view(), } } @@ -303,6 +332,7 @@ impl Operation for Reindex { Reindex::Permute(p) => p.srcs(), Reindex::Slice(s) => s.srcs(), Reindex::Broadcast(b) => b.srcs(), + Reindex::Flip(f) => f.srcs(), } } } diff --git a/crates/piston-core/src/tensor.rs b/crates/piston-core/src/tensor.rs index f0475748..9cac4fa0 100644 --- a/crates/piston-core/src/tensor.rs +++ b/crates/piston-core/src/tensor.rs @@ -1265,6 +1265,17 @@ pub fn permute(input: OpTensor, dims: D) -> Result { OpTensor::lazy(op, out_view, device, false) } +#[tensor_op(variants = [function, method])] +pub fn flip(input: OpTensor, dims: D) -> Result { + let dims = dims.to_indexes(input.shape(), "flip")?; + let device = input.device().clone(); + let flip = Flip::new(input, dims); + let out_view = flip.compute_view()?; + + let op = LazyOp::Reindex(Reindex::Flip(flip)); + OpTensor::lazy(op, out_view, device, false) +} + #[tensor_op(variants = [function, method, method_inplace])] pub fn transpose(input: OpTensor, dim0: D, dim1: D) -> Result { let dim0 = dim0.to_index(input.shape(), "transpose")?; diff --git a/crates/piston-web/src/tensor.rs b/crates/piston-web/src/tensor.rs index 04c50d67..478d8258 100644 --- a/crates/piston-web/src/tensor.rs +++ b/crates/piston-web/src/tensor.rs @@ -628,6 +628,9 @@ pub fn squeeze(input: Tensor, dim: Option) -> JsTensorResult { #[js_tensor_web_op(name = Permute, variants = [method, function])] pub fn permute(input: Tensor, dims: Dims) -> JsTensorResult {} +#[js_tensor_web_op(name = Flip, variants = [method, function])] +pub fn flip(input: Tensor, dims: Dims) -> JsTensorResult {} + #[js_tensor_web_op(name = Transpose, variants = [method, function])] pub fn transpose(input: Tensor, dim0: Dim, dim1: Dim) -> JsTensorResult {} From af140abbba58908522c8db08882022fea938860d Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 19:00:16 -0600 Subject: [PATCH 440/590] Merge rand and const fill shaders --- crates/piston-core/src/backprop.rs | 6 +- crates/piston-core/src/cpu/mod.rs | 3 +- crates/piston-core/src/op.rs | 19 +- crates/piston-core/src/ops/fill_constant.rs | 258 ------------ crates/piston-core/src/ops/fill_pointwise.rs | 415 +++++++++++++++++++ crates/piston-core/src/ops/fill_randn.rs | 287 ------------- crates/piston-core/src/ops/mod.rs | 6 +- crates/piston-core/src/tensor.rs | 81 ++-- 8 files changed, 479 insertions(+), 596 deletions(-) delete mode 100644 crates/piston-core/src/ops/fill_constant.rs create mode 100644 crates/piston-core/src/ops/fill_pointwise.rs delete mode 100644 crates/piston-core/src/ops/fill_randn.rs diff --git a/crates/piston-core/src/backprop.rs b/crates/piston-core/src/backprop.rs index 0fb4a450..b38f5642 100644 --- a/crates/piston-core/src/backprop.rs +++ b/crates/piston-core/src/backprop.rs @@ -319,9 +319,8 @@ impl Tensor { op: ReduceOp::ArgMax | ReduceOp::ArgMin, .. }) - | LazyOp::FillConstant(_) - | LazyOp::FillRandn(_) | LazyOp::Eye(_) + | LazyOp::FillPointwise(_) | LazyOp::Bernoulli(_) | LazyOp::Arange(_) | LazyOp::Cache(_) @@ -941,9 +940,8 @@ impl Tensor { op: ReduceOp::ArgMin, .. }) - | LazyOp::FillConstant(_) - | LazyOp::FillRandn(_) | LazyOp::Eye(_) + | LazyOp::FillPointwise(_) | LazyOp::Bernoulli(_) | LazyOp::Arange(_) | LazyOp::Unary(Unary { diff --git a/crates/piston-core/src/cpu/mod.rs b/crates/piston-core/src/cpu/mod.rs index 5f18aca5..c9392466 100644 --- a/crates/piston-core/src/cpu/mod.rs +++ b/crates/piston-core/src/cpu/mod.rs @@ -45,9 +45,8 @@ pub async fn apply_operation(op: LazyOp, dst: OpTensor) -> Result todo!(), LazyOp::Reduce(_r) => todo!(), LazyOp::Gather(_g) => todo!(), - LazyOp::FillConstant(_f) => todo!(), - LazyOp::FillRandn(_f) => todo!(), LazyOp::Eye(_e) => todo!(), + LazyOp::FillPointwise(_f) => todo!(), LazyOp::Bernoulli(_b) => todo!(), LazyOp::Arange(_a) => todo!(), LazyOp::IndexAdd(_i) => todo!(), diff --git a/crates/piston-core/src/op.rs b/crates/piston-core/src/op.rs index 4326dd4d..a20bc464 100644 --- a/crates/piston-core/src/op.rs +++ b/crates/piston-core/src/op.rs @@ -46,8 +46,7 @@ pub enum LazyOp { Ternary(Ternary), Lerp(Lerp), // ---- Everything below this line shouldn't exist ---- - FillConstant(FillConstant), - FillRandn(FillRandn), + FillPointwise(FillPointwise), Bernoulli(Bernoulli), RoPE(RoPE), Alibi(Alibi), @@ -90,9 +89,8 @@ impl LazyOp { LazyOp::IndexAdd(ia) => ia.name(), LazyOp::ScatterAdd(sa) => sa.name(), LazyOp::Trilu(t) => t.name(), - LazyOp::FillConstant(f) => f.name(), - LazyOp::FillRandn(f) => f.name(), LazyOp::Eye(e) => e.name(), + LazyOp::FillPointwise(f) => f.name(), LazyOp::Bernoulli(b) => b.name(), LazyOp::Arange(a) => a.name(), LazyOp::RoPE(r) => r.name(), @@ -138,7 +136,7 @@ impl LazyOp { LazyOp::View(v) => v.srcs(), LazyOp::Copy(c) => c.srcs(), LazyOp::Bernoulli(b) => b.srcs(), - LazyOp::FillConstant(_) | LazyOp::FillRandn(_) | LazyOp::Arange(_) | LazyOp::Const => { + LazyOp::FillPointwise(_) | LazyOp::Arange(_) | LazyOp::Const => { rvec![] } //end of the line kid } @@ -170,9 +168,8 @@ impl LazyOp { LazyOp::IndexAdd(ia) => ia.supports_inplace(), LazyOp::ScatterAdd(sa) => sa.supports_inplace(), LazyOp::Trilu(t) => t.supports_inplace(), - LazyOp::FillConstant(fc) => fc.supports_inplace(), - LazyOp::FillRandn(fr) => fr.supports_inplace(), LazyOp::Eye(e) => e.supports_inplace(), + LazyOp::FillPointwise(f) => f.supports_inplace(), LazyOp::Bernoulli(b) => b.supports_inplace(), LazyOp::Arange(a) => a.supports_inplace(), LazyOp::Cache(c) => c.supports_inplace(), @@ -193,7 +190,7 @@ impl LazyOp { pub fn can_be_parameter(&self) -> bool { matches!( self, - LazyOp::Const | LazyOp::FillConstant(_) | LazyOp::FillRandn(_) | LazyOp::Eye(_) | LazyOp::Arange(_) + LazyOp::Const | LazyOp::FillPointwise(_) | LazyOp::Eye(_) | LazyOp::Arange(_) ) } @@ -229,9 +226,8 @@ impl LazyOp { LazyOp::IndexAdd(ia) => ia.check_invariants(), LazyOp::ScatterAdd(sa) => sa.check_invariants(), LazyOp::Trilu(t) => t.check_invariants(), - LazyOp::FillConstant(f) => f.check_invariants(), - LazyOp::FillRandn(f) => f.check_invariants(), LazyOp::Eye(e) => e.check_invariants(), + LazyOp::FillPointwise(f) => f.check_invariants(), LazyOp::Bernoulli(b) => b.check_invariants(), LazyOp::Arange(a) => a.check_invariants(), LazyOp::Cache(c) => c.check_invariants(), @@ -268,9 +264,8 @@ impl LazyOp { LazyOp::IndexAdd(ia) => ia.ir(), LazyOp::ScatterAdd(sa) => sa.ir(), LazyOp::Trilu(t) => t.ir(), - LazyOp::FillConstant(f) => f.ir(), - LazyOp::FillRandn(f) => f.ir(), LazyOp::Eye(e) => e.ir(), + LazyOp::FillPointwise(f) => f.ir(), LazyOp::Bernoulli(b) => b.ir(), LazyOp::Arange(a) => a.ir(), LazyOp::Cache(c) => c.ir(), diff --git a/crates/piston-core/src/ops/fill_constant.rs b/crates/piston-core/src/ops/fill_constant.rs deleted file mode 100644 index 2001b899..00000000 --- a/crates/piston-core/src/ops/fill_constant.rs +++ /dev/null @@ -1,258 +0,0 @@ -use derive_new::new; -use half::f16; -use inline_wgsl::wgsl; -use piston_macros::IrFields; - -use crate::{ - Array, BindingMode, BuiltIn, DType, DynKernelMetadata, GPUOperation, Kernel, KernelElement, - KernelRenderable, KernelSource, OpGuards, OpTensor, Operation, OperationError, RVec, Scalar, - Shape, StorageView, Stride, Vec2, Vec4, WgslKernelBuilder, WgslPrimitive, WorkgroupSize, - Workload, - gpu::{BindGroupLayoutDescriptor, dtype::WgslDType}, - rvec, -}; - -#[derive(new, Debug, Clone, IrFields)] -pub struct FillConstant { - pub shape: Shape, - pub value: f32, -} - -impl Operation for FillConstant { - fn name(&self) -> &'static str { - "FillConstant" - } - fn compute_view(&self) -> Result { - let shape: Shape = self.shape.clone(); - let stride = Stride::from(&shape); - Ok(StorageView::new(shape, DType::F32, stride)) - } - - fn srcs(&self) -> RVec<&OpTensor> { - rvec![] - } - - fn supports_inplace(&self) -> bool { - false - } -} - -impl OpGuards for FillConstant { - fn check_shapes(&self) { - // No input shapes to check - } - - fn check_dtypes(&self) { - // No input dtypes to check - } -} - -pub enum FillConstantKernels { - Standard(FillConstant), -} - -impl GPUOperation for FillConstant { - type KernelEnum = FillConstantKernels; - - fn select_kernel(&self) -> Self::KernelEnum { - FillConstantKernels::Standard(self.clone()) - } -} - -impl KernelRenderable for FillConstantKernels { - fn register_bindings( - &self, - builder: &mut WgslKernelBuilder, - _: bool, - ) -> Result<(), OperationError> { - builder.register_storage("Y", BindingMode::ReadWrite, Array::

    ::default()); - builder.register_uniform(); - Ok(()) - } - - fn render( - &self, - _: bool, - dst: &OpTensor, - workgroup_size: &WorkgroupSize, - ) -> Result { - let device = dst.device().try_gpu()?; - let mut kernel_builder = WgslKernelBuilder::new( - workgroup_size.clone(), - rvec![ - BuiltIn::WorkgroupId, - BuiltIn::LocalInvocationIndex, - BuiltIn::NumWorkgroups - ], - device.compute_features().clone(), - ); - - self.register_bindings::

    (&mut kernel_builder, false)?; - kernel_builder.render_metadata(&self.metadata(dst, &self.kernel_element(dst))?); - - let N = (P::W as u32).render(); - let dtype = P::render_type(); - - kernel_builder.write_main(wgsl! { - let x_offset = workgroup_id.x * 64u; - let index = (workgroup_id.y * num_workgroups.x * 64u) + x_offset + local_invocation_index; - if (index >= metadata.numel / 'N) { - return; - } - Y[index] = 'dtype(metadata.value); - }); - - Ok(kernel_builder.build()?) - } -} - -impl Kernel for FillConstantKernels { - type Metadata = DynKernelMetadata; - - fn kernel_name(&self) -> String { - match self { - FillConstantKernels::Standard(_) => "fill_constant".to_string(), - } - } - - fn kernel_element(&self, dst: &OpTensor) -> KernelElement { - let rank = dst.shape().dim(); - let N = if rank > 0 { dst.shape()[rank - 1] } else { 1 }; - - if N.is_multiple_of(4) { - KernelElement::Vec4 - } else if N.is_multiple_of(2) { - KernelElement::Vec2 - } else { - KernelElement::Scalar - } - } - - fn calculate_dispatch(&self, dst: &OpTensor) -> Result { - Ok(Workload::std(dst.shape().numel(), self.kernel_element(dst))) - } - - fn storage_bind_group_layout( - &self, - _inplace: bool, - ) -> Result { - Ok(BindGroupLayoutDescriptor::unary_inplace()) - } - - fn metadata( - &self, - dst: &OpTensor, - _: &KernelElement, - ) -> Result { - let FillConstantKernels::Standard(op) = self; - let mut dyn_meta = DynKernelMetadata::new(); - dyn_meta.add_field("numel", dst.shape().numel() as u32); - if dst.dtype().is_float() { - dyn_meta.add_field("value", op.value); - } else { - dyn_meta.add_field("value", op.value as i32); - } - Ok(dyn_meta) - } - - fn build_kernel( - &self, - inplace: bool, - dst: &OpTensor, - workgroup_size: &WorkgroupSize, - ) -> Result { - let kernel_element = self.kernel_element(dst); - match (dst.dtype(), &kernel_element) { - (DType::F32, KernelElement::Scalar) => { - self.render::>(inplace, dst, workgroup_size) - } - (DType::F32, KernelElement::Vec2) => { - self.render::>(inplace, dst, workgroup_size) - } - (DType::F32, KernelElement::Vec4) => { - self.render::>(inplace, dst, workgroup_size) - } - (DType::F16, KernelElement::Scalar) => { - self.render::>(inplace, dst, workgroup_size) - } - (DType::F16, KernelElement::Vec2) => { - self.render::>(inplace, dst, workgroup_size) - } - (DType::F16, KernelElement::Vec4) => { - self.render::>(inplace, dst, workgroup_size) - } - (DType::I32, KernelElement::Scalar) => { - self.render::>(inplace, dst, workgroup_size) - } - (DType::I32, KernelElement::Vec2) => { - self.render::>(inplace, dst, workgroup_size) - } - (DType::I32, KernelElement::Vec4) => { - self.render::>(inplace, dst, workgroup_size) - } - _ => Err(OperationError::CompileError(format!( - "Unsupported dtype {:?} or kernel element {:?}", - dst.dtype(), - kernel_element - ))), - } - } -} - -#[cfg(all(test, feature = "pyo3"))] -mod tests { - use test_strategy::{Arbitrary, proptest}; - - use crate::{DType, Device, DeviceRequest, Tensor, TensorOptions, test_util::run_py_prg}; - - fn ground_truth(shape: &[usize], value: f32) -> anyhow::Result { - let prg = r#" -import torch -def fill_constant(shape, value): - return torch.full(shape, value, dtype=torch.float32).cpu().numpy() -"#; - - run_py_prg(prg.to_string(), &[], &[&shape, &value], DType::F32) - } - - fn run_fill_constant_trial(problem: FillConstantProblem, device: Device) { - let FillConstantProblem { B, M, N, value } = problem; - - let a = Tensor::full( - (B, M, N), - value, - TensorOptions::new().device(device.clone()), - ) - .unwrap(); - let ground = ground_truth(&[B, M, N], value).unwrap(); - - let a_gpu = a.to(&device).unwrap(); - - let ours = a_gpu.to(&Device::CPU).unwrap(); - - println!("ours = {ours:?}"); - println!("ground = {ground:?}"); - - // Compare our result with ground truth - ground.all_close(&ours, 1e-6, 1e-6).unwrap(); - } - - #[derive(Arbitrary, Debug)] - struct FillConstantProblem { - #[strategy(1..=128usize)] - B: usize, - #[strategy(1..=128usize)] - M: usize, - #[strategy(1..=128usize)] - N: usize, - value: f32, - } - - #[proptest(cases = 8)] - fn test_fill_constant(prob: FillConstantProblem) { - let FillConstantProblem { B, M, N, value } = prob; - println!("B = {B}, M = {M}, N = {N}, value = {value}"); - let device = Device::request_device(DeviceRequest::GPU).unwrap(); - run_fill_constant_trial(prob, device); - } -} diff --git a/crates/piston-core/src/ops/fill_pointwise.rs b/crates/piston-core/src/ops/fill_pointwise.rs new file mode 100644 index 00000000..19d06cbb --- /dev/null +++ b/crates/piston-core/src/ops/fill_pointwise.rs @@ -0,0 +1,415 @@ +use derive_new::new; +use inline_wgsl::wgsl; + +use crate::{ + Array, BindingMode, BuiltIn, DType, DynKernelMetadata, GPUOperation, Ir, IrFields, Kernel, + KernelElement, KernelRenderable, KernelSource, OpGuards, OpTensor, Operation, OperationError, + RVec, Scalar, Shape, StorageView, Stride, Vec2, Vec4, WgslKernelBuilder, WgslPrimitive, + WorkgroupSize, Workload, + gpu::{BindGroupLayoutDescriptor, dtype::WgslDType}, + rvec, +}; + +#[derive(Debug, Clone)] +pub enum FillPointwiseKind { + Constant { + value: f32, + }, + Randn { + mean: f32, + std: f32, + seed: Option, + }, + Rand { + lo: f32, + up: f32, + seed: Option, + }, +} + +#[derive(new, Debug, Clone)] +pub struct FillPointwise { + pub shape: Shape, + pub kind: FillPointwiseKind, +} + +impl FillPointwise { + pub fn kernel_name(&self) -> &'static str { + match self.kind { + FillPointwiseKind::Constant { .. } => "fill_constant", + FillPointwiseKind::Randn { .. } => "fill_randn", + FillPointwiseKind::Rand { .. } => "fill_rand", + } + } +} + +impl Operation for FillPointwise { + fn name(&self) -> &'static str { + "FillPointwise" + } + + fn compute_view(&self) -> Result { + // The dtype is determined by the OpTensor meta at construction time. + // Returning F32 here mirrors existing fill ops and is unused in GPU path. + let shape: Shape = self.shape.clone(); + let stride = Stride::from(&shape); + Ok(StorageView::new(shape, DType::F32, stride)) + } + + fn srcs(&self) -> RVec<&OpTensor> { + rvec![] + } + + fn supports_inplace(&self) -> bool { + false + } +} + +impl OpGuards for FillPointwise { + fn check_shapes(&self) {} + fn check_dtypes(&self) {} +} + +pub enum FillPointwiseKernels { + Standard(FillPointwise), +} + +impl GPUOperation for FillPointwise { + type KernelEnum = FillPointwiseKernels; + + fn select_kernel(&self) -> Self::KernelEnum { + FillPointwiseKernels::Standard(self.clone()) + } +} + +impl KernelRenderable for FillPointwiseKernels { + fn register_bindings( + &self, + builder: &mut WgslKernelBuilder, + _: bool, + ) -> Result<(), OperationError> { + builder.register_storage("Y", BindingMode::ReadWrite, Array::

    ::default()); + builder.register_uniform(); + Ok(()) + } + + fn render( + &self, + _: bool, + dst: &OpTensor, + workgroup_size: &WorkgroupSize, + ) -> Result { + let device = dst.device().try_gpu()?; + let mut kernel_builder = WgslKernelBuilder::new( + workgroup_size.clone(), + rvec![ + BuiltIn::WorkgroupId, + BuiltIn::LocalInvocationIndex, + BuiltIn::NumWorkgroups + ], + device.compute_features().clone(), + ); + + self.register_bindings::

    (&mut kernel_builder, false)?; + kernel_builder.render_metadata(&self.metadata(dst, &self.kernel_element(dst))?); + + let N = (P::W as u32).render(); + let dtype = P::render_type(); + + // Random helpers used by Randn/Xavier variants + let needs_rand = match self { + FillPointwiseKernels::Standard(inner) => matches!( + inner.kind, + FillPointwiseKind::Randn { .. } | FillPointwiseKind::Rand { .. } + ), + }; + + if needs_rand { + kernel_builder.write_global(wgsl! { + fn pcg_hash(input: u32) -> u32 { + let state = input * 747796405u + 2891336453u; + let word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u; + return (word >> 22u) ^ word; + } + + fn rand(seed: u32) -> f32 { + return f32(pcg_hash(seed)) / 4294967295.0; + } + + fn box_muller_1d(u1: f32, u2: f32) -> f32 { + let r = sqrt(-2.0 * log(u1)); + let theta = 2.0 * 3.14159265359 * u2; + return r * cos(theta); + } + }); + } + + // Common indexing prelude. Use N for vector width (1 for scalar types). + kernel_builder.write_main(wgsl! { + let x_offset = workgroup_id.x * 64u; + let index = (workgroup_id.y * num_workgroups.x * 64u) + x_offset + local_invocation_index; + if (index >= metadata.numel / 'N) { + return; + } + }); + + match self { + FillPointwiseKernels::Standard(inner) => match inner.kind { + FillPointwiseKind::Constant { .. } => { + kernel_builder.write_main(wgsl! { + Y[index] = 'dtype(metadata.value); + }); + } + FillPointwiseKind::Randn { .. } => { + kernel_builder.write_main(wgsl! { + let seed1 = index; + let seed2 = index ^ 2747636419u; + let u1 = rand(seed1 + metadata.seed); + let u2 = rand(seed2 + metadata.seed); + let normal = box_muller_1d(u1, u2); + Y[index] = 'dtype(f32(normal) * metadata.stddev + metadata.mean); + }); + } + FillPointwiseKind::Rand { .. } => { + kernel_builder.write_main(wgsl! { + let u = rand(index + metadata.seed); + Y[index] = 'dtype(metadata.lo + (metadata.hi - metadata.lo) * u); + }); + } + }, + } + + Ok(kernel_builder.build()?) + } +} + +impl Kernel for FillPointwiseKernels { + type Metadata = DynKernelMetadata; + + fn kernel_name(&self) -> String { + match self { + FillPointwiseKernels::Standard(inner) => inner.kernel_name().to_string(), + } + } + + fn kernel_element(&self, dst: &OpTensor) -> KernelElement { + match self { + FillPointwiseKernels::Standard(inner) => match inner.kind { + FillPointwiseKind::Constant { .. } => { + // Vectorize constant fills like the original FillConstant + let rank = dst.shape().dim(); + let N = if rank > 0 { dst.shape()[rank - 1] } else { 1 }; + if N.is_multiple_of(4) { + KernelElement::Vec4 + } else if N.is_multiple_of(2) { + KernelElement::Vec2 + } else { + KernelElement::Scalar + } + } + // Randomized variants: keep scalar for simplicity/correctness + _ => KernelElement::Scalar, + }, + } + } + + fn calculate_dispatch(&self, dst: &OpTensor) -> Result { + Ok(Workload::std(dst.shape().numel(), self.kernel_element(dst))) + } + + fn storage_bind_group_layout( + &self, + _inplace: bool, + ) -> Result { + Ok(BindGroupLayoutDescriptor::unary_inplace()) + } + + fn metadata( + &self, + dst: &OpTensor, + _: &KernelElement, + ) -> Result { + let mut dyn_meta = DynKernelMetadata::new(); + dyn_meta.add_field("numel", dst.shape().numel() as u32); + + match self { + FillPointwiseKernels::Standard(inner) => match inner.kind { + FillPointwiseKind::Constant { value } => { + if dst.dtype().is_float() { + dyn_meta.add_field("value", value); + } else { + dyn_meta.add_field("value", value as i32); + } + } + FillPointwiseKind::Randn { mean, std, seed } => { + dyn_meta.add_field("mean", mean); + dyn_meta.add_field("stddev", std); + dyn_meta.add_field("seed", seed.unwrap_or(0)); + } + FillPointwiseKind::Rand { lo, up, seed } => { + dyn_meta.add_field("lo", lo); + dyn_meta.add_field("hi", up); + dyn_meta.add_field("seed", seed.unwrap_or(0)); + } + }, + } + Ok(dyn_meta) + } + + fn build_kernel( + &self, + inplace: bool, + dst: &OpTensor, + workgroup_size: &WorkgroupSize, + ) -> Result { + let kernel_element = self.kernel_element(dst); + match (dst.dtype(), &kernel_element) { + // Floating types: all variants supported (random & constant). Random variants are Scalar-only by design. + (DType::F32, KernelElement::Scalar) => { + self.render::>(inplace, dst, workgroup_size) + } + (DType::F16, KernelElement::Scalar) => { + self.render::>(inplace, dst, workgroup_size) + } + + // Vectorized constant fills for performance + (DType::F32, KernelElement::Vec2) => { + self.render::>(inplace, dst, workgroup_size) + } + (DType::F32, KernelElement::Vec4) => { + self.render::>(inplace, dst, workgroup_size) + } + (DType::F16, KernelElement::Vec2) => { + self.render::>(inplace, dst, workgroup_size) + } + (DType::F16, KernelElement::Vec4) => { + self.render::>(inplace, dst, workgroup_size) + } + + // Integer dtype only meaningful for constant fills (random/Xavier require floats). KernelElement for random is Scalar. + (DType::I32, KernelElement::Scalar) => { + self.render::>(inplace, dst, workgroup_size) + } + (DType::I32, KernelElement::Vec2) => { + self.render::>(inplace, dst, workgroup_size) + } + (DType::I32, KernelElement::Vec4) => { + self.render::>(inplace, dst, workgroup_size) + } + + _ => Err(OperationError::CompileError(format!( + "Unsupported dtype {:?} or kernel element {:?}", + dst.dtype(), + kernel_element + ))), + } + } +} + +impl IrFields for FillPointwise { + fn ir_fields(&self, ir: &mut Ir) { + ir.with_field("shape", self.shape.clone()); + match &self.kind { + FillPointwiseKind::Constant { value } => { + ir.with_field("kind", "Constant"); + ir.with_field("value", *value); + } + FillPointwiseKind::Randn { mean, std, seed } => { + ir.with_field("kind", "Randn"); + ir.with_field("mean", *mean); + ir.with_field("stddev", *std); + ir.with_field("seed", seed.unwrap_or(0)); + } + FillPointwiseKind::Rand { lo, up, seed } => { + ir.with_field("kind", "Rand"); + ir.with_field("lo", *lo); + ir.with_field("hi", *up); + ir.with_field("seed", seed.unwrap_or(0)); + } + } + } +} + +#[cfg(all(test, feature = "pyo3"))] +mod tests { + use test_strategy::{Arbitrary, proptest}; + + use crate::{ + DType, Device, DeviceRequest, Tensor, TensorOptions, randn, test_util::run_py_prg, + }; + + #[derive(Arbitrary, Debug)] + struct FillPointwiseProblem { + #[strategy(1..=64usize)] + B: usize, + #[strategy(1..=128usize)] + M: usize, + #[strategy(1..=128usize)] + N: usize, + } + + fn normal_parameters(output: &Tensor) -> anyhow::Result<(f32, f32)> { + let prg = r#" +import numpy as np + +def check_normal(output): + output_np = np.array(output) + mean = float(np.mean(output_np)) + std = float(np.std(output_np)) + return np.array([mean, std], dtype=np.float32) +"#; + + let params = run_py_prg(prg.to_string(), &[output], &[], DType::F32)? + .to(&Device::CPU) + .unwrap() + .to_vec::() + .unwrap(); + Ok((params[0], params[1])) + } + + #[proptest(cases = 16)] + fn test_fill_pointwise_constant(prob: FillPointwiseProblem) { + let FillPointwiseProblem { B, M, N } = prob; + let device = Device::request_device(DeviceRequest::GPU).unwrap(); + let value: f32 = 0.5; + + let ours = Tensor::full( + (B, M, N), + value, + TensorOptions::new().device(device.clone()), + ) + .unwrap() + .to(&Device::CPU) + .unwrap(); + + // Ground truth via torch.full + let prg = r#" +import torch +def fill_constant(shape, value): + return torch.full(shape, value, dtype=torch.float32).cpu().numpy() +"#; + let ground = run_py_prg( + prg.to_string(), + &[], + &[&vec![B as i64, M as i64, N as i64], &value], + DType::F32, + ) + .unwrap(); + + ground.all_close(&ours, 1e-6, 1e-6).unwrap(); + } + + #[proptest(cases = 16)] + fn test_fill_pointwise_randn(prob: FillPointwiseProblem) { + let FillPointwiseProblem { B, M, N } = prob; + let device = Device::request_device(DeviceRequest::GPU).unwrap(); + let a = randn((B, M, N), None, None, TensorOptions::new().device(device)) + .unwrap() + .to(&Device::CPU) + .unwrap(); + + let (mean, std) = normal_parameters(&a).unwrap(); + assert!((mean - 0.0).abs() < 0.1, "mean={mean}"); + assert!((std - 1.0).abs() < 0.1, "std={std}"); + } +} diff --git a/crates/piston-core/src/ops/fill_randn.rs b/crates/piston-core/src/ops/fill_randn.rs deleted file mode 100644 index 268d7b6c..00000000 --- a/crates/piston-core/src/ops/fill_randn.rs +++ /dev/null @@ -1,287 +0,0 @@ -use derive_new::new; -use encase::ShaderType; -use half::f16; -use inline_wgsl::wgsl; -use piston_macros::{IrFields, WgslMetadata}; - -use crate::{ - Array, BindingMode, BuiltIn, DType, GPUOperation, Kernel, KernelElement, KernelRenderable, - KernelSource, OpGuards, OpTensor, Operation, OperationError, RVec, Scalar, Shape, StorageView, - Stride, Vec2, Vec4, WgslKernelBuilder, WgslPrimitive, WorkgroupSize, Workload, - gpu::BindGroupLayoutDescriptor, rvec, -}; - -#[derive(new, Debug, Clone, IrFields)] -pub struct FillRandn { - pub shape: Shape, - pub mean: f32, - pub std: f32, - pub seed: Option, -} - -#[derive(Debug, derive_new::new, ShaderType, WgslMetadata)] -pub struct FillRandnMeta { - numel: u32, - mean: f32, - stddev: f32, - seed: u32, -} - -impl Operation for FillRandn { - fn name(&self) -> &'static str { - "FillRandn" - } - - fn compute_view(&self) -> Result { - let shape: Shape = self.shape.clone(); - let stride = Stride::from(&shape); - Ok(StorageView::new(shape, crate::DType::F32, stride)) - } - - fn srcs(&self) -> RVec<&OpTensor> { - rvec![] - } - - fn supports_inplace(&self) -> bool { - false - } -} - -impl OpGuards for FillRandn { - fn check_shapes(&self) { - // No input shapes to check - } - - fn check_dtypes(&self) { - // No input dtypes to check - } -} - -pub enum FillRandnKernels { - Standard(FillRandn), -} - -impl GPUOperation for FillRandn { - type KernelEnum = FillRandnKernels; - - fn select_kernel(&self) -> Self::KernelEnum { - FillRandnKernels::Standard(self.clone()) - } -} - -impl KernelRenderable for FillRandnKernels { - fn register_bindings( - &self, - builder: &mut WgslKernelBuilder, - _: bool, - ) -> Result<(), OperationError> { - builder.register_storage("Y", BindingMode::ReadWrite, Array::

    ::default()); - builder.register_uniform(); - Ok(()) - } - - fn render( - &self, - _: bool, - dst: &OpTensor, - workgroup_size: &WorkgroupSize, - ) -> Result { - let device = dst.device().try_gpu()?; - let mut kernel_builder = WgslKernelBuilder::new( - workgroup_size.clone(), - rvec![ - BuiltIn::WorkgroupId, - BuiltIn::LocalInvocationIndex, - BuiltIn::NumWorkgroups - ], - device.compute_features().clone(), - ); - - self.register_bindings::

    (&mut kernel_builder, false)?; - kernel_builder.render_metadata(&self.metadata(dst, &self.kernel_element(dst))?); - - kernel_builder.write_global(wgsl! { - fn pcg_hash(input: u32) -> u32 { - let state = input * 747796405u + 2891336453u; - let word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u; - return (word >> 22u) ^ word; - } - - fn rand(seed: u32) -> f32 { - return f32(pcg_hash(seed)) / 4294967295.0; - } - - fn box_muller_1d(u1: f32, u2: f32) -> f32 { - let r = sqrt(-2.0 * log(u1)); - let theta = 2.0 * 3.14159265359 * u2; - return r * cos(theta); - } - }); - - kernel_builder.write_main(wgsl! { - let x_offset = workgroup_id.x * 64u; - let index = (workgroup_id.y * num_workgroups.x * 64u) + x_offset + local_invocation_index; - if (index >= metadata.numel) { - return; - } - - let seed1 = index; - let seed2 = index ^ 2747636419u; // XOR with a prime for a different seed - - let u1 = rand(seed1 + metadata.seed); - let u2 = rand(seed2 + metadata.seed); - - let normal = box_muller_1d(u1, u2); - - Y[index] = f32(normal) * metadata.stddev + metadata.mean; - }); - - Ok(kernel_builder.build()?) - } -} - -impl Kernel for FillRandnKernels { - type Metadata = FillRandnMeta; - - fn kernel_name(&self) -> String { - match self { - FillRandnKernels::Standard(_) => "fill_randn".to_string(), - } - } - - fn kernel_element(&self, _dst: &OpTensor) -> KernelElement { - KernelElement::Scalar - } - - fn calculate_dispatch(&self, dst: &OpTensor) -> Result { - Ok(Workload::std(dst.shape().numel(), self.kernel_element(dst))) - } - - fn storage_bind_group_layout( - &self, - _inplace: bool, - ) -> Result { - Ok(BindGroupLayoutDescriptor::unary_inplace()) - } - - fn metadata(&self, _: &OpTensor, _: &KernelElement) -> Result { - let FillRandnKernels::Standard(inner) = self; - Ok(FillRandnMeta { - numel: inner.shape.clone().numel() as u32, - mean: inner.mean, - stddev: inner.std, - seed: inner.seed.unwrap_or(0), - }) - } - - fn build_kernel( - &self, - inplace: bool, - dst: &OpTensor, - workgroup_size: &WorkgroupSize, - ) -> Result { - let kernel_element = self.kernel_element(dst); - match (dst.dtype(), &kernel_element) { - (DType::F32, KernelElement::Scalar) => { - self.render::>(inplace, dst, workgroup_size) - } - (DType::F32, KernelElement::Vec2) => { - self.render::>(inplace, dst, workgroup_size) - } - (DType::F32, KernelElement::Vec4) => { - self.render::>(inplace, dst, workgroup_size) - } - (DType::F16, KernelElement::Scalar) => { - self.render::>(inplace, dst, workgroup_size) - } - (DType::F16, KernelElement::Vec2) => { - self.render::>(inplace, dst, workgroup_size) - } - (DType::F16, KernelElement::Vec4) => { - self.render::>(inplace, dst, workgroup_size) - } - _ => Err(OperationError::CompileError(format!( - "Unsupported dtype {:?} or kernel element {:?}", - dst.dtype(), - kernel_element - ))), - } - } -} - -#[cfg(all(test, feature = "pyo3"))] -mod tests { - use test_strategy::{Arbitrary, proptest}; - - use crate::{DType, Device, DeviceRequest, Tensor, randn, test_util::run_py_prg}; - - fn normal_parameters(output: &Tensor) -> anyhow::Result { - let prg = r#" -import numpy as np - -def check_normal(output): - output_np = np.array(output) - mean = np.mean(output_np) - std = np.std(output_np) - return np.array([mean, std], dtype=np.float32) -"#; - - run_py_prg(prg.to_string(), &[output], &[], DType::F32) - } - - fn run_fill_randn_trial(problem: FillRandnProblem, device: Device) { - let FillRandnProblem { B, M, N } = problem; - - let a = randn((B, M, N), None, None, Default::default()) - .unwrap() - .to(&Device::CPU) - .unwrap(); - - let params = normal_parameters(&a) - .unwrap() - .to(&device) - .unwrap() - .to(&Device::CPU) - .unwrap() - .to_vec::() - .unwrap(); - - let mean = params[0]; - let std = params[1]; - - // Check if the distribution is approximately normal - // We use a tolerance of 0.1 for both mean and standard deviation - if (mean - 0.0).abs() < 0.1 && (std - 1.0).abs() < 0.1 { - println!("\x1b[1;32mDistribution approximately normal\x1b[0m - mean={mean} std={std}"); - } else { - (|| -> anyhow::Result<()> { - { - anyhow::bail!( - "\x1b[1;31mDistribution not normal\x1b[0m - mean={} std={}", - mean, - std - ) - } - })() - .unwrap(); - } - } - - #[derive(Arbitrary, Debug)] - struct FillRandnProblem { - #[strategy(1..=128usize)] - B: usize, - #[strategy(1..=128usize)] - M: usize, - #[strategy(1..=128usize)] - N: usize, - } - - #[proptest(cases = 8)] - fn test_fill_randn(prob: FillRandnProblem) { - let FillRandnProblem { B, M, N } = prob; - println!("B = {B}, M = {M}, N = {N}"); - let device = Device::request_device(DeviceRequest::GPU).unwrap(); - run_fill_randn_trial(prob, device); - } -} diff --git a/crates/piston-core/src/ops/mod.rs b/crates/piston-core/src/ops/mod.rs index 676d2e12..5e810474 100644 --- a/crates/piston-core/src/ops/mod.rs +++ b/crates/piston-core/src/ops/mod.rs @@ -8,9 +8,8 @@ mod cast; mod cmp; mod concat; mod conv; -mod fill_constant; -mod fill_randn; mod eye; +mod fill_pointwise; mod gather; mod index_add; mod index_write; @@ -42,9 +41,8 @@ pub use cast::*; pub use cmp::*; pub use concat::*; pub use conv::*; -pub use fill_constant::*; -pub use fill_randn::*; pub use eye::*; +pub use fill_pointwise::*; pub use gather::*; pub use index_add::*; pub use index_write::*; diff --git a/crates/piston-core/src/tensor.rs b/crates/piston-core/src/tensor.rs index 9cac4fa0..80c8feab 100644 --- a/crates/piston-core/src/tensor.rs +++ b/crates/piston-core/src/tensor.rs @@ -151,9 +151,9 @@ impl OpTensor { stride: Stride::from(&shape.clone()), }; Self::new( - LazyOp::FillConstant(FillConstant { + LazyOp::FillPointwise(FillPointwise { + kind: FillPointwiseKind::Constant { value: value.as_() }, shape: shape.clone(), - value: value.as_(), }), meta, None, @@ -1832,11 +1832,13 @@ fn randn_impl>( stride: Stride::from(shape), }; OpTensor::new( - LazyOp::FillRandn(FillRandn { + LazyOp::FillPointwise(FillPointwise { shape: shape.clone(), - mean: mean.as_(), - std: std.as_(), - seed: Some(rng.write().next_u32()), + kind: FillPointwiseKind::Randn { + mean: mean.as_(), + std: std.as_(), + seed: Some(rng.write().next_u32()), + }, }), meta, None, @@ -1897,25 +1899,48 @@ fn rand_impl>( requires_grad: bool, ) -> Result { let rng = device.get_rng(); - let distr = Uniform::new(lo.as_(), up.as_()); - let data = (0..shape.numel()) - .map(|_| { - let sample: f32 = distr.sample(&mut *rng.write()); - T::from(sample).expect("Failed to convert sample") - }) - .collect::>(); - let stride = Stride::from(shape); - let meta = StorageView::new(shape.clone(), T::dtype(), stride); - let storage = Storage::from_slice(&data, shape, device); + if device.is_cpu() { + let distr = Uniform::new(lo.as_(), up.as_()); + let data = (0..shape.numel()) + .map(|_| { + let sample: f32 = distr.sample(&mut *rng.write()); + T::from(sample).expect("Failed to convert sample") + }) + .collect::>(); - OpTensor::new( - LazyOp::Const, - meta, - Some(storage), - device.clone(), - requires_grad, - ) + let stride = Stride::from(shape); + let meta = StorageView::new(shape.clone(), T::dtype(), stride); + let storage = Storage::from_slice(&data, shape, device); + + OpTensor::new( + LazyOp::Const, + meta, + Some(storage), + device.clone(), + requires_grad, + ) + } else { + let meta = StorageView { + shape: shape.clone(), + dtype: T::dtype(), + stride: Stride::from(shape), + }; + OpTensor::new( + LazyOp::FillPointwise(FillPointwise { + shape: shape.clone(), + kind: FillPointwiseKind::Rand { + lo: lo.as_(), + up: up.as_(), + seed: Some(rng.write().next_u32()), + }, + }), + meta, + None, + device.clone(), + requires_grad, + ) + } } #[cfg(feature = "rand")] @@ -2030,9 +2055,9 @@ fn full_impl>( stride: Stride::from(shape), }; OpTensor::new( - LazyOp::FillConstant(FillConstant { + LazyOp::FillPointwise(FillPointwise { shape: shape.clone(), - value: value.as_(), + kind: FillPointwiseKind::Constant { value: value.as_() }, }), meta, None, @@ -2536,8 +2561,7 @@ impl OpTensor { LazyOp::Reduce(s) => s.create_gpu_compile_key(self, can_inplace, uniform).ok(), LazyOp::Detach(d) => self.gpu_compile_key_for_op(d, can_inplace, uniform), LazyOp::Gather(g) => g.create_gpu_compile_key(self, can_inplace, uniform).ok(), - LazyOp::FillConstant(f) => f.create_gpu_compile_key(self, can_inplace, uniform).ok(), - LazyOp::FillRandn(f) => f.create_gpu_compile_key(self, can_inplace, uniform).ok(), + LazyOp::FillPointwise(f) => f.create_gpu_compile_key(self, can_inplace, uniform).ok(), LazyOp::Bernoulli(b) => b.create_gpu_compile_key(self, can_inplace, uniform).ok(), LazyOp::Arange(a) => a.create_gpu_compile_key(self, can_inplace, uniform).ok(), LazyOp::Eye(e) => e.create_gpu_compile_key(self, can_inplace, uniform).ok(), @@ -2840,8 +2864,7 @@ pub fn compile_gpu_for_op( LazyOp::Reduce(s) => s.compile_gpu(gpu_compile_key, gpu_device, debug).ok(), LazyOp::Detach(d) => compile_gpu_for_op(d, gpu_compile_key, gpu_device, debug), LazyOp::Gather(g) => g.compile_gpu(gpu_compile_key, gpu_device, debug).ok(), - LazyOp::FillConstant(f) => f.compile_gpu(gpu_compile_key, gpu_device, debug).ok(), - LazyOp::FillRandn(f) => f.compile_gpu(gpu_compile_key, gpu_device, debug).ok(), + LazyOp::FillPointwise(f) => f.compile_gpu(gpu_compile_key, gpu_device, debug).ok(), LazyOp::Eye(e) => e.compile_gpu(gpu_compile_key, gpu_device, debug).ok(), LazyOp::Bernoulli(b) => b.compile_gpu(gpu_compile_key, gpu_device, debug).ok(), LazyOp::Arange(a) => a.compile_gpu(gpu_compile_key, gpu_device, debug).ok(), From 7ad19a39067258885d9d3e932213c31c2d0452a8 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 19:04:10 -0600 Subject: [PATCH 441/590] Add multinomial op --- crates/piston-core/src/backprop.rs | 2 + crates/piston-core/src/cpu/mod.rs | 1 + crates/piston-core/src/op.rs | 6 + crates/piston-core/src/ops/mod.rs | 2 + crates/piston-core/src/ops/multinomial.rs | 329 ++++++++++++++++++++++ crates/piston-core/src/tensor.rs | 16 ++ crates/piston-web/src/tensor.rs | 8 + 7 files changed, 364 insertions(+) create mode 100644 crates/piston-core/src/ops/multinomial.rs diff --git a/crates/piston-core/src/backprop.rs b/crates/piston-core/src/backprop.rs index b38f5642..f6024794 100644 --- a/crates/piston-core/src/backprop.rs +++ b/crates/piston-core/src/backprop.rs @@ -322,6 +322,7 @@ impl Tensor { | LazyOp::Eye(_) | LazyOp::FillPointwise(_) | LazyOp::Bernoulli(_) + | LazyOp::Multinomial(_) | LazyOp::Arange(_) | LazyOp::Cache(_) | LazyOp::Trilu(_) => nodes, @@ -943,6 +944,7 @@ impl Tensor { | LazyOp::Eye(_) | LazyOp::FillPointwise(_) | LazyOp::Bernoulli(_) + | LazyOp::Multinomial(_) | LazyOp::Arange(_) | LazyOp::Unary(Unary { op: UnaryOp::IsInf | UnaryOp::IsNan, diff --git a/crates/piston-core/src/cpu/mod.rs b/crates/piston-core/src/cpu/mod.rs index c9392466..7f47c1b7 100644 --- a/crates/piston-core/src/cpu/mod.rs +++ b/crates/piston-core/src/cpu/mod.rs @@ -48,6 +48,7 @@ pub async fn apply_operation(op: LazyOp, dst: OpTensor) -> Result todo!(), LazyOp::FillPointwise(_f) => todo!(), LazyOp::Bernoulli(_b) => todo!(), + LazyOp::Multinomial(_m) => todo!(), LazyOp::Arange(_a) => todo!(), LazyOp::IndexAdd(_i) => todo!(), LazyOp::ScatterAdd(_s) => todo!(), diff --git a/crates/piston-core/src/op.rs b/crates/piston-core/src/op.rs index a20bc464..6d5a639e 100644 --- a/crates/piston-core/src/op.rs +++ b/crates/piston-core/src/op.rs @@ -43,6 +43,7 @@ pub enum LazyOp { WhereCond(WhereCond), Reduce(Reduce), Gather(Gather), + Multinomial(Multinomial), Ternary(Ternary), Lerp(Lerp), // ---- Everything below this line shouldn't exist ---- @@ -81,6 +82,7 @@ impl LazyOp { LazyOp::WhereCond(w) => w.name(), LazyOp::Reduce(s) => s.name(), LazyOp::Gather(g) => g.name(), + LazyOp::Multinomial(m) => m.name(), LazyOp::Conv(c) => c.name(), LazyOp::Ternary(t) => t.name(), LazyOp::Lerp(l) => l.name(), @@ -122,6 +124,7 @@ impl LazyOp { LazyOp::WhereCond(w) => w.srcs(), LazyOp::Reduce(s) => s.srcs(), LazyOp::Gather(g) => g.srcs(), + LazyOp::Multinomial(m) => m.srcs(), LazyOp::Conv(c) => c.srcs(), LazyOp::Ternary(t) => t.srcs(), LazyOp::Lerp(l) => l.srcs(), @@ -160,6 +163,7 @@ impl LazyOp { LazyOp::WhereCond(w) => w.supports_inplace(), LazyOp::Reduce(s) => s.supports_inplace(), LazyOp::Gather(g) => g.supports_inplace(), + LazyOp::Multinomial(m) => m.supports_inplace(), LazyOp::Conv(c) => c.supports_inplace(), LazyOp::Ternary(t) => t.supports_inplace(), LazyOp::Lerp(l) => l.supports_inplace(), @@ -218,6 +222,7 @@ impl LazyOp { LazyOp::WhereCond(w) => w.check_invariants(), LazyOp::Reduce(s) => s.check_invariants(), LazyOp::Gather(g) => g.check_invariants(), + LazyOp::Multinomial(m) => m.check_invariants(), LazyOp::Conv(c) => c.check_invariants(), LazyOp::Ternary(t) => t.check_invariants(), LazyOp::Lerp(l) => l.check_invariants(), @@ -256,6 +261,7 @@ impl LazyOp { LazyOp::WhereCond(w) => w.ir(), LazyOp::Reduce(s) => s.ir(), LazyOp::Gather(g) => g.ir(), + LazyOp::Multinomial(m) => m.ir(), LazyOp::Conv(c) => c.ir(), LazyOp::Ternary(t) => t.ir(), LazyOp::Lerp(l) => l.ir(), diff --git a/crates/piston-core/src/ops/mod.rs b/crates/piston-core/src/ops/mod.rs index 5e810474..cdaa12dc 100644 --- a/crates/piston-core/src/ops/mod.rs +++ b/crates/piston-core/src/ops/mod.rs @@ -15,6 +15,7 @@ mod index_add; mod index_write; mod lerp; mod matmul; +mod multinomial; mod norm; mod powf; mod reduce; @@ -48,6 +49,7 @@ pub use index_add::*; pub use index_write::*; pub use lerp::*; pub use matmul::*; +pub use multinomial::*; pub use norm::*; use piston_macros::IrFields; pub use powf::*; diff --git a/crates/piston-core/src/ops/multinomial.rs b/crates/piston-core/src/ops/multinomial.rs new file mode 100644 index 00000000..18f4ac21 --- /dev/null +++ b/crates/piston-core/src/ops/multinomial.rs @@ -0,0 +1,329 @@ +use derive_new::new; +use encase::ShaderType; +use half::f16; +use inline_wgsl::wgsl; +use piston_macros::{IrFields, WgslMetadata}; + +use crate::{ + Array, BindingMode, BuiltIn, DType, GPUOperation, Kernel, KernelElement, KernelRenderable, + KernelSource, OpGuards, OpTensor, Operation, OperationError, RVec, Scalar, StorageView, Stride, + Vec2, Vec4, WgslKernelBuilder, WgslPrimitive, WorkgroupSize, Workload, + gpu::BindGroupLayoutDescriptor, rvec, +}; + +#[derive(new, Debug, Clone, IrFields)] +pub struct Multinomial { + pub probs: OpTensor, + pub num_samples: usize, + pub replacement: bool, + pub seed: Option, +} + +#[derive(Debug, derive_new::new, ShaderType, WgslMetadata)] +pub struct MultinomialMeta { + num_rows: u32, + row_size: u32, + num_samples: u32, + replacement: u32, + seed: u32, +} + +impl Operation for Multinomial { + fn name(&self) -> &'static str { + "Multinomial" + } + + fn compute_view(&self) -> Result { + // Supports [V] -> [S] and [B, V] -> [B, S] + let shape = self.probs.shape(); + assert!( + shape.dim() == 1 || shape.dim() == 2, + "Multinomial: input must be 1D or 2D" + ); + let out_shape = if shape.dim() == 1 { + crate::Shape::from(rvec![self.num_samples]) + } else { + crate::Shape::from(rvec![shape[0], self.num_samples]) + }; + let stride = Stride::from(&out_shape); + Ok(StorageView::new(out_shape, crate::DType::I32, stride)) + } + + fn srcs(&self) -> RVec<&OpTensor> { + rvec![&self.probs] + } + + fn supports_inplace(&self) -> bool { + false + } +} + +impl OpGuards for Multinomial { + fn check_shapes(&self) { + let shape = self.probs.shape(); + assert!( + shape.dim() == 1 || shape.dim() == 2, + "Multinomial: input must be 1D or 2D" + ); + } + + fn check_dtypes(&self) { + assert!( + self.probs.dtype().is_float(), + "Multinomial: probs must be float dtype" + ); + } +} + +pub enum MultinomialKernels { + Standard(Multinomial), +} + +impl GPUOperation for Multinomial { + type KernelEnum = MultinomialKernels; + + fn select_kernel(&self) -> Self::KernelEnum { + MultinomialKernels::Standard(self.clone()) + } +} + +impl KernelRenderable for MultinomialKernels { + fn register_bindings( + &self, + builder: &mut WgslKernelBuilder, + _: bool, + ) -> Result<(), OperationError> { + builder.register_storage("X", BindingMode::ReadOnly, Array::

    ::default()); + builder.register_storage("Y", BindingMode::ReadWrite, Array::>::default()); + builder.register_uniform(); + Ok(()) + } + + fn render( + &self, + _: bool, + dst: &OpTensor, + workgroup_size: &WorkgroupSize, + ) -> Result { + let device = dst.device().try_gpu()?; + let mut kernel_builder = WgslKernelBuilder::new( + workgroup_size.clone(), + rvec![ + BuiltIn::WorkgroupId, + BuiltIn::LocalInvocationIndex, + BuiltIn::NumWorkgroups + ], + device.compute_features().clone(), + ); + + self.register_bindings::

    (&mut kernel_builder, false)?; + kernel_builder.render_metadata(&self.metadata(dst, &self.kernel_element(dst))?); + + kernel_builder.write_global(wgsl! { + fn pcg_hash(input: u32) -> u32 { + let state = input * 747796405u + 2891336453u; + let word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u; + return (word >> 22u) ^ word; + } + + fn rand(seed: u32) -> f32 { + return f32(pcg_hash(seed)) / 4294967295.0; + } + + fn already_selected(row: u32, s: u32, j: u32, num_samples: u32) -> bool { + var t: u32 = 0u; + loop { + if (t >= s) { break; } + if (u32(Y[row * num_samples + t]) == j) { return true; } + t = t + 1u; + } + return false; + } + }); + + kernel_builder.write_main(wgsl! { + let x_offset = workgroup_id.x * 64u; + let row = (workgroup_id.y * num_workgroups.x * 64u) + x_offset + local_invocation_index; + if (row >= metadata.num_rows) { + return; + } + + let base = row * metadata.row_size; + + var s: u32 = 0u; + loop { + if (s >= metadata.num_samples) { break; } + + var total: f32 = 0.0; + var j: u32 = 0u; + loop { + if (j >= metadata.row_size) { break; } + if ( + metadata.replacement == 1u + || !already_selected(row, s, j, metadata.num_samples) + ) { + total = total + X[base + j]; + } + j = j + 1u; + } + + let r = rand((row + s) ^ metadata.seed) * total; + var acc: f32 = 0.0; + var chosen: u32 = 0u; + j = 0u; + loop { + if (j >= metadata.row_size) { break; } + if ( + metadata.replacement == 1u + || !already_selected(row, s, j, metadata.num_samples) + ) { + acc = acc + X[base + j]; + if (acc >= r) { + chosen = j; + break; + } + } + j = j + 1u; + } + + Y[row * metadata.num_samples + s] = i32(chosen); + s = s + 1u; + } + }); + + Ok(kernel_builder.build()?) + } +} + +impl Kernel for MultinomialKernels { + type Metadata = MultinomialMeta; + + fn kernel_name(&self) -> String { + match self { + MultinomialKernels::Standard(_) => "multinomial".to_string(), + } + } + + fn kernel_element(&self, _dst: &OpTensor) -> KernelElement { + KernelElement::Scalar + } + + fn calculate_dispatch(&self, dst: &OpTensor) -> Result { + let num_rows = if dst.shape().dim() == 1 { + 1 + } else { + dst.shape()[0] + }; + Ok(Workload::std(num_rows, self.kernel_element(dst))) + } + + fn storage_bind_group_layout( + &self, + _inplace: bool, + ) -> Result { + Ok(BindGroupLayoutDescriptor::unary()) + } + + fn metadata( + &self, + _dst: &OpTensor, + _: &KernelElement, + ) -> Result { + let MultinomialKernels::Standard(inner) = self; + let shape = inner.probs.shape(); + let (num_rows, row_size) = if shape.dim() == 1 { + (1u32, shape[0] as u32) + } else { + (shape[0] as u32, shape[1] as u32) + }; + Ok(MultinomialMeta { + num_rows, + row_size, + num_samples: inner.num_samples as u32, + replacement: if inner.replacement { 1 } else { 0 }, + seed: inner.seed.unwrap_or(0), + }) + } + + fn build_kernel( + &self, + inplace: bool, + dst: &OpTensor, + workgroup_size: &WorkgroupSize, + ) -> Result { + let MultinomialKernels::Standard(inner) = self; + let kernel_element = self.kernel_element(dst); + match (inner.probs.dtype(), &kernel_element) { + (DType::F32, KernelElement::Scalar) => { + self.render::>(inplace, dst, workgroup_size) + } + (DType::F16, KernelElement::Scalar) => { + self.render::>(inplace, dst, workgroup_size) + } + (DType::F32, KernelElement::Vec2) => { + self.render::>(inplace, dst, workgroup_size) + } + (DType::F32, KernelElement::Vec4) => { + self.render::>(inplace, dst, workgroup_size) + } + (DType::F16, KernelElement::Vec2) => { + self.render::>(inplace, dst, workgroup_size) + } + (DType::F16, KernelElement::Vec4) => { + self.render::>(inplace, dst, workgroup_size) + } + _ => Err(OperationError::CompileError(format!( + "Unsupported dtype {:?} or kernel element {:?}", + inner.probs.dtype(), + kernel_element + ))), + } + } +} + +#[cfg(all(test, feature = "pyo3"))] +mod tests { + use crate::{Device, DeviceRequest, Tensor, TensorOptions}; + + #[test] + fn multinomial_vector_single_hot_replacement_true() { + let device = Device::request_device(DeviceRequest::GPU).unwrap(); + let probs = Tensor::from_data(vec![0.0f32, 0.0, 1.0, 0.0], 4, TensorOptions::new()) + .unwrap() + .to(&device) + .unwrap(); + // Sample 3 with replacement; should always pick index 2 + let out = probs + .multinomial(3usize, true) + .unwrap() + .to(&Device::CPU) + .unwrap(); + let vals = out.to_vec::().unwrap(); + assert!(vals.iter().all(|&x| x == 2)); + } + + #[test] + fn multinomial_matrix_rowwise_single_hot_replacement_false() { + let device = Device::request_device(DeviceRequest::GPU).unwrap(); + // Two rows: [0,1,0] and [1,0,0] + let probs = Tensor::from_data( + vec![0.0f32, 1.0, 0.0, 1.0, 0.0, 0.0], + (2, 3), + TensorOptions::new(), + ) + .unwrap() + .to(&device) + .unwrap(); + // Sample 1 without replacement along last dim + let out = probs + .multinomial(1usize, false) + .unwrap() + .to(&Device::CPU) + .unwrap(); + let vals = out.to_vec::().unwrap(); + // Shape is [2,1] => vals[0], vals[1] + assert_eq!(vals.len(), 2); + assert_eq!(vals[0], 1); + assert_eq!(vals[1], 0); + } +} diff --git a/crates/piston-core/src/tensor.rs b/crates/piston-core/src/tensor.rs index 80c8feab..a10c7b0a 100644 --- a/crates/piston-core/src/tensor.rs +++ b/crates/piston-core/src/tensor.rs @@ -1510,6 +1510,20 @@ pub fn gather(input: OpTensor, dim: D, index: OpTensor) -> Result Result { + let shape = input.shape(); + if !(shape.dim() == 1 || shape.dim() == 2) { + anyhow::bail!("multinomial: input must be 1D [V] or 2D [B, V]"); + } + let device = input.device().clone(); + let rng = device.get_rng(); + let seed = rng.write().next_u32(); + let op = Multinomial::new(input, num_samples, replacement, Some(seed)); + let new_view = op.compute_view()?; + OpTensor::lazy(LazyOp::Multinomial(op), new_view, device, false) +} + // // TensorOptions for factory functions // @@ -2561,6 +2575,7 @@ impl OpTensor { LazyOp::Reduce(s) => s.create_gpu_compile_key(self, can_inplace, uniform).ok(), LazyOp::Detach(d) => self.gpu_compile_key_for_op(d, can_inplace, uniform), LazyOp::Gather(g) => g.create_gpu_compile_key(self, can_inplace, uniform).ok(), + LazyOp::Multinomial(m) => m.create_gpu_compile_key(self, can_inplace, uniform).ok(), LazyOp::FillPointwise(f) => f.create_gpu_compile_key(self, can_inplace, uniform).ok(), LazyOp::Bernoulli(b) => b.create_gpu_compile_key(self, can_inplace, uniform).ok(), LazyOp::Arange(a) => a.create_gpu_compile_key(self, can_inplace, uniform).ok(), @@ -2864,6 +2879,7 @@ pub fn compile_gpu_for_op( LazyOp::Reduce(s) => s.compile_gpu(gpu_compile_key, gpu_device, debug).ok(), LazyOp::Detach(d) => compile_gpu_for_op(d, gpu_compile_key, gpu_device, debug), LazyOp::Gather(g) => g.compile_gpu(gpu_compile_key, gpu_device, debug).ok(), + LazyOp::Multinomial(m) => m.compile_gpu(gpu_compile_key, gpu_device, debug).ok(), LazyOp::FillPointwise(f) => f.compile_gpu(gpu_compile_key, gpu_device, debug).ok(), LazyOp::Eye(e) => e.compile_gpu(gpu_compile_key, gpu_device, debug).ok(), LazyOp::Bernoulli(b) => b.compile_gpu(gpu_compile_key, gpu_device, debug).ok(), diff --git a/crates/piston-web/src/tensor.rs b/crates/piston-web/src/tensor.rs index 478d8258..eacbcad6 100644 --- a/crates/piston-web/src/tensor.rs +++ b/crates/piston-web/src/tensor.rs @@ -684,6 +684,14 @@ pub fn lerp(input: Tensor, end: Tensor, weight: TensorOrScalar) -> JsTensorResul #[js_tensor_web_op(name = Bernoulli, variants = [function, method, method_inplace])] pub fn bernoulli(input: Tensor) -> JsTensorResult {} +#[js_tensor_web_op(name = Multinomial, variants = [method, function])] +pub fn multinomial( + input: Tensor, + #[op(name = "numSamples")] num_samples: usize, + #[op(default = false)] replacement: bool, +) -> JsTensorResult { +} + #[js_tensor_web_op(name = Zero, variants = [method_inplace])] pub fn zero(input: Tensor) -> JsTensorResult {} From 338aca12b95433ceefd56fe6600bc32e0da9f852 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 19:09:29 -0600 Subject: [PATCH 442/590] Add topk op --- crates/piston-core/src/backprop.rs | 6 +- crates/piston-core/src/cpu/mod.rs | 1 + crates/piston-core/src/op.rs | 6 + crates/piston-core/src/ops/mod.rs | 2 + crates/piston-core/src/ops/topk.rs | 530 +++++++++++++++++++++++++++++ crates/piston-core/src/tensor.rs | 26 ++ crates/piston-web/src/tensor.rs | 16 + packages/web/src/globals.ts | 11 + packages/web/src/wasm.ts | 1 + 9 files changed, 598 insertions(+), 1 deletion(-) create mode 100644 crates/piston-core/src/ops/topk.rs diff --git a/crates/piston-core/src/backprop.rs b/crates/piston-core/src/backprop.rs index f6024794..b47b3fcf 100644 --- a/crates/piston-core/src/backprop.rs +++ b/crates/piston-core/src/backprop.rs @@ -325,7 +325,8 @@ impl Tensor { | LazyOp::Multinomial(_) | LazyOp::Arange(_) | LazyOp::Cache(_) - | LazyOp::Trilu(_) => nodes, + | LazyOp::Trilu(_) + | LazyOp::TopK(_) => nodes, } }; already_seen.insert(node.id(), track_grad); @@ -950,6 +951,9 @@ impl Tensor { op: UnaryOp::IsInf | UnaryOp::IsNan, .. }) => {} + // TopK itself doesn't handle any inputs in the graph; the Gather we parameterize + // with its indices does. Since we have a gather case, we're good to go. + LazyOp::TopK(_) => {} LazyOp::View(View { src: arg, .. }) => { let arg = arg.wrap(); let arg_grad = grad.clone().view(arg.shape().clone())?; diff --git a/crates/piston-core/src/cpu/mod.rs b/crates/piston-core/src/cpu/mod.rs index 7f47c1b7..67fb99ba 100644 --- a/crates/piston-core/src/cpu/mod.rs +++ b/crates/piston-core/src/cpu/mod.rs @@ -47,6 +47,7 @@ pub async fn apply_operation(op: LazyOp, dst: OpTensor) -> Result todo!(), LazyOp::Eye(_e) => todo!(), LazyOp::FillPointwise(_f) => todo!(), + LazyOp::TopK(_t) => todo!(), LazyOp::Bernoulli(_b) => todo!(), LazyOp::Multinomial(_m) => todo!(), LazyOp::Arange(_a) => todo!(), diff --git a/crates/piston-core/src/op.rs b/crates/piston-core/src/op.rs index 6d5a639e..a04327b2 100644 --- a/crates/piston-core/src/op.rs +++ b/crates/piston-core/src/op.rs @@ -63,6 +63,7 @@ pub enum LazyOp { Arange(Arange), Copy(TensorCopy), Detach(Box), //Because the entire graph is lazy, you can't actually detach something without computing the graph in parts + TopK(TopK), } impl LazyOp { @@ -92,6 +93,7 @@ impl LazyOp { LazyOp::ScatterAdd(sa) => sa.name(), LazyOp::Trilu(t) => t.name(), LazyOp::Eye(e) => e.name(), + LazyOp::TopK(t) => t.name(), LazyOp::FillPointwise(f) => f.name(), LazyOp::Bernoulli(b) => b.name(), LazyOp::Arange(a) => a.name(), @@ -134,6 +136,7 @@ impl LazyOp { LazyOp::ScatterAdd(sa) => sa.srcs(), LazyOp::Trilu(t) => t.srcs(), LazyOp::Eye(e) => e.srcs(), + LazyOp::TopK(t) => t.srcs(), LazyOp::Cache(c) => c.srcs(), LazyOp::Detach(d) => d.srcs(), LazyOp::View(v) => v.srcs(), @@ -173,6 +176,7 @@ impl LazyOp { LazyOp::ScatterAdd(sa) => sa.supports_inplace(), LazyOp::Trilu(t) => t.supports_inplace(), LazyOp::Eye(e) => e.supports_inplace(), + LazyOp::TopK(t) => t.supports_inplace(), LazyOp::FillPointwise(f) => f.supports_inplace(), LazyOp::Bernoulli(b) => b.supports_inplace(), LazyOp::Arange(a) => a.supports_inplace(), @@ -232,6 +236,7 @@ impl LazyOp { LazyOp::ScatterAdd(sa) => sa.check_invariants(), LazyOp::Trilu(t) => t.check_invariants(), LazyOp::Eye(e) => e.check_invariants(), + LazyOp::TopK(t) => t.check_invariants(), LazyOp::FillPointwise(f) => f.check_invariants(), LazyOp::Bernoulli(b) => b.check_invariants(), LazyOp::Arange(a) => a.check_invariants(), @@ -271,6 +276,7 @@ impl LazyOp { LazyOp::ScatterAdd(sa) => sa.ir(), LazyOp::Trilu(t) => t.ir(), LazyOp::Eye(e) => e.ir(), + LazyOp::TopK(t) => t.ir(), LazyOp::FillPointwise(f) => f.ir(), LazyOp::Bernoulli(b) => b.ir(), LazyOp::Arange(a) => a.ir(), diff --git a/crates/piston-core/src/ops/mod.rs b/crates/piston-core/src/ops/mod.rs index cdaa12dc..0a43838c 100644 --- a/crates/piston-core/src/ops/mod.rs +++ b/crates/piston-core/src/ops/mod.rs @@ -25,6 +25,7 @@ mod scatter_add; mod select; mod softmax; mod ternary; +mod topk; mod trilu; mod unary; mod view; @@ -60,6 +61,7 @@ pub use scatter_add::*; pub use select::*; pub use softmax::*; pub use ternary::*; +pub use topk::*; pub use trilu::*; pub use unary::*; pub use view::*; diff --git a/crates/piston-core/src/ops/topk.rs b/crates/piston-core/src/ops/topk.rs new file mode 100644 index 00000000..22c96f3a --- /dev/null +++ b/crates/piston-core/src/ops/topk.rs @@ -0,0 +1,530 @@ +use derive_new::new; +use half::f16; +use inline_wgsl::wgsl; +use piston_macros::IrFields; + +use crate::{ + Array, BindingMode, BuiltIn, DType, GPUOperation, Kernel, KernelElement, KernelRenderable, + KernelSource, OpGuards, OpTensor, Operation, OperationError, RVec, Scalar, Shape, StorageView, + Stride, WgslKernelBuilder, WgslPrimitive, WorkgroupSize, Workload, + gpu::BindGroupLayoutDescriptor, +}; + +const MAX_TOPK: usize = 256; + +#[derive(new, Debug, Clone, IrFields)] +pub struct TopK { + pub input: OpTensor, + pub k: usize, + pub dim: usize, + pub largest: bool, + pub sorted: bool, +} + +impl OpGuards for TopK { + fn check_shapes(&self) { + let rank = self.input.dim(); + assert!(rank > 0, "topk: input must have at least 1 dimension"); + assert!(self.dim < rank, "topk: dim out of range"); + assert!(self.k > 0, "topk: k must be > 0"); + assert!( + self.k <= self.input.shape()[self.dim], + "topk: k must be <= dim size" + ); + } + + fn check_dtypes(&self) { + let dt = self.input.dtype(); + assert!( + dt.is_float() || matches!(dt, DType::I32), + "topk: only F32/F16/I32 supported" + ); + assert!( + self.k <= MAX_TOPK, + "topk: k={} exceeds MAX_TOPK={} (temporary limitation)", + self.k, + MAX_TOPK + ); + assert!(!self.sorted, "topk: sorted=true not implemented"); + } +} + +impl Operation for TopK { + fn name(&self) -> &'static str { + "TopK" + } + + fn compute_view(&self) -> Result { + // Output are indices along dim: replace dim size with k, dtype I32 + let mut out_shape = self.input.shape().to_vec(); + out_shape[self.dim] = self.k; + let out_shape = Shape::from(out_shape); + let stride = Stride::from(&out_shape); + Ok(StorageView::new(out_shape, DType::I32, stride)) + } + + #[inline] + fn srcs(&self) -> RVec<&OpTensor> { + crate::rvec![&self.input] + } + + fn supports_inplace(&self) -> bool { + false + } +} + +pub enum TopKKernels { + Standard(TopK), +} + +impl GPUOperation for TopK { + type KernelEnum = TopKKernels; + + fn select_kernel(&self) -> Self::KernelEnum { + TopKKernels::Standard(self.clone()) + } +} + +#[derive(Debug, derive_new::new, encase::ShaderType, piston_macros::WgslMetadata)] +pub struct TopKMeta { + rank: u32, + dim: u32, + k: u32, + num_slices: u32, + shape: glam::UVec4, + stride: glam::UVec4, + out_stride: glam::UVec4, +} + +impl Kernel for TopKKernels { + type Metadata = TopKMeta; + + fn kernel_name(&self) -> String { + match self { + TopKKernels::Standard(inner) => if inner.largest { + "topk_largest" + } else { + "topk_smallest" + } + .to_string(), + } + } + + fn kernel_element(&self, _dst: &OpTensor) -> KernelElement { + KernelElement::Scalar + } + + fn calculate_dispatch(&self, dst: &OpTensor) -> Result { + // One invocation per slice (all dims except target) + let TopKKernels::Standard(inner) = self; + let total_slices = dst.shape().numel() / inner.k; + Ok(Workload::std(total_slices, KernelElement::Scalar)) + } + + fn storage_bind_group_layout( + &self, + inplace: bool, + ) -> Result { + if inplace { + return Err(OperationError::InplaceError( + "TopK cannot be done in place".to_string(), + )); + } + Ok(BindGroupLayoutDescriptor::unary()) + } + + fn metadata( + &self, + dst: &OpTensor, + _: &KernelElement, + ) -> Result { + let TopKKernels::Standard(inner) = self; + let rank = inner.input.dim() as u32; + let mut shape = [1u32; 4]; + let mut stride = [0u32; 4]; + for (i, &d) in inner.input.shape().iter().enumerate() { + shape[i] = d as u32; + stride[i] = inner.input.stride()[i] as u32; + } + let mut out_stride = [0u32; 4]; + for (i, &s) in dst.stride().iter().enumerate() { + out_stride[i] = s as u32; + } + let num_slices = (inner.input.shape().numel() / inner.input.shape()[inner.dim]) as u32; + Ok(TopKMeta { + rank, + dim: inner.dim as u32, + k: inner.k as u32, + num_slices, + shape: shape.into(), + stride: stride.into(), + out_stride: out_stride.into(), + }) + } + + fn build_kernel( + &self, + inplace: bool, + dst: &OpTensor, + workgroup_size: &WorkgroupSize, + ) -> Result { + let TopKKernels::Standard(inner) = self; + match inner.input.dtype() { + DType::F32 => self.render::>(inplace, dst, workgroup_size), + DType::F16 => self.render::>(inplace, dst, workgroup_size), + DType::I32 => self.render::>(inplace, dst, workgroup_size), + _ => Err(OperationError::CompileError( + "TopK only supports F32/F16/I32".to_string(), + )), + } + } +} + +impl KernelRenderable for TopKKernels { + fn register_bindings( + &self, + builder: &mut WgslKernelBuilder, + _: bool, + ) -> Result<(), OperationError> { + builder.register_storage("X", BindingMode::ReadOnly, Array::

    ::default()); + builder.register_storage("Y", BindingMode::ReadWrite, Array::>::default()); + builder.register_uniform(); + Ok(()) + } + + fn render( + &self, + inplace: bool, + dst: &OpTensor, + workgroup_size: &WorkgroupSize, + ) -> Result { + let device = dst.device().try_gpu()?; + let mut kernel_builder = WgslKernelBuilder::new( + workgroup_size.clone(), + crate::rvec![ + BuiltIn::WorkgroupId, + BuiltIn::LocalInvocationIndex, + BuiltIn::NumWorkgroups + ], + device.compute_features().clone(), + ); + + let TopKKernels::Standard(inner) = self; + + self.register_bindings::

    (&mut kernel_builder, inplace)?; + kernel_builder.render_metadata(&self.metadata(dst, &self.kernel_element(dst))?); + + let dtype = P::render_type(); + // Helpers + kernel_builder.write_global(wgsl! { + const MAX_TOPK: u32 = 'MAX_TOPK; + + fn coords_from_slice_id( + idx_in: u32, + rank: u32, + shape: vec4, + exclude_dim: u32, + ) -> vec4 { + var coords: vec4 = vec4(0u); + var idx: u32 = idx_in; + for (var d: u32 = 0u; d < rank; d++) { + let dim_idx = rank - 1u - d; + if (dim_idx == exclude_dim) { continue; } + let base = shape[dim_idx]; + coords[dim_idx] = idx % base; + idx = idx / base; + } + return coords; + } + + fn linear_from_coords(coords: vec4, stride: vec4, rank: u32) -> u32 { + var off: u32 = 0u; + for (var d: u32 = 0u; d < rank; d++) { + off += coords[d] * stride[d]; + } + return off; + } + }); + + let loop_body = if inner.largest { + wgsl! { + while (pos < limit && top_vals[pos] > v) { pos += 1u; } + } + } else { + wgsl! { + while (pos < limit && top_vals[pos] < v) { pos += 1u; } + } + }; + + // Main + kernel_builder.write_main(wgsl! { + // One invocation per slice + let x_offset = workgroup_id.x * 64u; + let slice_id = (workgroup_id.y * num_workgroups.x * 64u) + x_offset + local_invocation_index; + if (slice_id >= metadata.num_slices) { return; } + + // Guard Max K + if (metadata.k > MAX_TOPK) { return; } + + let rank = metadata.rank; + let dim = metadata.dim; + let n_dim = metadata.shape[dim]; + + var coords = coords_from_slice_id(slice_id, rank, metadata.shape, dim); + // Ensure target dim index 0 for base + coords[dim] = 0u; + let base = linear_from_coords(coords, metadata.stride, rank); + let step = metadata.stride[dim]; + + // Local buffers + var top_vals: array<'dtype, MAX_TOPK>; + var top_idxs: array; + var count: u32 = 0u; + + // Iterate across the reduction dimension + for (var j: u32 = 0u; j < n_dim; j++) { + let idx = base + j * step; + let v = X[idx]; + + // Determine insertion position + var pos: u32 = 0u; + let limit = count; + 'loop_body + + if (count < metadata.k) { + // Make room + var i: i32 = i32(count); + while (i > i32(pos)) { + top_vals[u32(i)] = top_vals[u32(i - 1)]; + top_idxs[u32(i)] = top_idxs[u32(i - 1)]; + i -= 1; + } + top_vals[pos] = v; + top_idxs[pos] = i32(j); + count += 1u; + } else if (pos < metadata.k) { + // Insert and drop the last element + var i: i32 = i32(metadata.k - 1u); + while (i > i32(pos)) { + top_vals[u32(i)] = top_vals[u32(i - 1)]; + top_idxs[u32(i)] = top_idxs[u32(i - 1)]; + i -= 1; + } + top_vals[pos] = v; + top_idxs[pos] = i32(j); + } + } + + // Write out indices for this slice across k outputs + // coords currently has target dim = 0; update per r + for (var r: u32 = 0u; r < metadata.k; r++) { + coords[dim] = r; + let out_off = linear_from_coords(coords, metadata.out_stride, rank); + Y[out_off] = top_idxs[r]; + } + }); + + Ok(kernel_builder.build()?) + } +} + +#[cfg(all(test, feature = "pyo3"))] +mod tests { + use crate::randint; + use crate::{DType, Device, DeviceRequest, Tensor, randn, test_util::run_py_prg}; + use test_strategy::{Arbitrary, proptest}; + + #[derive(Arbitrary, Debug)] + struct TopKProblem { + #[strategy(1..=3usize)] + B: usize, + #[strategy(1..=64usize)] + M: usize, + #[strategy(1..=64usize)] + N: usize, + #[strategy(0..=2usize)] + dim: usize, + #[strategy(1..=4usize)] + k: usize, + largest: bool, + } + + fn ground_truth_values( + a: &Tensor, + k: usize, + dim: usize, + largest: bool, + ) -> anyhow::Result { + let prg = r#" +import torch +def topk_vals(a, k, dim, largest): + return torch.topk(torch.from_numpy(a), k, dim=dim, largest=largest)[0].float().numpy() +"# + .to_string(); + run_py_prg( + prg, + &[a], + &[&(k as i32), &(dim as i32), &largest], + DType::F32, + ) + } + + fn ground_truth_indices( + a: &Tensor, + k: usize, + dim: usize, + largest: bool, + ) -> anyhow::Result { + let prg = r#" +import torch +import numpy as np +def topk_idx(a, k, dim, largest): + return torch.topk(torch.from_numpy(a), k, dim=dim, largest=largest)[1].to(torch.int32).numpy().astype(np.int32) +"#.to_string(); + run_py_prg( + prg, + &[a], + &[&(k as i32), &(dim as i32), &largest], + DType::I32, + ) + } + + #[proptest(cases = 8)] + fn test_topk(prob: TopKProblem) { + let TopKProblem { + B, + M, + N, + dim, + k, + largest, + } = prob; + let device = Device::request_device(DeviceRequest::GPU).unwrap(); + let a = randn((B, M, N), None, None, Default::default()).unwrap(); + let (_dim_len, k_use) = { + let dims = a.shape().to_vec(); + let dlen = dims[dim]; + (dlen, k.min(dims[dim].max(1))) + }; + let a_vals = ground_truth_values(&a, k_use, dim, largest).unwrap(); + let a_idx = ground_truth_indices(&a, k_use, dim, largest).unwrap(); + + let a_gpu = a.to(&device).unwrap(); + let ours = crate::topk(a_gpu, k_use, Some(dim), Some(largest), Some(false)).unwrap(); + let values = ours[0].clone().to(&Device::CPU).unwrap(); + let indices = ours[1].clone().to(&Device::CPU).unwrap(); + let a_idx_f32 = a_idx + .clone() + .cast(DType::F32) + .unwrap() + .to(&Device::CPU) + .unwrap(); + let indices_f32 = indices + .clone() + .cast(DType::F32) + .unwrap() + .to(&Device::CPU) + .unwrap(); + + a_vals.all_close(&values, 3e-5f32, 1e-5f32).unwrap(); + assert_eq!(a_idx.dtype(), DType::I32); + assert_eq!(indices.dtype(), DType::I32); + a_idx_f32.all_close(&indices_f32, 0.0f32, 0.0f32).unwrap(); + } + + #[derive(Arbitrary, Debug)] + struct TopKIntProblem { + #[strategy(1..=3usize)] + B: usize, + #[strategy(1..=32usize)] + M: usize, + #[strategy(1..=32usize)] + N: usize, + #[strategy(0..=2usize)] + dim: usize, + #[strategy(1..=4usize)] + k: usize, + largest: bool, + } + + fn ground_truth_values_i32( + a: &Tensor, + k: usize, + dim: usize, + largest: bool, + ) -> anyhow::Result { + let prg = r#" +import torch +def topk_vals_i32(a, k, dim, largest): + return torch.topk(torch.from_numpy(a).to(torch.int32), k, dim=dim, largest=largest)[0].to(torch.int32).numpy() +"#.to_string(); + run_py_prg( + prg, + &[a], + &[&(k as i32), &(dim as i32), &largest], + DType::I32, + ) + } + + fn ground_truth_indices_i32( + a: &Tensor, + k: usize, + dim: usize, + largest: bool, + ) -> anyhow::Result { + let prg = r#" +import torch +import numpy as np +def topk_idx_i32(a, k, dim, largest): + return torch.topk(torch.from_numpy(a).to(torch.int32), k, dim=dim, largest=largest)[1].to(torch.int32).numpy().astype(np.int32) +"#.to_string(); + run_py_prg( + prg, + &[a], + &[&(k as i32), &(dim as i32), &largest], + DType::I32, + ) + } + + #[proptest(cases = 6)] + fn test_topk_i32(prob: TopKIntProblem) { + let TopKIntProblem { + B, + M, + N, + dim, + k, + largest, + } = prob; + let device = Device::request_device(DeviceRequest::GPU).unwrap(); + + // Generate integer tensor in a safe range [0, 1000) + let a = randint(0, 1000, (B, M, N), Default::default()).unwrap(); + let (_dim_len, k_use) = { + let dims = a.shape().to_vec(); + let dlen = dims[dim]; + (dlen, k.min(dims[dim].max(1))) + }; + + let a_vals = ground_truth_values_i32(&a, k_use, dim, largest).unwrap(); + let a_idx = ground_truth_indices_i32(&a, k_use, dim, largest).unwrap(); + + let a_gpu = a.to(&device).unwrap(); + let ours = crate::topk(a_gpu, k_use, Some(dim), Some(largest), Some(false)).unwrap(); + let values = ours[0].clone().to(&Device::CPU).unwrap(); + let indices = ours[1].clone().to(&Device::CPU).unwrap(); + + assert_eq!(values.dtype(), DType::I32); + assert_eq!(indices.dtype(), DType::I32); + + // Compare values and indices against PyTorch ground truth + // Convert to F32 for all_close API that expects floats + let a_vals_f32 = a_vals.clone().cast(DType::F32).unwrap(); + let values_f32 = values.clone().cast(DType::F32).unwrap(); + a_vals_f32.all_close(&values_f32, 0.0f32, 0.0f32).unwrap(); + + let a_idx_f32 = a_idx.clone().cast(DType::F32).unwrap(); + let indices_f32 = indices.clone().cast(DType::F32).unwrap(); + a_idx_f32.all_close(&indices_f32, 0.0f32, 0.0f32).unwrap(); + } +} diff --git a/crates/piston-core/src/tensor.rs b/crates/piston-core/src/tensor.rs index a10c7b0a..d9abb862 100644 --- a/crates/piston-core/src/tensor.rs +++ b/crates/piston-core/src/tensor.rs @@ -1524,6 +1524,30 @@ pub fn multinomial(input: OpTensor, num_samples: usize, replacement: bool) -> Re OpTensor::lazy(LazyOp::Multinomial(op), new_view, device, false) } +#[tensor_op(variants = [function, method])] +pub fn topk( + input: OpTensor, + k: usize, + dim: Option, + largest: Option, + sorted: Option, +) -> Result> { + let dim_idx = match dim { + Some(d) => d.to_index(input.shape(), "topk")?, + None => input.dim().saturating_sub(1), + }; + let largest = largest.unwrap_or(true); + let sorted = sorted.unwrap_or(false); + + let device = input.device().clone(); + let op = TopK::new(input.clone(), k, dim_idx, largest, sorted); + let indices_view = op.compute_view()?; + let indices = OpTensor::lazy(LazyOp::TopK(op), indices_view, device, false)?; + + let values = gather_kernel(input, dim_idx, indices.clone())?; + Ok(rvec![values, indices]) +} + // // TensorOptions for factory functions // @@ -2581,6 +2605,7 @@ impl OpTensor { LazyOp::Arange(a) => a.create_gpu_compile_key(self, can_inplace, uniform).ok(), LazyOp::Eye(e) => e.create_gpu_compile_key(self, can_inplace, uniform).ok(), LazyOp::Copy(_) | LazyOp::View(_) | LazyOp::Const => None, + LazyOp::TopK(t) => t.create_gpu_compile_key(self, can_inplace, uniform).ok(), } } @@ -2884,6 +2909,7 @@ pub fn compile_gpu_for_op( LazyOp::Eye(e) => e.compile_gpu(gpu_compile_key, gpu_device, debug).ok(), LazyOp::Bernoulli(b) => b.compile_gpu(gpu_compile_key, gpu_device, debug).ok(), LazyOp::Arange(a) => a.compile_gpu(gpu_compile_key, gpu_device, debug).ok(), + LazyOp::TopK(t) => t.compile_gpu(gpu_compile_key, gpu_device, debug).ok(), LazyOp::View(_) | LazyOp::Const => None, LazyOp::Copy(_) => panic!("Copy should not have a gpu_compile_key"), } diff --git a/crates/piston-web/src/tensor.rs b/crates/piston-web/src/tensor.rs index eacbcad6..1af2ccaf 100644 --- a/crates/piston-web/src/tensor.rs +++ b/crates/piston-web/src/tensor.rs @@ -692,6 +692,22 @@ pub fn multinomial( ) -> JsTensorResult { } +#[js_tensor_web_op(name = Topk, variants = [method, function])] +pub fn topk( + input: Tensor, + k: usize, + #[op(default = -1)] dim: Dim, + #[op(default = true)] largest: bool, + #[op(default = false)] sorted: bool, +) -> Result, JsError> { + if sorted { + return Err(JsError::new("topk: sorted=true not implemented")); + } + let result = piston::topk(input, k, Some(dim), Some(largest), Some(sorted)) + .map_err(|e| e.into_js_error())?; + Ok::<_, JsError>(result.into_iter().collect::>()) +} + #[js_tensor_web_op(name = Zero, variants = [method_inplace])] pub fn zero(input: Tensor) -> JsTensorResult {} diff --git a/packages/web/src/globals.ts b/packages/web/src/globals.ts index 53e38822..568189c6 100644 --- a/packages/web/src/globals.ts +++ b/packages/web/src/globals.ts @@ -61,6 +61,7 @@ import { swiglu as swiglu_wasm, tanh as tanh_wasm, Tensor_wasm, + topk as topk_wasm, uint32_wasm, zeros as zeros_wasm, zerosLike as zerosLike_wasm, @@ -129,6 +130,7 @@ export let recip: typeof recip_wasm; export let eye: typeof eye_wasm; export let isnan: typeof isnan_wasm; export let isinf: typeof isinf_wasm; +export let topk: typeof topk_wasm; export let tensor: { ( @@ -186,6 +188,14 @@ function wrapWithLibTensor( }; } +function wrapWithLibTensorArray( + fn: (...args: Args) => Tensor_wasm[], +): (...args: Args) => Tensor[] { + return (...args: Args) => { + return fn(...args).map((t) => Tensor._wrap(t)); + }; +} + function wrapWithParam( fn: (...args: [...Args, O?]) => Tensor, ): { @@ -305,6 +315,7 @@ export async function initGlobals() { logicalNot = wrapWithLibTensor(logicalNot_wasm); logicalOr = wrapWithLibTensor(logicalOr_wasm); logicalXor = wrapWithLibTensor(logicalXor_wasm); + topk = wrapWithLibTensorArray(topk_wasm); bernoulli = wrapWithLibTensor(bernoulli_wasm); zeros = wrapWithParam(wrapWithLibTensor(zeros_wasm)); zerosLike = wrapWithParam(wrapWithLibTensor(zerosLike_wasm)); diff --git a/packages/web/src/wasm.ts b/packages/web/src/wasm.ts index 77eb8d7b..45d0b60c 100644 --- a/packages/web/src/wasm.ts +++ b/packages/web/src/wasm.ts @@ -67,6 +67,7 @@ export { swiglu, tanh, Tensor as Tensor_wasm, + topk, Trainer as Trainer_wasm, uint32 as uint32_wasm, default as wasmInit, From 2b315aaa05a2ce3dceea4c37bc9db0bb3b0d2b8c Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 20:58:44 -0600 Subject: [PATCH 443/590] Add one-hot op --- crates/piston-core/src/backprop.rs | 4 +- crates/piston-core/src/cpu/mod.rs | 1 + crates/piston-core/src/op.rs | 6 + crates/piston-core/src/ops/mod.rs | 2 + crates/piston-core/src/ops/one_hot.rs | 246 ++++++++++++++++++++++++++ crates/piston-core/src/tensor.rs | 10 ++ crates/piston-web/src/tensor.rs | 3 + packages/web/src/globals.ts | 3 + 8 files changed, 274 insertions(+), 1 deletion(-) create mode 100644 crates/piston-core/src/ops/one_hot.rs diff --git a/crates/piston-core/src/backprop.rs b/crates/piston-core/src/backprop.rs index b47b3fcf..2773b0ff 100644 --- a/crates/piston-core/src/backprop.rs +++ b/crates/piston-core/src/backprop.rs @@ -326,7 +326,8 @@ impl Tensor { | LazyOp::Arange(_) | LazyOp::Cache(_) | LazyOp::Trilu(_) - | LazyOp::TopK(_) => nodes, + | LazyOp::TopK(_) + | LazyOp::OneHot(_) => nodes, } }; already_seen.insert(node.id(), track_grad); @@ -943,6 +944,7 @@ impl Tensor { .. }) | LazyOp::Eye(_) + | LazyOp::OneHot(_) | LazyOp::FillPointwise(_) | LazyOp::Bernoulli(_) | LazyOp::Multinomial(_) diff --git a/crates/piston-core/src/cpu/mod.rs b/crates/piston-core/src/cpu/mod.rs index 67fb99ba..8d57bbed 100644 --- a/crates/piston-core/src/cpu/mod.rs +++ b/crates/piston-core/src/cpu/mod.rs @@ -47,6 +47,7 @@ pub async fn apply_operation(op: LazyOp, dst: OpTensor) -> Result todo!(), LazyOp::Eye(_e) => todo!(), LazyOp::FillPointwise(_f) => todo!(), + LazyOp::OneHot(_o) => todo!(), LazyOp::TopK(_t) => todo!(), LazyOp::Bernoulli(_b) => todo!(), LazyOp::Multinomial(_m) => todo!(), diff --git a/crates/piston-core/src/op.rs b/crates/piston-core/src/op.rs index a04327b2..bb322354 100644 --- a/crates/piston-core/src/op.rs +++ b/crates/piston-core/src/op.rs @@ -60,6 +60,7 @@ pub enum LazyOp { ScatterAdd(ScatterAdd), Trilu(TriluOp), Eye(Eye), + OneHot(OneHot), Arange(Arange), Copy(TensorCopy), Detach(Box), //Because the entire graph is lazy, you can't actually detach something without computing the graph in parts @@ -83,6 +84,7 @@ impl LazyOp { LazyOp::WhereCond(w) => w.name(), LazyOp::Reduce(s) => s.name(), LazyOp::Gather(g) => g.name(), + LazyOp::OneHot(o) => o.name(), LazyOp::Multinomial(m) => m.name(), LazyOp::Conv(c) => c.name(), LazyOp::Ternary(t) => t.name(), @@ -126,6 +128,7 @@ impl LazyOp { LazyOp::WhereCond(w) => w.srcs(), LazyOp::Reduce(s) => s.srcs(), LazyOp::Gather(g) => g.srcs(), + LazyOp::OneHot(o) => o.srcs(), LazyOp::Multinomial(m) => m.srcs(), LazyOp::Conv(c) => c.srcs(), LazyOp::Ternary(t) => t.srcs(), @@ -166,6 +169,7 @@ impl LazyOp { LazyOp::WhereCond(w) => w.supports_inplace(), LazyOp::Reduce(s) => s.supports_inplace(), LazyOp::Gather(g) => g.supports_inplace(), + LazyOp::OneHot(o) => o.supports_inplace(), LazyOp::Multinomial(m) => m.supports_inplace(), LazyOp::Conv(c) => c.supports_inplace(), LazyOp::Ternary(t) => t.supports_inplace(), @@ -226,6 +230,7 @@ impl LazyOp { LazyOp::WhereCond(w) => w.check_invariants(), LazyOp::Reduce(s) => s.check_invariants(), LazyOp::Gather(g) => g.check_invariants(), + LazyOp::OneHot(o) => o.check_invariants(), LazyOp::Multinomial(m) => m.check_invariants(), LazyOp::Conv(c) => c.check_invariants(), LazyOp::Ternary(t) => t.check_invariants(), @@ -266,6 +271,7 @@ impl LazyOp { LazyOp::WhereCond(w) => w.ir(), LazyOp::Reduce(s) => s.ir(), LazyOp::Gather(g) => g.ir(), + LazyOp::OneHot(o) => o.ir(), LazyOp::Multinomial(m) => m.ir(), LazyOp::Conv(c) => c.ir(), LazyOp::Ternary(t) => t.ir(), diff --git a/crates/piston-core/src/ops/mod.rs b/crates/piston-core/src/ops/mod.rs index 0a43838c..af5c0ea9 100644 --- a/crates/piston-core/src/ops/mod.rs +++ b/crates/piston-core/src/ops/mod.rs @@ -17,6 +17,7 @@ mod lerp; mod matmul; mod multinomial; mod norm; +mod one_hot; mod powf; mod reduce; mod reindex; @@ -52,6 +53,7 @@ pub use lerp::*; pub use matmul::*; pub use multinomial::*; pub use norm::*; +pub use one_hot::*; use piston_macros::IrFields; pub use powf::*; pub use reduce::*; diff --git a/crates/piston-core/src/ops/one_hot.rs b/crates/piston-core/src/ops/one_hot.rs new file mode 100644 index 00000000..dd25cac7 --- /dev/null +++ b/crates/piston-core/src/ops/one_hot.rs @@ -0,0 +1,246 @@ +use crate::{ + Array, BindingMode, BuiltIn, DType, GPUOperation, Kernel, KernelElement, KernelRenderable, + KernelSource, OpGuards, OpTensor, Operation, OperationError, RVec, Scalar, Shape, StorageView, + Stride, WgslKernelBuilder, WgslPrimitive, WorkgroupSize, Workload, + gpu::BindGroupLayoutDescriptor, rvec, +}; +use derive_new::new; +use encase::ShaderType; +use inline_wgsl::wgsl; +use piston_macros::{IrFields, WgslMetadata}; + +#[derive(new, Debug, Clone, IrFields)] +pub struct OneHot { + pub indices: OpTensor, + pub num_classes: usize, +} + +#[derive(Debug, derive_new::new, ShaderType, WgslMetadata)] +pub struct OneHotMeta { + input_numel: u32, + num_classes: u32, +} + +impl OpGuards for OneHot { + fn check_shapes(&self) {} + + fn check_dtypes(&self) { + assert!( + matches!(self.indices.dtype(), DType::I32), + "one_hot: indices must be I32" + ); + } + + fn check_custom(&self) { + assert!(self.num_classes > 0, "one_hot: num_classes must be > 0"); + } +} + +impl Operation for OneHot { + fn name(&self) -> &'static str { + "OneHot" + } + + fn compute_view(&self) -> Result { + let mut out_shape = self.indices.shape().to_vec(); + out_shape.push(self.num_classes); + let shape: Shape = out_shape.into(); + let stride = Stride::from(&shape); + Ok(StorageView::new(shape, DType::I32, stride)) + } + + fn srcs(&self) -> RVec<&OpTensor> { + rvec![&self.indices] + } + + fn supports_inplace(&self) -> bool { + false + } +} + +pub enum OneHotKernels { + Standard(OneHot), +} + +impl GPUOperation for OneHot { + type KernelEnum = OneHotKernels; + + fn select_kernel(&self) -> Self::KernelEnum { + OneHotKernels::Standard(self.clone()) + } +} + +impl KernelRenderable for OneHotKernels { + fn register_bindings( + &self, + builder: &mut WgslKernelBuilder, + _: bool, + ) -> Result<(), OperationError> { + builder.register_storage("X", BindingMode::ReadOnly, Array::>::default()); + builder.register_storage("Y", BindingMode::ReadWrite, Array::>::default()); + builder.register_uniform(); + Ok(()) + } + + fn render( + &self, + _: bool, + dst: &OpTensor, + workgroup_size: &WorkgroupSize, + ) -> Result { + let device = dst.device().try_gpu()?; + let mut kernel_builder = WgslKernelBuilder::new( + workgroup_size.clone(), + rvec![ + BuiltIn::WorkgroupId, + BuiltIn::LocalInvocationIndex, + BuiltIn::NumWorkgroups + ], + device.compute_features().clone(), + ); + + self.register_bindings::

    (&mut kernel_builder, false)?; + kernel_builder.render_metadata(&self.metadata(dst, &self.kernel_element(dst))?); + + kernel_builder.write_main(wgsl! { + let x_offset = workgroup_id.x * 64u; + let index = (workgroup_id.y * num_workgroups.x * 64u) + x_offset + local_invocation_index; + let total = metadata.input_numel * metadata.num_classes; + if (index >= total) { + return; + } + + for (var i: u32 = index; i < total; i += 64u * num_workgroups.x) { + let sample_idx = i / metadata.num_classes; + let class_idx = i % metadata.num_classes; + let idx_val = i32(X[sample_idx]); + if (idx_val == i32(class_idx)) { + Y[i] = 1; + } else { + Y[i] = 0; + } + } + }); + + Ok(kernel_builder.build()?) + } +} + +impl Kernel for OneHotKernels { + type Metadata = OneHotMeta; + + fn storage_bind_group_layout( + &self, + inplace: bool, + ) -> Result { + if inplace { + panic!("Only non-inplace one_hot is supported"); + } + Ok(BindGroupLayoutDescriptor::unary()) + } + + fn calculate_dispatch(&self, dst: &OpTensor) -> Result { + Ok(Workload::std(dst.shape().numel(), KernelElement::Scalar)) + } + + fn kernel_name(&self) -> String { + match self { + OneHotKernels::Standard(_) => "one_hot".to_string(), + } + } + + fn kernel_element(&self, _dst: &OpTensor) -> KernelElement { + KernelElement::Scalar + } + + fn metadata( + &self, + _dst: &OpTensor, + _ke: &KernelElement, + ) -> Result { + let OneHotKernels::Standard(inner) = self; + Ok(OneHotMeta { + input_numel: inner.indices.shape().numel() as u32, + num_classes: inner.num_classes as u32, + }) + } + + fn build_kernel( + &self, + inplace: bool, + dst: &OpTensor, + workgroup_size: &WorkgroupSize, + ) -> Result { + match dst.dtype() { + DType::I32 => self.render::>(inplace, dst, workgroup_size), + _ => Err(OperationError::CompileError(format!( + "Unsupported dtype {:?} for one_hot", + dst.dtype() + ))), + } + } +} + +#[cfg(all(test, feature = "pyo3"))] +mod tests { + use crate::test_util::run_py_prg; + use crate::{DType, Device, DeviceRequest, Tensor, TensorOptions, randint}; + use test_strategy::{Arbitrary, proptest}; + + fn ground_truth(indices: &Tensor, num_classes: usize) -> anyhow::Result { + let prg = r#" +import torch +def one_hot(indices, num_classes): + t = torch.from_numpy(indices).long() + out = torch.nn.functional.one_hot(t, num_classes) + return out.to(torch.int32).numpy() +"#; + run_py_prg(prg.to_string(), &[indices], &[&num_classes], DType::I32) + } + + #[derive(Arbitrary, Debug)] + struct OneHotProblem { + #[strategy(1..=3usize)] + B: usize, + #[strategy(1..=16usize)] + M: usize, + #[strategy(1..=8usize)] + N: usize, + #[strategy(2..=16usize)] + num_classes: usize, + #[strategy(1..=3usize)] + rank: usize, + } + + fn shape_for(prob: &OneHotProblem) -> crate::Shape { + match prob.rank { + 1 => crate::Shape::from(vec![prob.M]), + 2 => crate::Shape::from(vec![prob.B, prob.M]), + _ => crate::Shape::from(vec![prob.B, prob.M, prob.N]), + } + } + + #[proptest(cases = 8)] + fn test_one_hot_matches_pytorch(prob: OneHotProblem) { + let _ = env_logger::builder().is_test(true).try_init(); + let device = Device::request_device(DeviceRequest::GPU).unwrap(); + let shape = shape_for(&prob); + + let indices = randint(0, prob.num_classes as i32, shape, TensorOptions::new()).unwrap(); + + let ground = ground_truth(&indices, prob.num_classes).unwrap(); + + let ours = indices + .clone() + .to(&device) + .unwrap() + .one_hot(prob.num_classes) + .unwrap() + .to(&crate::Device::CPU) + .unwrap(); + + let ground_f32 = ground.cast(DType::F32).unwrap(); + let ours_f32 = ours.cast(DType::F32).unwrap(); + ground_f32.all_close(&ours_f32, 0.0f32, 0.0f32).unwrap(); + } +} diff --git a/crates/piston-core/src/tensor.rs b/crates/piston-core/src/tensor.rs index d9abb862..0364c0d9 100644 --- a/crates/piston-core/src/tensor.rs +++ b/crates/piston-core/src/tensor.rs @@ -2080,6 +2080,14 @@ pub fn eye(n: usize, m: Option, options: TensorOptions) -> Result Result { + let device = input.device().clone(); + let op = crate::ops::OneHot::new(input, num_classes); + let view = op.compute_view()?; + OpTensor::lazy(LazyOp::OneHot(op), view, device, false) +} + /// Private implementation for full fn full_impl>( shape: &Shape, @@ -2599,6 +2607,7 @@ impl OpTensor { LazyOp::Reduce(s) => s.create_gpu_compile_key(self, can_inplace, uniform).ok(), LazyOp::Detach(d) => self.gpu_compile_key_for_op(d, can_inplace, uniform), LazyOp::Gather(g) => g.create_gpu_compile_key(self, can_inplace, uniform).ok(), + LazyOp::OneHot(o) => o.create_gpu_compile_key(self, can_inplace, uniform).ok(), LazyOp::Multinomial(m) => m.create_gpu_compile_key(self, can_inplace, uniform).ok(), LazyOp::FillPointwise(f) => f.create_gpu_compile_key(self, can_inplace, uniform).ok(), LazyOp::Bernoulli(b) => b.create_gpu_compile_key(self, can_inplace, uniform).ok(), @@ -2902,6 +2911,7 @@ pub fn compile_gpu_for_op( LazyOp::Trilu(t) => t.compile_gpu(gpu_compile_key, gpu_device, debug).ok(), LazyOp::Cache(c) => c.compile_gpu(gpu_compile_key, gpu_device, debug).ok(), LazyOp::Reduce(s) => s.compile_gpu(gpu_compile_key, gpu_device, debug).ok(), + LazyOp::OneHot(o) => o.compile_gpu(gpu_compile_key, gpu_device, debug).ok(), LazyOp::Detach(d) => compile_gpu_for_op(d, gpu_compile_key, gpu_device, debug), LazyOp::Gather(g) => g.compile_gpu(gpu_compile_key, gpu_device, debug).ok(), LazyOp::Multinomial(m) => m.compile_gpu(gpu_compile_key, gpu_device, debug).ok(), diff --git a/crates/piston-web/src/tensor.rs b/crates/piston-web/src/tensor.rs index 1af2ccaf..6564f2de 100644 --- a/crates/piston-web/src/tensor.rs +++ b/crates/piston-web/src/tensor.rs @@ -957,6 +957,9 @@ pub fn eye(n: usize, m: Option, options: TensorOptions) -> anyhow::Result piston::eye(n, m, options) } +#[js_tensor_web_op(name = OneHot, variants = [function, method])] +pub fn one_hot(input: Tensor, #[op(name = "numClasses")] num_classes: usize) -> JsTensorResult {} + #[js_tensor_web_op(name = "Zeros", variants = [function])] pub fn zeros(shape: Shape, options: TensorOptions) -> anyhow::Result { piston::zeros(shape, options) diff --git a/packages/web/src/globals.ts b/packages/web/src/globals.ts index 568189c6..e68b1600 100644 --- a/packages/web/src/globals.ts +++ b/packages/web/src/globals.ts @@ -43,6 +43,7 @@ import { lt as lt_wasm, ne as ne_wasm, neg as neg_wasm, + oneHot as oneHot_wasm, ones as ones_wasm, onesLike as onesLike_wasm, rand as rand_wasm, @@ -130,6 +131,7 @@ export let recip: typeof recip_wasm; export let eye: typeof eye_wasm; export let isnan: typeof isnan_wasm; export let isinf: typeof isinf_wasm; +export let oneHot: typeof oneHot_wasm; export let topk: typeof topk_wasm; export let tensor: { @@ -315,6 +317,7 @@ export async function initGlobals() { logicalNot = wrapWithLibTensor(logicalNot_wasm); logicalOr = wrapWithLibTensor(logicalOr_wasm); logicalXor = wrapWithLibTensor(logicalXor_wasm); + oneHot = wrapWithLibTensor(oneHot_wasm); topk = wrapWithLibTensorArray(topk_wasm); bernoulli = wrapWithLibTensor(bernoulli_wasm); zeros = wrapWithParam(wrapWithLibTensor(zeros_wasm)); From 1c9e39e9333912568bb94c6f011d6e86db9a905e Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 21:08:29 -0600 Subject: [PATCH 444/590] Update detach arm in backprop --- crates/piston-core/src/backprop.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/piston-core/src/backprop.rs b/crates/piston-core/src/backprop.rs index 2773b0ff..effa4b7d 100644 --- a/crates/piston-core/src/backprop.rs +++ b/crates/piston-core/src/backprop.rs @@ -923,7 +923,6 @@ impl Tensor { let arg_grad = grad.div(node.clone())?.affine(0.5, 0.)?; ctx.add(&arg, arg_grad)?; } - LazyOp::Detach(_) => todo!(), LazyOp::Unary(Unary { input: arg, op: UnaryOp::Sigmoid, @@ -933,6 +932,7 @@ impl Tensor { let arg_grad = grad.clone().mul(node.clone())?.mul((1. - node.clone())?)?; ctx.add(&arg, arg_grad)?; } + LazyOp::Detach(_) => todo!("detach backprop"), LazyOp::Reduce(Reduce { input: _, op: ReduceOp::ArgMax, From 49224b340bd40040cb9594e29ef7b7e699cbd186 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 21:08:45 -0600 Subject: [PATCH 445/590] Label todo arms in backprop --- crates/piston-core/src/backprop.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/piston-core/src/backprop.rs b/crates/piston-core/src/backprop.rs index effa4b7d..f2f21ad8 100644 --- a/crates/piston-core/src/backprop.rs +++ b/crates/piston-core/src/backprop.rs @@ -300,8 +300,8 @@ impl Tensor { nodes } } - LazyOp::IndexWrite(_) => todo!(), - LazyOp::Copy(_) => todo!(), + LazyOp::IndexWrite(_) => todo!("index write walking for backprop"), + LazyOp::Copy(_) => todo!("copy walking for backprop"), LazyOp::Detach(_) | LazyOp::Cmp(_) | LazyOp::Unary(Unary { @@ -1209,10 +1209,10 @@ impl Tensor { offset += input.shape()[dim]; } } - LazyOp::Norm(_) => todo!(), + LazyOp::Norm(_) => todo!("norm backprop"), LazyOp::Const => panic!("piston internal error - const node in backprop"), LazyOp::Cmp(_) => todo!("cmp backprop"), - LazyOp::Powf(_) => todo!(), + LazyOp::Powf(_) => todo!("powf backprop"), LazyOp::RoPE(RoPE { input: arg, dim, @@ -1224,11 +1224,11 @@ impl Tensor { let arg_grad = grad.rope_backward_(dim, base, offset)?; ctx.add(&arg, arg_grad)?; } - LazyOp::Conv(_) => todo!(), - LazyOp::IndexWrite(_) => todo!(), - LazyOp::IndexAdd(_) => todo!(), - LazyOp::Cache(_) => todo!(), - LazyOp::Copy(_) => todo!(), + LazyOp::Conv(_) => todo!("conv backprop"), + LazyOp::IndexWrite(_) => todo!("index write backprop"), + LazyOp::IndexAdd(_) => todo!("index add backprop"), + LazyOp::Cache(_) => todo!("cache backprop"), + LazyOp::Copy(_) => todo!("copy backprop"), }; } Ok(()) From 784b14d84efca8448f61caaa81baa94761bcc64e Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 21:13:38 -0600 Subject: [PATCH 446/590] Everything that goes with updating wgpu --- Cargo.toml | 2 +- crates/piston-core/src/executable.rs | 6 ++-- .../buffer_allocator/lazy_graph_executor.rs | 22 ++++++++---- crates/piston-core/src/gpu/device.rs | 10 +++--- crates/piston-core/src/gpu/logging.rs | 2 +- .../piston-core/src/gpu/pools/buffer_pool.rs | 4 +-- .../src/gpu/pools/kernel_module_pool.rs | 7 +++- crates/piston-core/src/gpu/profiler.rs | 6 ++-- crates/piston-core/src/storage/gpu_buffer.rs | 34 +++++++++---------- crates/piston-core/src/tensor.rs | 10 ------ crates/piston-web/src/device.rs | 8 +++-- crates/piston-web/src/tensor.rs | 11 +----- 12 files changed, 62 insertions(+), 60 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 43fe792c..16cc688f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ inherits = "release" debug = 2 [workspace.dependencies] -wgpu = { git = "https://github.com/vinhowe/wgpu", branch = "feature/extract-webgpu-gpubuffer", features = [ +wgpu = { git = "https://github.com/vinhowe/wgpu", rev = "8aa00b0ef4b2903b542741213f3ad186c4c32186", features = [ "fragile-send-sync-non-atomic-wasm", ] } bytemuck = { version = "1.14.0", features = [ diff --git a/crates/piston-core/src/executable.rs b/crates/piston-core/src/executable.rs index 0bffe352..ab9a9032 100644 --- a/crates/piston-core/src/executable.rs +++ b/crates/piston-core/src/executable.rs @@ -79,11 +79,11 @@ impl Executable { ) -> Result<(), ExecutionError> { compute_pass.set_pipeline(pipeline_resources.get(op.pipeline_handle())?); for (group_index, bind_group) in op.storage_groups().iter().enumerate() { - compute_pass.set_bind_group(group_index as u32, bind_group, &[]); + compute_pass.set_bind_group(group_index as u32, Some(&**bind_group), &[]); } let uniform_group_index = op.storage_groups().len() as u32; let uniform_group = gpu_uniform.bind_group(); - compute_pass.set_bind_group(uniform_group_index, uniform_group, &[op.offset()]); + compute_pass.set_bind_group(uniform_group_index, Some(&**uniform_group), &[op.offset()]); let [x_count, y_count, z_count] = op.workgroup_count().as_slice(); compute_pass.dispatch_workgroups(x_count, y_count, z_count); Ok(()) @@ -211,7 +211,7 @@ impl Executable { 0, &debug_buffer, 0, - result_t.num_bytes() as _, + Some(result_t.num_bytes() as _), ); gpu_bufs diff --git a/crates/piston-core/src/gpu/buffer_allocator/lazy_graph_executor.rs b/crates/piston-core/src/gpu/buffer_allocator/lazy_graph_executor.rs index 474d1ae3..a8b19ffb 100644 --- a/crates/piston-core/src/gpu/buffer_allocator/lazy_graph_executor.rs +++ b/crates/piston-core/src/gpu/buffer_allocator/lazy_graph_executor.rs @@ -203,7 +203,12 @@ impl LazyGraphExecutor { .await?; if immediate { - gpu_device.poll(wgpu::MaintainBase::WaitForSubmissionIndex(index)); + gpu_device + .poll(wgpu::PollType::Wait { + submission_index: Some(index), + timeout: None, + }) + .unwrap(); } Ok(result) } @@ -252,17 +257,22 @@ impl LazyGraphExecutor { let mut seen_nodes = HashSet::default(); let mut post_order = Vec::new(); + // let mut can_inplace_count = 0; + // let mut no_op_support_for_inplacing_count = 0; + // let mut no_inplacing_because_source_requires_grad_count = 0; + // let post_order_len = combined_post_orders.len(); + // First we loop over the post order to hash the tensors in the right order for tensor in combined_post_orders.into_iter() { if seen_nodes.insert(tensor.id()) { post_order.push(tensor); // Scope to drop tensor_hashes before inserting let srcs = tensor.op().srcs(); - log::trace!( - "{:?}: Srcs: {:?}", - tensor.id(), - srcs.iter().map(|s| s.id()).collect::>() - ); + // log::trace!( + // "{:?}: Srcs: {:?}", + // tensor.id(), + // srcs.iter().map(|s| s.id()).collect::>() + // ); let first_src = srcs.first().cloned(); let mut to_modify = None; diff --git a/crates/piston-core/src/gpu/device.rs b/crates/piston-core/src/gpu/device.rs index a08458b3..8d9a6b39 100644 --- a/crates/piston-core/src/gpu/device.rs +++ b/crates/piston-core/src/gpu/device.rs @@ -54,7 +54,7 @@ impl std::fmt::Debug for WgpuDevice { impl PartialEq for WgpuDevice { fn eq(&self, other: &Self) -> bool { - self.ordinal == other.ordinal && self.device.global_id() == other.device.global_id() + self.ordinal == other.ordinal && self.device == other.device } } @@ -83,13 +83,15 @@ impl WgpuDevice { ..Default::default() }, memory_hints: wgpu::MemoryHints::Performance, + experimental_features: wgpu::ExperimentalFeatures::disabled(), + trace: Default::default(), }; - let device_request = adapter.request_device(&device_descriptor, None).await; + let device_request = adapter.request_device(&device_descriptor).await; let (device, queue) = if let Err(e) = device_request { log::error!("Failed to acq. device, trying with reduced limits: {e:?}"); device_descriptor.required_limits = adapter.limits(); device_descriptor.required_features = adapter.features(); - adapter.request_device(&device_descriptor, None).await + adapter.request_device(&device_descriptor).await } else { device_request }?; @@ -147,7 +149,7 @@ impl WgpuDevice { force_fallback_adapter: false, }) .await - .ok_or(DeviceError::AdapterRequestFailed) + .map_err(|_| DeviceError::AdapterRequestFailed) } #[cfg(not(target_arch = "wasm32"))] diff --git a/crates/piston-core/src/gpu/logging.rs b/crates/piston-core/src/gpu/logging.rs index dc9afe19..e121b7b1 100644 --- a/crates/piston-core/src/gpu/logging.rs +++ b/crates/piston-core/src/gpu/logging.rs @@ -173,7 +173,7 @@ impl TensorLogStep { pub fn gpu_buf(&self) -> JsValue { self.gpu_buf .as_ref() - .map(|buf| buf.as_webgpu_buffer().unwrap().into()) + .map(|buf| buf.as_webgpu_buffer().into()) .unwrap_or(JsValue::null()) } } diff --git a/crates/piston-core/src/gpu/pools/buffer_pool.rs b/crates/piston-core/src/gpu/pools/buffer_pool.rs index fdd39537..bbe8085e 100644 --- a/crates/piston-core/src/gpu/pools/buffer_pool.rs +++ b/crates/piston-core/src/gpu/pools/buffer_pool.rs @@ -38,7 +38,7 @@ impl std::ops::Deref for PooledGPUBuffer { impl PartialEq for PooledGPUBuffer { fn eq(&self, other: &Self) -> bool { - self.0.inner.global_id() == other.0.inner.global_id() + self.0.inner == other.0.inner } } @@ -117,7 +117,7 @@ impl BufferPool { }); if immediate { device.queue().submit(None); - device.poll(wgpu::Maintain::Wait); + device.poll(wgpu::PollType::wait_indefinitely()).unwrap(); } buf })) diff --git a/crates/piston-core/src/gpu/pools/kernel_module_pool.rs b/crates/piston-core/src/gpu/pools/kernel_module_pool.rs index 3689cc96..6978748d 100644 --- a/crates/piston-core/src/gpu/pools/kernel_module_pool.rs +++ b/crates/piston-core/src/gpu/pools/kernel_module_pool.rs @@ -61,7 +61,12 @@ impl KernelModulePool { log::warn!("Using checked shader compilation"); device.create_shader_module(shader_module_desc) } else { - unsafe { device.create_shader_module_unchecked(shader_module_desc) } + unsafe { + device.create_shader_module_trusted( + shader_module_desc, + wgpu::ShaderRuntimeChecks::unchecked(), + ) + } } }) } diff --git a/crates/piston-core/src/gpu/profiler.rs b/crates/piston-core/src/gpu/profiler.rs index 758938f4..5fee58ad 100644 --- a/crates/piston-core/src/gpu/profiler.rs +++ b/crates/piston-core/src/gpu/profiler.rs @@ -260,7 +260,7 @@ impl Profiler { self.destination_buffer .slice(..) .map_async(wgpu::MapMode::Read, |_| ()); - self.device.poll(wgpu::Maintain::Wait); + self.device.poll(wgpu::Maintain::Wait).unwrap(); let timestamp_view = self .destination_buffer .slice( @@ -290,7 +290,9 @@ impl Profiler { .map_async(wgpu::MapMode::Read, move |_| { tx.send(()).expect("Failed to sync for profiling"); }); - self.device.poll(wgpu::Maintain::Wait); + self.device + .poll(wgpu::PollType::wait_indefinitely()) + .unwrap(); #[cfg(target_arch = "wasm32")] rx.receive().await.unwrap(); #[cfg(not(target_arch = "wasm32"))] diff --git a/crates/piston-core/src/storage/gpu_buffer.rs b/crates/piston-core/src/storage/gpu_buffer.rs index 2e3c7484..d0acccc9 100644 --- a/crates/piston-core/src/storage/gpu_buffer.rs +++ b/crates/piston-core/src/storage/gpu_buffer.rs @@ -83,7 +83,7 @@ impl GPUBuffer { ) .unwrap(); device.queue().submit(None); - device.poll(wgpu::Maintain::Wait); + device.poll(wgpu::PollType::wait_indefinitely()).unwrap(); Self { inner, alignment, @@ -119,7 +119,7 @@ impl GPUBuffer { device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); encoder.copy_buffer_to_buffer(&self.inner, 0, &clone, 0, self.inner.size()); device.queue().submit(Some(encoder.finish())); - device.poll(wgpu::Maintain::Wait); + device.poll(wgpu::PollType::wait_indefinitely()); Self { inner: clone, alignment: self.alignment, @@ -136,22 +136,22 @@ impl GPUBuffer { CPUBuffer::from_disk::(reader, shape)?.to_device(device) } - pub fn trim_id(id: wgpu::Id) -> Option { - let id = format!("{id:?}"); - let trimmed = id.trim_start_matches("Id(").trim_end_matches(')'); - if trimmed.len() > 12 && trimmed.chars().all(|c| c.is_numeric()) { - Some(trimmed[12..].to_string()) - } else { - None - } - } + // pub fn trim_id(id: wgpu::Id) -> Option { + // let id = format!("{id:?}"); + // let trimmed = id.trim_start_matches("Id(").trim_end_matches(')'); + // if trimmed.len() > 12 && trimmed.chars().all(|c| c.is_numeric()) { + // Some(trimmed[12..].to_string()) + // } else { + // None + // } + // } #[cfg(feature = "plotting")] pub fn plot_fmt(&self) -> String { - let id_string = Self::trim_id(self.inner().global_id()).unwrap_or_default(); + // let id_string = Self::trim_id(self.inner().global_id()).unwrap_or_default(); format!( - "GPU:#{}\n({:?})\n{} bytes", - id_string, + "GPU:\n({:?})\n{} bytes", + // id_string, self.inner.handle, self.inner.size() ) @@ -178,8 +178,8 @@ impl DeviceStorage for GPUBuffer { fn dump(&self, _: DType, _: bool) -> String { let mut result = String::new(); - let id_string = Self::trim_id(self.inner().global_id()).unwrap_or_default(); - result.push_str(&format!("GPU Buffer #{id_string}\n")); + // let id_string = Self::trim_id(self.inner().global_id()).unwrap_or_default(); + // result.push_str(&format!("GPU Buffer #{id_string}\n")); result.push_str(&format!("Size: {} bytes\n", self.inner.size())); result } @@ -208,7 +208,7 @@ pub async fn wgpu_buffer_to_cpu_buffer( }) .expect("Failed to send result of read_buffer"); }); - device.poll(wgpu::Maintain::Wait); + device.poll(wgpu::PollType::wait_indefinitely()).unwrap(); #[cfg(target_arch = "wasm32")] return rx.receive().await.unwrap().unwrap(); #[cfg(not(target_arch = "wasm32"))] diff --git a/crates/piston-core/src/tensor.rs b/crates/piston-core/src/tensor.rs index 0364c0d9..086a8a42 100644 --- a/crates/piston-core/src/tensor.rs +++ b/crates/piston-core/src/tensor.rs @@ -3484,16 +3484,6 @@ impl Tensor { pub fn take_grad(&self) -> Option { self.inner_or_source().take_grad() } - - pub fn storage_id(&self) -> Option { - self.inner_or_source() - .storage() - .as_ref() - .and_then(|s| match s { - Storage::CPU(_) => None, - Storage::GPU(g) => Some(g.inner().global_id().inner() as _), - }) - } } impl From> for Tensor { diff --git a/crates/piston-web/src/device.rs b/crates/piston-web/src/device.rs index 9d6a0d27..f0803403 100644 --- a/crates/piston-web/src/device.rs +++ b/crates/piston-web/src/device.rs @@ -66,9 +66,11 @@ impl JsDevice { #[wasm_bindgen(js_name = asWebGPUDevice)] pub fn as_webgpu_device(&self) -> Option { match &self.inner { - Device::GPU(gpu) => gpu - .as_webgpu_device() - .map(|d| d.dyn_into::().unwrap()), + Device::GPU(gpu) => Some( + gpu.as_webgpu_device() + .dyn_into::() + .unwrap(), + ), Device::CPU => None, } } diff --git a/crates/piston-web/src/tensor.rs b/crates/piston-web/src/tensor.rs index 6564f2de..86a59fde 100644 --- a/crates/piston-web/src/tensor.rs +++ b/crates/piston-web/src/tensor.rs @@ -287,11 +287,6 @@ impl JsTensor { self.inner().retain_grad().map_err(|e| e.into_js_error()) } - #[wasm_bindgen(js_name = storageId)] - pub fn storage_id(&self) -> Option { - self.inner().storage_id() - } - pub fn is_contiguous(&self) -> bool { self.inner().is_contiguous() } @@ -822,11 +817,7 @@ impl JsTensor { let inner = self.inner(); let inner_source = inner.inner_or_source(); match inner_source.storage().as_ref() { - Some(Storage::GPU(g)) => Ok(g - .inner() - .as_webgpu_buffer() - .map(|b| b.into()) - .unwrap_or(JsValue::null())), + Some(Storage::GPU(g)) => Ok(g.inner().as_webgpu_buffer().into()), _ => Err(JsError::new("Tensor is not on GPU")), } } From 1bf21249352334b84ca9ef25d043d6eb2653fe7f Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 21:16:38 -0600 Subject: [PATCH 447/590] Clunky GPU usage peaks API --- .../src/gpu/buffer_allocator/allocator.rs | 8 ++++ crates/piston-core/src/gpu/device.rs | 8 ++++ .../src/gpu/pools/bind_group_layout_pool.rs | 4 ++ .../src/gpu/pools/bind_group_pool.rs | 21 ++++----- .../piston-core/src/gpu/pools/buffer_pool.rs | 8 ++++ .../src/gpu/pools/dynamic_resource_pool.rs | 44 +++++++++++++++++-- .../src/gpu/pools/pipeline_layout_pool.rs | 4 ++ .../src/gpu/pools/pipeline_pool.rs | 4 ++ crates/piston-core/src/storage/cpu_buffer.rs | 18 ++++++++ crates/piston-web/src/device.rs | 10 +++++ 10 files changed, 115 insertions(+), 14 deletions(-) diff --git a/crates/piston-core/src/gpu/buffer_allocator/allocator.rs b/crates/piston-core/src/gpu/buffer_allocator/allocator.rs index 7db93266..43160da9 100644 --- a/crates/piston-core/src/gpu/buffer_allocator/allocator.rs +++ b/crates/piston-core/src/gpu/buffer_allocator/allocator.rs @@ -459,4 +459,12 @@ impl BufferAllocator { pub fn usage_bytes(&self) -> u64 { self.pool.read().total_gpu_size_in_bytes() } + + pub fn reset_usage_peaks(&self) { + self.pool.read().reset_usage_peaks(); + } + + pub fn peak_usage_bytes_since_reset(&self) -> u64 { + self.pool.read().peak_total_gpu_size_in_bytes_since_reset() + } } diff --git a/crates/piston-core/src/gpu/device.rs b/crates/piston-core/src/gpu/device.rs index 8d9a6b39..c6a84ab0 100644 --- a/crates/piston-core/src/gpu/device.rs +++ b/crates/piston-core/src/gpu/device.rs @@ -374,6 +374,14 @@ impl WgpuDevice { self.buffer_allocator.usage_bytes() } + pub fn mark_usage_bytes_step(&self) { + self.buffer_allocator.reset_usage_peaks(); + } + + pub fn peak_usage_bytes_since_reset(&self) -> u64 { + self.buffer_allocator.peak_usage_bytes_since_reset() + } + pub fn set_step_log_config(&self, config: StepLogConfig) { self.lazy_graph_executor.write().set_step_log_config(config); } diff --git a/crates/piston-core/src/gpu/pools/bind_group_layout_pool.rs b/crates/piston-core/src/gpu/pools/bind_group_layout_pool.rs index 6ec039fc..68828deb 100644 --- a/crates/piston-core/src/gpu/pools/bind_group_layout_pool.rs +++ b/crates/piston-core/src/gpu/pools/bind_group_layout_pool.rs @@ -206,4 +206,8 @@ impl BindGroupLayoutPool { ) -> StaticResourcePoolReadLockAccessor<'_, BindGroupLayoutHandle, wgpu::BindGroupLayout> { self.inner.resources() } + + pub fn num_resources(&self) -> usize { + self.inner.num_resources() + } } diff --git a/crates/piston-core/src/gpu/pools/bind_group_pool.rs b/crates/piston-core/src/gpu/pools/bind_group_pool.rs index ff4848e3..8ca646d6 100644 --- a/crates/piston-core/src/gpu/pools/bind_group_pool.rs +++ b/crates/piston-core/src/gpu/pools/bind_group_pool.rs @@ -1,5 +1,6 @@ use super::*; use crate::{RVec, gpu::WgpuDevice}; +use parking_lot::RwLock; use std::sync::Arc; slotmap::new_key_type! { pub struct GpuBindGroupHandle; } @@ -84,12 +85,8 @@ impl DynamicResourcesDesc for BindGroupDescriptor { /// The question whether a bind groups happen to be re-usable becomes again a simple question of matching /// bind group descs which itself does not contain any ref counted objects! pub struct BindGroupPool { - // Use a DynamicResourcePool because it gives out reference counted handles - // which makes interacting with buffer/textures easier. - // - // On the flipside if someone requests the exact same bind group again as before, - // they'll get a new one which is unnecessary. But this is *very* unlikely to ever happen. - inner: DynamicResourcePool, + // Wrapped in RwLock so we can run maintenance each pass without requiring &mut self on device + inner: RwLock>, } impl Default for BindGroupPool { @@ -101,7 +98,7 @@ impl Default for BindGroupPool { impl BindGroupPool { pub fn new() -> Self { Self { - inner: DynamicResourcePool::default(), + inner: RwLock::new(DynamicResourcePool::default()), } } @@ -118,7 +115,7 @@ impl BindGroupPool { .collect() }; - let resource = self.inner.get_or_create(desc, |desc| { + let resource = self.inner.read().get_or_create(desc, |desc| { let mut buffer_index = 0; let entries = desc @@ -155,7 +152,11 @@ impl BindGroupPool { } } - pub fn begin_pass(&mut self, pass_index: u64) { - self.inner.begin_pass(pass_index, |_res| {}); + pub fn num_resources(&self) -> usize { + self.inner.read().num_resources() + } + + pub fn begin_pass(&self, pass_index: u64) { + self.inner.write().begin_pass(pass_index, |_res| {}); } } diff --git a/crates/piston-core/src/gpu/pools/buffer_pool.rs b/crates/piston-core/src/gpu/pools/buffer_pool.rs index bbe8085e..77c0bf9c 100644 --- a/crates/piston-core/src/gpu/pools/buffer_pool.rs +++ b/crates/piston-core/src/gpu/pools/buffer_pool.rs @@ -147,4 +147,12 @@ impl BufferPool { pub fn total_gpu_size_in_bytes(&self) -> u64 { self.inner.total_resource_size_in_bytes() } + + pub fn reset_usage_peaks(&self) { + self.inner.reset_usage_peaks(); + } + + pub fn peak_total_gpu_size_in_bytes_since_reset(&self) -> u64 { + self.inner.peak_total_resource_size_in_bytes_since_reset() + } } diff --git a/crates/piston-core/src/gpu/pools/dynamic_resource_pool.rs b/crates/piston-core/src/gpu/pools/dynamic_resource_pool.rs index 20f4d82a..2e50165c 100644 --- a/crates/piston-core/src/gpu/pools/dynamic_resource_pool.rs +++ b/crates/piston-core/src/gpu/pools/dynamic_resource_pool.rs @@ -58,6 +58,8 @@ pub(super) struct DynamicResourcePool { state: RwLock>, current_pass_index: u64, total_resource_size_in_bytes: AtomicU64, + /// Peak of total_resource_size_in_bytes observed since last reset + peak_total_resource_size_since_reset: AtomicU64, } /// We cannot #derive(Default) as that would require Handle/Desc/Res to implement Default too. @@ -73,6 +75,7 @@ where }), current_pass_index: Default::default(), total_resource_size_in_bytes: AtomicU64::new(0), + peak_total_resource_size_since_reset: AtomicU64::new(0), } } } @@ -103,10 +106,28 @@ where // Otherwise create a new resource let inner_resource = { constructor(desc) }; - self.total_resource_size_in_bytes.fetch_add( - desc.resource_size_in_bytes(), - std::sync::atomic::Ordering::Relaxed, - ); + let added_size = desc.resource_size_in_bytes(); + let prev_total = self + .total_resource_size_in_bytes + .fetch_add(added_size, std::sync::atomic::Ordering::Relaxed); + let new_total = prev_total + added_size; + // Update peak if this allocation increased the total beyond the current peak + let mut observed_peak = self + .peak_total_resource_size_since_reset + .load(std::sync::atomic::Ordering::Relaxed); + while new_total > observed_peak { + match self + .peak_total_resource_size_since_reset + .compare_exchange_weak( + observed_peak, + new_total, + std::sync::atomic::Ordering::Relaxed, + std::sync::atomic::Ordering::Relaxed, + ) { + Ok(_) => break, + Err(current) => observed_peak = current, + } + } let handle = state.all_resources.insert_with_key(|handle| { Arc::new(DynamicResource { @@ -206,6 +227,21 @@ where self.total_resource_size_in_bytes .load(std::sync::atomic::Ordering::Relaxed) } + + /// Reset peak tracking to the current total so the window starts from baseline usage + pub fn reset_usage_peaks(&self) { + let current_total = self + .total_resource_size_in_bytes + .load(std::sync::atomic::Ordering::Relaxed); + self.peak_total_resource_size_since_reset + .store(current_total, std::sync::atomic::Ordering::Relaxed); + } + + /// Get the peak total resource size (in bytes) observed since the last reset + pub fn peak_total_resource_size_in_bytes_since_reset(&self) -> u64 { + self.peak_total_resource_size_since_reset + .load(std::sync::atomic::Ordering::Relaxed) + } } #[cfg(test)] diff --git a/crates/piston-core/src/gpu/pools/pipeline_layout_pool.rs b/crates/piston-core/src/gpu/pools/pipeline_layout_pool.rs index a19f492b..31de2dc6 100644 --- a/crates/piston-core/src/gpu/pools/pipeline_layout_pool.rs +++ b/crates/piston-core/src/gpu/pools/pipeline_layout_pool.rs @@ -54,4 +54,8 @@ impl PipelineLayoutPool { ) -> StaticResourcePoolReadLockAccessor<'_, PipelineLayoutHandle, wgpu::PipelineLayout> { self.inner.resources() } + + pub fn num_resources(&self) -> usize { + self.inner.num_resources() + } } diff --git a/crates/piston-core/src/gpu/pools/pipeline_pool.rs b/crates/piston-core/src/gpu/pools/pipeline_pool.rs index 0966fd6e..2c07fbc3 100644 --- a/crates/piston-core/src/gpu/pools/pipeline_pool.rs +++ b/crates/piston-core/src/gpu/pools/pipeline_pool.rs @@ -69,4 +69,8 @@ impl ComputePipelinePool { ) -> StaticResourcePoolReadLockAccessor<'_, ComputePipelineHandle, wgpu::ComputePipeline> { self.inner.resources() } + + pub fn num_resources(&self) -> usize { + self.inner.num_resources() + } } diff --git a/crates/piston-core/src/storage/cpu_buffer.rs b/crates/piston-core/src/storage/cpu_buffer.rs index 253f46b3..d093c966 100644 --- a/crates/piston-core/src/storage/cpu_buffer.rs +++ b/crates/piston-core/src/storage/cpu_buffer.rs @@ -4,11 +4,23 @@ use half::f16; use crate::{DType, Device, DeviceError, GPUBuffer, Shape, TensorDType, storage::DeviceStorage}; use maybe_async::maybe_async; +use std::sync::atomic::{AtomicUsize, Ordering}; use std::{alloc::Layout, fmt::Debug, mem::MaybeUninit, sync::Arc}; #[derive(derive_new::new, Debug, PartialEq, Eq)] pub struct RawCPUBuffer(*mut u8, Layout); +static ALIVE_CPUBUF_BYTES: AtomicUsize = AtomicUsize::new(0); +static ALIVE_CPUBUF_COUNT: AtomicUsize = AtomicUsize::new(0); + +pub fn alive_cpu_bytes() -> usize { + ALIVE_CPUBUF_BYTES.load(Ordering::Relaxed) +} + +pub fn alive_cpu_count() -> usize { + ALIVE_CPUBUF_COUNT.load(Ordering::Relaxed) +} + impl RawCPUBuffer { pub fn into_raw_parts(&self) -> (*mut u8, Layout) { (self.0, self.1) @@ -41,6 +53,10 @@ impl RawCPUBuffer { assert!(!ptr.is_null()); ptr } as *mut u8; + if size > 0 { + ALIVE_CPUBUF_BYTES.fetch_add(size, Ordering::Relaxed); + ALIVE_CPUBUF_COUNT.fetch_add(1, Ordering::Relaxed); + } Self(data, layout) } } @@ -56,6 +72,8 @@ impl Clone for RawCPUBuffer { impl Drop for RawCPUBuffer { fn drop(&mut self) { if !self.0.is_null() && self.1.size() > 0 { + ALIVE_CPUBUF_BYTES.fetch_sub(self.1.size(), Ordering::Relaxed); + ALIVE_CPUBUF_COUNT.fetch_sub(1, Ordering::Relaxed); unsafe { std::alloc::dealloc(self.0, self.1) } } } diff --git a/crates/piston-web/src/device.rs b/crates/piston-web/src/device.rs index f0803403..98021014 100644 --- a/crates/piston-web/src/device.rs +++ b/crates/piston-web/src/device.rs @@ -58,6 +58,16 @@ impl JsDevice { self.inner.try_gpu().unwrap().usage_bytes() } + #[wasm_bindgen(js_name = markUsageBytesStep)] + pub fn mark_usage_bytes_step(&self) { + self.inner.try_gpu().unwrap().mark_usage_bytes_step(); + } + + #[wasm_bindgen(js_name = peakUsageBytes)] + pub fn peak_usage_bytes(&self) -> u64 { + self.inner.try_gpu().unwrap().peak_usage_bytes_since_reset() + } + #[wasm_bindgen(js_name = setVRAMLimit)] pub fn set_vram_limit(&self, #[wasm_bindgen(js_name = vramLimit)] vram_limit: Option) { self.inner.try_gpu().unwrap().set_vram_limit(vram_limit); From 5c77af16343038e309f3d16197b23afbd7fe9e02 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 21:17:28 -0600 Subject: [PATCH 448/590] Remove JsCast, JsValue from core Device --- crates/piston-core/src/gpu/device.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crates/piston-core/src/gpu/device.rs b/crates/piston-core/src/gpu/device.rs index c6a84ab0..e6787373 100644 --- a/crates/piston-core/src/gpu/device.rs +++ b/crates/piston-core/src/gpu/device.rs @@ -4,10 +4,6 @@ use maybe_async::maybe_async; use parking_lot::RwLock; use std::collections::BTreeMap; use std::{borrow::Cow, sync::Arc}; -#[cfg(target_arch = "wasm32")] -use wasm_bindgen::JsCast; -#[cfg(target_arch = "wasm32")] -use wasm_bindgen::JsValue; use wgpu::{Adapter, Limits}; use crate::DeviceError; From 547b738df91468eb7fdd92c3b440bc1a9f8f8b75 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 21:18:22 -0600 Subject: [PATCH 449/590] Begin pass on bind group pool to mitigate memory leak --- crates/piston-core/src/gpu/device.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/piston-core/src/gpu/device.rs b/crates/piston-core/src/gpu/device.rs index e6787373..32c87916 100644 --- a/crates/piston-core/src/gpu/device.rs +++ b/crates/piston-core/src/gpu/device.rs @@ -356,6 +356,7 @@ impl WgpuDevice { pub fn begin_pass(&self, pass_index: u64) { self.buffer_allocator.begin_pass(pass_index); + self.bind_group_pool.begin_pass(pass_index); } pub fn compute_features(&self) -> &DeviceFeatures { From 540b9ade7f4581a9a9d7881a9d2327123e068f93 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 21:20:36 -0600 Subject: [PATCH 450/590] Add is_leaf and correct bugs --- crates/piston-core/src/op.rs | 16 ++++++++++++++++ crates/piston-core/src/tensor.rs | 10 +++++++++- crates/piston-web/src/tensor.rs | 5 +++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/crates/piston-core/src/op.rs b/crates/piston-core/src/op.rs index bb322354..73712b11 100644 --- a/crates/piston-core/src/op.rs +++ b/crates/piston-core/src/op.rs @@ -206,6 +206,22 @@ impl LazyOp { ) } + /// Returns true if this operation represents a leaf in the computation graph. + /// + /// Semantics are analogous to PyTorch's `is_leaf`: + /// - First, treat obvious parameter-capable creators as leaves (`can_be_parameter`). + /// - Also treat explicit detach boundaries as leaves (`Detach`). + /// - Finally, include any zero-input op as a leaf. + pub fn is_leaf(&self) -> bool { + if self.can_be_parameter() { + return true; + } + match self { + LazyOp::Detach(_) => true, + _ => self.srcs().is_empty(), + } + } + #[track_caller] pub fn check_invariants(&self) { match self { diff --git a/crates/piston-core/src/tensor.rs b/crates/piston-core/src/tensor.rs index 086a8a42..1f4e4a6b 100644 --- a/crates/piston-core/src/tensor.rs +++ b/crates/piston-core/src/tensor.rs @@ -2385,7 +2385,7 @@ impl OpTensor { } pub fn retain_grad(&self) -> Result<()> { - if self.op.can_be_parameter() { + if self.op.is_leaf() { if !self.requires_grad { return Err(anyhow::anyhow!( "can't retain_grad on Tensor that has requires_grad=false" @@ -2399,6 +2399,10 @@ impl OpTensor { Ok(()) } + pub fn is_leaf(&self) -> bool { + self.op.is_leaf() + } + /// Returns a new tensor detached from the current graph, gradient are not propagated through /// this new node. The storage of this tensor is shared with the initial tensor. /// @@ -3318,6 +3322,10 @@ impl Tensor { self.inner_or_source().requires_grad() } + pub fn is_leaf(&self) -> bool { + self.inner_or_source().is_leaf() + } + pub fn set_sync(&self, src: Self) -> Result<()> { self.inner_or_source() .clone() diff --git a/crates/piston-web/src/tensor.rs b/crates/piston-web/src/tensor.rs index 86a59fde..54bb6b62 100644 --- a/crates/piston-web/src/tensor.rs +++ b/crates/piston-web/src/tensor.rs @@ -277,6 +277,11 @@ impl JsTensor { self.inner().requires_grad() } + #[wasm_bindgen(getter = isLeaf)] + pub fn is_leaf(&self) -> bool { + self.inner().is_leaf() + } + #[wasm_bindgen(getter = retainsGrad)] pub fn retains_grad(&self) -> bool { self.inner().retains_grad() From 17838fc7266f7f1cdf6c1ff0856d3c3e31de7a92 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 21:22:13 -0600 Subject: [PATCH 451/590] Add clone_inner to uniform --- crates/piston-core/src/gpu/uniform.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/piston-core/src/gpu/uniform.rs b/crates/piston-core/src/gpu/uniform.rs index f1ad2893..063f5e49 100644 --- a/crates/piston-core/src/gpu/uniform.rs +++ b/crates/piston-core/src/gpu/uniform.rs @@ -35,6 +35,10 @@ impl CpuUniform { self.0.into_inner() } + pub fn clone_inner(&self) -> Vec { + self.0.as_ref().clone() + } + /// Consumes the CPU repr of the uniform buffer and writes to the GPU. pub(crate) fn into_gpu(self, device: &WgpuDevice) -> Result { let buf = device.create_uniform_init(self); From e5715fce314daeb4d893c1c84079fd4bb201d191 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 21:22:59 -0600 Subject: [PATCH 452/590] make rvec more robust? don't remember --- crates/piston-core/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/piston-core/src/lib.rs b/crates/piston-core/src/lib.rs index 7765069f..c71dab0b 100644 --- a/crates/piston-core/src/lib.rs +++ b/crates/piston-core/src/lib.rs @@ -59,7 +59,7 @@ macro_rules! rvec { $crate::RVec::from_elem($elem, $n) }); ($($x:expr),*$(,)*) => ({ - let count = 0usize $(+ rvec![@one $x])*; + let count = 0usize $(+ $crate::rvec![@one $x])*; #[allow(unused_mut)] let mut vec = $crate::RVec::new(); if count <= vec.inline_size() { From 1261fbf283eb5ddcfcafc7c8b9cf9183f13327ad Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 21:23:20 -0600 Subject: [PATCH 453/590] Add more test cases to cmp --- crates/piston-core/src/ops/cmp.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/piston-core/src/ops/cmp.rs b/crates/piston-core/src/ops/cmp.rs index 86c18660..02fdce6e 100644 --- a/crates/piston-core/src/ops/cmp.rs +++ b/crates/piston-core/src/ops/cmp.rs @@ -492,13 +492,13 @@ def {kn}(a, scalar): Ok(()) } - #[proptest(cases = 16)] + #[proptest(cases = 32)] fn test_binary(prob: BinaryProblem) { let device = Device::request_device(DeviceRequest::GPU).unwrap(); run_cmp_trial(prob, device).unwrap(); } - #[proptest(cases = 16)] + #[proptest(cases = 32)] fn test_scalar(prob: CmpScalarProblem) { let device = Device::request_device(DeviceRequest::GPU).unwrap(); run_cmp_scalar_trial(prob, device).unwrap(); From 10ef55de0a53a06fa613c48f2c45c5d55cc5091d Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 21:24:06 -0600 Subject: [PATCH 454/590] Remove a log debug call --- crates/piston-core/src/gpu/pools/dynamic_resource_pool.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/piston-core/src/gpu/pools/dynamic_resource_pool.rs b/crates/piston-core/src/gpu/pools/dynamic_resource_pool.rs index 2e50165c..eeceb1f9 100644 --- a/crates/piston-core/src/gpu/pools/dynamic_resource_pool.rs +++ b/crates/piston-core/src/gpu/pools/dynamic_resource_pool.rs @@ -183,7 +183,6 @@ where continue; }; update_stats(&desc); - log::debug!("Dropping resource {desc:?}"); destructor(&removed_resource); } } From 0fa896db4f024b8ee4eacbe2ca6f154fa713f227 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 21:24:55 -0600 Subject: [PATCH 455/590] underscore unused debug arg --- crates/piston-core/src/op.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/piston-core/src/op.rs b/crates/piston-core/src/op.rs index 73712b11..5331a9f9 100644 --- a/crates/piston-core/src/op.rs +++ b/crates/piston-core/src/op.rs @@ -834,7 +834,7 @@ pub trait GPUOperation: Operation { gpu_compile_key: &ComputeCompileKey<'a>, device: &'a WgpuDevice, // TODO(vinhowe): We should remove this - debug: bool, + _debug: bool, ) -> Result { let ComputeCompileKey { dst, From e9c4ae8797731a743d2298dd9108f76ebddf2ca1 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 21:25:17 -0600 Subject: [PATCH 456/590] Support i32 gather --- crates/piston-core/src/ops/gather.rs | 34 ++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/crates/piston-core/src/ops/gather.rs b/crates/piston-core/src/ops/gather.rs index 37a3118a..d5f02b0c 100644 --- a/crates/piston-core/src/ops/gather.rs +++ b/crates/piston-core/src/ops/gather.rs @@ -205,6 +205,9 @@ impl Kernel for GatherKernels { (DType::F16, KernelElement::Vec4) => { self.render::>(inplace, dst, workgroup_size) } + (DType::I32, KernelElement::Scalar) => { + self.render::>(inplace, dst, workgroup_size) + } _ => Err(OperationError::CompileError(format!( "Unsupported dtype {:?} or kernel element {:?}", inner.src.dtype(), @@ -257,6 +260,30 @@ def gather(src, ids, dim): ground.all_close(&ours, 1e-5, 1e-5).unwrap(); } + fn run_gather_i32_trial(problem: GatherProblem, device: Device) { + let GatherProblem { B, M, N, dim } = problem; + + let src = randint(0, 1000, (B, M, N), Default::default()).unwrap(); + + // Create the shape for ids tensor + let mut ids_shape = rvec![B, M, N]; + ids_shape[dim] = 1; + let ids = randint(0, src.shape()[dim] as i32, ids_shape, Default::default()).unwrap(); + + let ground = ground_truth(&src, &ids, dim).unwrap(); + + let src_gpu = src.to(&device).unwrap(); + let ids_gpu = ids.to(&device).unwrap(); + + let result = src_gpu.gather(dim, ids_gpu).unwrap(); + + let ours = result.to(&Device::CPU).unwrap(); + // Compare in float space for equality + let ground_f32 = ground.cast(DType::F32).unwrap(); + let ours_f32 = ours.cast(DType::F32).unwrap(); + ground_f32.all_close(&ours_f32, 0.0f32, 0.0f32).unwrap(); + } + #[derive(Arbitrary, Debug)] struct GatherProblem { #[strategy(1..=3usize)] @@ -278,6 +305,13 @@ def gather(src, ids, dim): run_gather_trial(prob, device); } + #[proptest(cases = 6)] + fn test_gather_i32(prob: GatherProblem) { + let _ = env_logger::builder().is_test(true).try_init(); + let device = Device::request_device(DeviceRequest::GPU).unwrap(); + run_gather_i32_trial(prob, device); + } + #[derive(Arbitrary, Debug)] struct GatherBackwardProblem { #[strategy(1..=3usize)] From e7a0df80e030d472de455b7aa9b5262200298fc0 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 21:27:17 -0600 Subject: [PATCH 457/590] Fix where op! --- crates/piston-core/src/ops/where_cond.rs | 8 ++++---- crates/piston-core/src/tensor.rs | 7 +------ 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/crates/piston-core/src/ops/where_cond.rs b/crates/piston-core/src/ops/where_cond.rs index 3ed680b4..240e5a19 100644 --- a/crates/piston-core/src/ops/where_cond.rs +++ b/crates/piston-core/src/ops/where_cond.rs @@ -372,10 +372,10 @@ impl KernelRenderable for WhereCondKernels { let apply = if inplace { wgsl! { let val = A[index]; - A[index] = select('on_true_expr, 'on_false_expr, val != 'kernel_element_str(0)); + A[index] = select('on_false_expr, 'on_true_expr, val != 'kernel_element_str(0)); } } else { - wgsl! { Y[index] = select('on_true_expr, 'on_false_expr, A[index] != 'kernel_element_str(0)); } + wgsl! { Y[index] = select('on_false_expr, 'on_true_expr, A[index] != 'kernel_element_str(0)); } }; kernel_builder.write_main(apply); Ok(kernel_builder.build()?) @@ -394,7 +394,7 @@ mod tests { let prg = r#" import torch def where_cond(a, b, c): - return torch.where(torch.from_numpy(a) == 0, torch.from_numpy(b), torch.from_numpy(c)).numpy() + return torch.where(torch.from_numpy(a) != 0, torch.from_numpy(b), torch.from_numpy(c)).numpy() "#; run_py_prg(prg.to_string(), &[a, b, c], &[], b.dtype()) } @@ -403,7 +403,7 @@ def where_cond(a, b, c): let prg = r#" import torch def where_cond_scalar(a, b, scalar): - return torch.where(torch.from_numpy(a) == 0, torch.from_numpy(b), scalar).numpy() + return torch.where(torch.from_numpy(a) != 0, torch.from_numpy(b), scalar).numpy() "#; run_py_prg(prg.to_string(), &[a, b], &[&scalar], b.dtype()) } diff --git a/crates/piston-core/src/tensor.rs b/crates/piston-core/src/tensor.rs index 1f4e4a6b..a9c6eeaf 100644 --- a/crates/piston-core/src/tensor.rs +++ b/crates/piston-core/src/tensor.rs @@ -1378,13 +1378,8 @@ pub fn where_cond>( on_false: T, ) -> Result { let device = condition.device().clone(); - // let (input, other) = crate::promoted_cast(input, other)?; let (input, condition, on_false) = crate::promoted_cast_ternary(input, condition, on_false)?; - let where_cond = WhereCond::new( - condition, - TensorTypeOrScalarEnum::Tensor(input), - on_false.into(), - ); + let where_cond = WhereCond::new(condition, TensorTypeOrScalarEnum::Tensor(input), on_false); let new_view = where_cond.compute_view()?; OpTensor::lazy(LazyOp::WhereCond(where_cond), new_view, device, false) } From aecf7bb83a1333c7bfe7a0483649fccc8ef16ccf Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 21:30:33 -0600 Subject: [PATCH 458/590] Add chunk and split --- crates/piston-core/src/tensor.rs | 75 ++++++++++++++++++++++++++++++++ crates/piston-web/src/tensor.rs | 41 +++++++++++++++++ packages/web/src/globals.ts | 6 +++ packages/web/src/wasm.ts | 2 + 4 files changed, 124 insertions(+) diff --git a/crates/piston-core/src/tensor.rs b/crates/piston-core/src/tensor.rs index a9c6eeaf..529b670b 100644 --- a/crates/piston-core/src/tensor.rs +++ b/crates/piston-core/src/tensor.rs @@ -1092,6 +1092,81 @@ pub fn slice>(input: OpTensor, ranges: &[D]) -> OpTensor::lazy(op, out_view, device, false) } +pub enum SplitArg { + SplitSize(usize), + Sizes(RVec), +} + +/// Splits the tensor along dimension `dim`. When given a `SplitArg::SplitSize(n)`, +/// the tensor is split into chunks of size `n` (last chunk may be smaller). When +/// given `SplitArg::Sizes(sizes)`, it is split according to the explicit sizes. +#[tensor_op(variants = [function, method])] +pub fn split(input: OpTensor, arg: SplitArg, dim: D) -> Result> { + let dim = dim.to_index(input.shape(), "split")?; + let dim_len = input.shape()[dim]; + + match arg { + SplitArg::SplitSize(split_size) => { + if split_size == 0 { + anyhow::bail!("split: split_size must be > 0"); + } + if dim_len == 0 { + return Ok(rvec![]); + } + let mut start = 0usize; + let mut outputs: RVec = RVec::with_capacity(dim_len.div_ceil(split_size)); + while start < dim_len { + let len = (dim_len - start).min(split_size); + outputs.push(narrow_kernel(input.clone(), dim, start, len)?); + start += len; + } + Ok(outputs) + } + SplitArg::Sizes(sizes) => { + let total: usize = sizes.iter().sum(); + if total != dim_len { + anyhow::bail!( + "split_with_sizes: sizes {:?} must sum to dim length {} (dim {})", + sizes, + dim_len, + dim + ); + } + let mut start = 0usize; + let mut outputs: RVec = RVec::with_capacity(sizes.len()); + for &len in sizes.iter() { + outputs.push(narrow_kernel(input.clone(), dim, start, len)?); + start += len; + } + Ok(outputs) + } + } +} + +/// Splits the tensor into `chunks` parts along dimension `dim`. +/// The first `dim_len % chunks` chunks will have size `ceil(dim_len / chunks)`, +/// and the remainder will have size `floor(dim_len / chunks)`. Zero-length chunks +/// are produced if `chunks > dim_len`. +#[tensor_op(variants = [function, method])] +pub fn chunk(input: OpTensor, chunks: usize, dim: D) -> Result> { + let dim = dim.to_index(input.shape(), "chunk")?; + let dim_len = input.shape()[dim]; + + if chunks == 0 { + anyhow::bail!("chunk: chunks must be > 0"); + } + + let base = dim_len / chunks; + let rem = dim_len % chunks; + + // Build explicit sizes and reuse split_with_sizes implementation + let mut sizes = RVec::with_capacity(chunks); + for i in 0..chunks { + sizes.push(base + ((i < rem) as usize)); + } + split_kernel(input, SplitArg::Sizes(sizes), dim) +} + /// Returns a new tensor that is a narrowed version of the input, the dimension `dim` /// ranges from `start` to `start + len`. /// This calls `slice` internally. diff --git a/crates/piston-web/src/tensor.rs b/crates/piston-web/src/tensor.rs index 54bb6b62..e355914c 100644 --- a/crates/piston-web/src/tensor.rs +++ b/crates/piston-web/src/tensor.rs @@ -611,6 +611,47 @@ pub fn slice( input.slice(&ranges) } +#[js_tensor_web_op(name = Split, variants = [method, function])] +pub fn split( + input: Tensor, + #[op(unchecked_type = "number | number[]", name = "splitSizeOrSections")] + split_size_or_sections: JsValue, + #[op(default = 0)] dim: Dim, +) -> Result, JsError> { + let arg = if let Some(n) = split_size_or_sections.as_f64() { + piston::SplitArg::SplitSize(n as usize) + } else if split_size_or_sections.is_object() && split_size_or_sections.is_array() { + let arr = split_size_or_sections + .dyn_into::() + .map_err(|_| JsError::new("split sections must be an array"))?; + let sizes = arr + .iter() + .map(|v| { + v.as_f64() + .map(|f| f as usize) + .ok_or_else(|| JsError::new("sections must be numbers")) + }) + .collect::, JsError>>()?; + piston::SplitArg::Sizes(sizes.into()) + } else { + return Err(JsError::new( + "split requires a number or an array of numbers", + )); + }; + let parts = piston::split(input, arg, dim).map_err(|e| e.into_js_error())?; + Ok::<_, JsError>(parts.into_iter().collect::>()) +} + +#[js_tensor_web_op(name = Chunk, variants = [method, function])] +pub fn chunk( + input: Tensor, + chunks: usize, + #[op(default = 0)] dim: Dim, +) -> Result, JsError> { + let parts = piston::chunk(input, chunks, dim).map_err(|e| e.into_js_error())?; + Ok::<_, JsError>(parts.into_iter().collect::>()) +} + #[js_tensor_web_op(name = View, variants = [method])] pub fn view(input: Tensor, shape: ShapeWithOneHole) -> JsTensorResult {} diff --git a/packages/web/src/globals.ts b/packages/web/src/globals.ts index e68b1600..d14ef7f9 100644 --- a/packages/web/src/globals.ts +++ b/packages/web/src/globals.ts @@ -15,6 +15,7 @@ import { bernoulli as bernoulli_wasm, cat as cat_wasm, ceil as ceil_wasm, + chunk as chunk_wasm, cos as cos_wasm, cpu_wasm, Device, @@ -56,6 +57,7 @@ import { sigmoid as sigmoid_wasm, silu as silu_wasm, sin as sin_wasm, + split as split_wasm, sqrt as sqrt_wasm, square as square_wasm, stack as stack_wasm, @@ -133,6 +135,8 @@ export let isnan: typeof isnan_wasm; export let isinf: typeof isinf_wasm; export let oneHot: typeof oneHot_wasm; export let topk: typeof topk_wasm; +export let split: typeof split_wasm; +export let chunk: typeof chunk_wasm; export let tensor: { ( @@ -320,6 +324,8 @@ export async function initGlobals() { oneHot = wrapWithLibTensor(oneHot_wasm); topk = wrapWithLibTensorArray(topk_wasm); bernoulli = wrapWithLibTensor(bernoulli_wasm); + split = wrapWithLibTensorArray(split_wasm); + chunk = wrapWithLibTensorArray(chunk_wasm); zeros = wrapWithParam(wrapWithLibTensor(zeros_wasm)); zerosLike = wrapWithParam(wrapWithLibTensor(zerosLike_wasm)); ones = wrapWithParam(wrapWithLibTensor(ones_wasm)); diff --git a/packages/web/src/wasm.ts b/packages/web/src/wasm.ts index 45d0b60c..10204647 100644 --- a/packages/web/src/wasm.ts +++ b/packages/web/src/wasm.ts @@ -13,6 +13,7 @@ export { bernoulli, cat, ceil, + chunk, cos, cpu as cpu_wasm, Device, @@ -60,6 +61,7 @@ export { sigmoid, silu, sin, + split, sqrt, square, stack, From acaff89df2ea84ec8d9ea165421277b3889b04e9 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 21:33:33 -0600 Subject: [PATCH 459/590] Expose strong count to web API --- crates/piston-core/src/tensor.rs | 6 +++++- crates/piston-web/src/tensor.rs | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/crates/piston-core/src/tensor.rs b/crates/piston-core/src/tensor.rs index 529b670b..81c41102 100644 --- a/crates/piston-core/src/tensor.rs +++ b/crates/piston-core/src/tensor.rs @@ -206,7 +206,7 @@ impl OpTensor { Ok(value) } - pub(crate) fn strong_count(&self) -> usize { + pub fn strong_count(&self) -> usize { Arc::strong_count(&self.inner) } @@ -3413,6 +3413,10 @@ impl Tensor { pub fn num_bytes(&self) -> usize { self.inner_or_source().num_bytes() } + + pub fn strong_count(&self) -> usize { + self.inner_or_source().strong_count() + } } impl Tensor { diff --git a/crates/piston-web/src/tensor.rs b/crates/piston-web/src/tensor.rs index e355914c..ed09ed1c 100644 --- a/crates/piston-web/src/tensor.rs +++ b/crates/piston-web/src/tensor.rs @@ -140,6 +140,11 @@ impl JsTensor { self.inner().id().0 } + #[wasm_bindgen(getter = __pistonStrongCount)] + pub fn __piston_strong_count(&self) -> usize { + self.inner().strong_count() + } + // - Skipping storage_view pub fn dim(&self) -> usize { From 9cfa9e9fcf88e4111093e26b0ff174697e0bed45 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 22:26:41 -0600 Subject: [PATCH 460/590] Add clamp --- crates/piston-core/src/tensor.rs | 30 ++++++++++++++++++++++++++++++ crates/piston-web/src/tensor.rs | 8 ++++++++ 2 files changed, 38 insertions(+) diff --git a/crates/piston-core/src/tensor.rs b/crates/piston-core/src/tensor.rs index 81c41102..bd3212ae 100644 --- a/crates/piston-core/src/tensor.rs +++ b/crates/piston-core/src/tensor.rs @@ -1459,6 +1459,36 @@ pub fn where_cond>( OpTensor::lazy(LazyOp::WhereCond(where_cond), new_view, device, false) } +#[tensor_op(variants = [function, method, method_inplace])] +pub fn clamp, T2: TensorTypeOrScalar>( + input: OpTensor, + min: Option, + max: Option, +) -> Result { + if min.is_none() && max.is_none() { + return Ok(input); + } + + let min_val = min + .as_ref() + .map(|t| t.map_tensor(|t| t.into())) + .transpose()? + .unwrap_or(TensorTypeOrScalarEnum::Scalar(0.0)); + let max_val = max + .as_ref() + .map(|t| t.map_tensor(|t| t.into())) + .transpose()? + .unwrap_or(TensorTypeOrScalarEnum::Scalar(1.0)); + let (mut input, min_cast, max_cast) = crate::promoted_cast_ternary(input, min_val, max_val)?; + if min.is_some() { + input = where_cond_kernel(input.clone(), ge_kernel(input, min_cast.clone())?, min_cast)?; + } + if max.is_some() { + input = where_cond_kernel(input.clone(), le_kernel(input, max_cast.clone())?, max_cast)?; + } + Ok(input) +} + #[tensor_op(variants = [function, method, method_inplace])] pub fn scatter_add( input: OpTensor, diff --git a/crates/piston-web/src/tensor.rs b/crates/piston-web/src/tensor.rs index ed09ed1c..528644f2 100644 --- a/crates/piston-web/src/tensor.rs +++ b/crates/piston-web/src/tensor.rs @@ -709,6 +709,14 @@ pub fn index_write(input: Tensor, src: Tensor, write_start: Dims) -> JsTensorRes #[js_tensor_web_op(name = Where, variants = [method, function], js_name = "where")] pub fn where_cond(input: Tensor, condition: Tensor, on_false: TensorOrScalar) -> JsTensorResult {} +#[js_tensor_web_op(name = Clamp, variants = [method, method_inplace, function])] +pub fn clamp( + input: Tensor, + min: Option, + max: Option, +) -> JsTensorResult { +} + #[js_tensor_web_op(name = ScatterAdd, variants = [method, function])] pub fn scatter_add(input: Tensor, indices: Tensor, source: Tensor, dim: Dim) -> JsTensorResult {} From 6353e961b40c4b1d683a3bdb45c631abb71204bd Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 22:27:07 -0600 Subject: [PATCH 461/590] Add debug, clone to TensorOptions --- crates/piston-core/src/tensor.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/piston-core/src/tensor.rs b/crates/piston-core/src/tensor.rs index bd3212ae..153274f1 100644 --- a/crates/piston-core/src/tensor.rs +++ b/crates/piston-core/src/tensor.rs @@ -1678,6 +1678,7 @@ pub fn topk( /// let tensor = full([3, 3], 5.0, TensorOptions::new() /// .requires_grad(true))?; /// ``` +#[derive(Debug, Clone)] pub struct TensorOptions { pub device: Option, pub dtype: Option, From edbcc7cb88b5ff72752a67efebb8046fd92a9ca7 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 22:29:28 -0600 Subject: [PATCH 462/590] Update some of lazy graph executor API --- .../buffer_allocator/lazy_graph_executor.rs | 2 +- crates/piston-web/src/device.rs | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/crates/piston-core/src/gpu/buffer_allocator/lazy_graph_executor.rs b/crates/piston-core/src/gpu/buffer_allocator/lazy_graph_executor.rs index a8b19ffb..dd9da155 100644 --- a/crates/piston-core/src/gpu/buffer_allocator/lazy_graph_executor.rs +++ b/crates/piston-core/src/gpu/buffer_allocator/lazy_graph_executor.rs @@ -475,7 +475,7 @@ impl LazyGraphExecutor { &post_order, &output_tensors, &compile_keys, - do_shared_realloc, + self.shared_object_allocation_enabled, gpu_device, )?) } else { diff --git a/crates/piston-web/src/device.rs b/crates/piston-web/src/device.rs index 98021014..8fa136c6 100644 --- a/crates/piston-web/src/device.rs +++ b/crates/piston-web/src/device.rs @@ -45,6 +45,24 @@ impl JsDevice { self.inner.try_gpu().unwrap().begin_pass(0); } + #[wasm_bindgen(js_name = setSharedObjectAllocationEnabled)] + pub fn set_shared_object_allocation_enabled(&self, enabled: bool) { + self.inner + .try_gpu() + .unwrap() + .set_shared_object_allocation_enabled(enabled); + } + + #[wasm_bindgen(js_name = setCachingEnabled)] + pub fn set_caching_enabled(&self, enabled: bool) { + self.inner.try_gpu().unwrap().set_caching_enabled(enabled); + } + + #[wasm_bindgen(js_name = setInplaceSupport)] + pub fn set_inplace_support(&self, enabled: bool) { + self.inner.try_gpu().unwrap().set_inplace_support(enabled); + } + #[wasm_bindgen(getter)] pub fn name(&self) -> String { match self.inner { From f4a5bd82a147c1db4b34b442791e5bab878fd68d Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 22:31:23 -0600 Subject: [PATCH 463/590] Improve implacing somehow don't remember --- .../buffer_allocator/lazy_graph_executor.rs | 66 +++++++++++-------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/crates/piston-core/src/gpu/buffer_allocator/lazy_graph_executor.rs b/crates/piston-core/src/gpu/buffer_allocator/lazy_graph_executor.rs index dd9da155..e8454d47 100644 --- a/crates/piston-core/src/gpu/buffer_allocator/lazy_graph_executor.rs +++ b/crates/piston-core/src/gpu/buffer_allocator/lazy_graph_executor.rs @@ -303,7 +303,7 @@ impl LazyGraphExecutor { if tensor.is_inplace() { true - } else if !self.inplace_support { + } else { // TODO(vinhowe): This really is horrible; we should be able to just // check if the op supports inplace. match tensor.op() { @@ -318,35 +318,43 @@ impl LazyGraphExecutor { | LazyOp::IndexAdd(_) ) } - _ => false, + _ => { + // vinhowe: we need to check if the src is a parameter, because + // we can't inplace parameters unless we've disabled gradient tracking. + if !tensor.op().supports_inplace() + || to_modify_src.requires_grad() + { + false + } else { + // Typical references: + // 1. Its original consumer. Whatever scope it was created + // in. + // 2. `tensors`, as passed into this method, if it wasn't + // resolved and we upgraded its weak reference. This + // happens when we do a sync of live tensors, say, in an + // optimizer step, but a one-off sync won't do this. This + // is why we have the optional `owned_tensors`. + // If these two are the only references, then we can + // inplace. Usually, additional references include, not in + // any particular order: + // 3. The optimizer, if it is a parameter. We'll also check + // if the src is a parameter. + // 4+ Any other Tensor consumers in the post-order. If it's + // not a parameter, these are the references we're + // concerned about messing with. + // + // If we own a copy, 2, otherwise 1. + let expected_strong = owned_tensors + .as_ref() + .and_then(|ot| { + ot.contains(&to_modify_src.id()).then_some(2) + }) + .unwrap_or(1); + + to_modify_src.strong_count() <= expected_strong + } + } } - } else if !tensor.op().supports_inplace() - // vinhowe: we need to check if the src is a parameter, because we can't - // inplace parameters unless we've disabled gradient tracking. - || to_modify_src.requires_grad() - { - false - } else { - // Typical references: - // 1. Its original consumer. Whatever scope it was created in. - // 2. `tensors`, as passed into this method, if it wasn't resolved and we - // upgraded its weak reference. This happens when we do a sync of live - // tensors, say, in an optimizer step, but a one-off sync won't do this. - // This is why we have the optional `owned_tensors`. - // If these two are the only references, then we can inplace. Usually, - // additional references include, not in any particular order: - // 3. The optimizer, if it is a parameter. We'll also check if the src is a - // parameter. - // 4+ Any other Tensor consumers in the post-order. If it's not a parameter, - // these are the references we're concerned about messing with. - // - // If we own a copy, 2, otherwise 1. - let expected_strong = owned_tensors - .as_ref() - .and_then(|ot| ot.contains(&to_modify_src.id()).then_some(2)) - .unwrap_or(1); - - to_modify_src.strong_count() == expected_strong } } None => false, From dad82f0a9445365e200f1abd1d8138ff93048fa6 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 22:59:04 -0600 Subject: [PATCH 464/590] JsTensorOrScalar from JsValue --- crates/piston-web/src/tensor.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/crates/piston-web/src/tensor.rs b/crates/piston-web/src/tensor.rs index 528644f2..f7dcac72 100644 --- a/crates/piston-web/src/tensor.rs +++ b/crates/piston-web/src/tensor.rs @@ -1034,6 +1034,7 @@ impl JsTensor { } #[derive(Clone)] +#[wasm_bindgen(js_name = TensorOrScalar)] struct JsTensorOrScalar { inner: JsValue, } @@ -1053,6 +1054,22 @@ impl TensorTypeOrScalar for JsTensorOrScalar { } } +impl TryFrom for JsTensorOrScalar { + type Error = JsError; + fn try_from(value: JsValue) -> Result { + if let Some(scalar) = value.as_f64() { + Ok(JsTensorOrScalar { + inner: JsValue::from_f64(scalar), + }) + } else { + // We don't do any extra validation here; just hope it'll work + Ok(JsTensorOrScalar { + inner: value.clone(), + }) + } + } +} + fn js_value_to_norm_ord(value: JsValue) -> Result, JsError> { if value.is_undefined() || value.is_null() { // Handle undefined or null values From b742bc2d06483de6c49d416eb9be65d63d23d4b5 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 23:01:28 -0600 Subject: [PATCH 465/590] Make randint i32 by default --- crates/piston-core/src/tensor.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/piston-core/src/tensor.rs b/crates/piston-core/src/tensor.rs index 153274f1..493b03f3 100644 --- a/crates/piston-core/src/tensor.rs +++ b/crates/piston-core/src/tensor.rs @@ -1729,6 +1729,10 @@ impl TensorOptions { self.dtype.unwrap_or(DType::F32) } + pub fn dtype_or(&self, dtype: DType) -> DType { + self.dtype.unwrap_or(dtype) + } + pub fn requires_grad_or_default(&self) -> bool { self.requires_grad.unwrap_or(false) } @@ -1922,7 +1926,7 @@ pub fn randint>( ) -> Result { let shape = shape.into(); let device = options.device_or_default(); - let dtype = options.dtype_or_default(); + let dtype = options.dtype_or(DType::I32); dispatch_int_types!( dtype, From 316fecb02d26847c1464dabc5854b5f872e259b5 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 23:19:18 -0600 Subject: [PATCH 466/590] Remove num_resources from pipeline layout pool --- crates/piston-core/src/gpu/pools/pipeline_layout_pool.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crates/piston-core/src/gpu/pools/pipeline_layout_pool.rs b/crates/piston-core/src/gpu/pools/pipeline_layout_pool.rs index 31de2dc6..a19f492b 100644 --- a/crates/piston-core/src/gpu/pools/pipeline_layout_pool.rs +++ b/crates/piston-core/src/gpu/pools/pipeline_layout_pool.rs @@ -54,8 +54,4 @@ impl PipelineLayoutPool { ) -> StaticResourcePoolReadLockAccessor<'_, PipelineLayoutHandle, wgpu::PipelineLayout> { self.inner.resources() } - - pub fn num_resources(&self) -> usize { - self.inner.num_resources() - } } From b2e71d1d665547668f7b81194e8c78f66cb93d7d Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 23:27:04 -0600 Subject: [PATCH 467/590] Add initialization methods --- crates/piston-core/src/tensor.rs | 357 +++++++++++++++++++++++++++++++ crates/piston-web/src/tensor.rs | 104 ++++++++- packages/web/src/globals.ts | 33 +++ packages/web/src/wasm.ts | 11 + 4 files changed, 503 insertions(+), 2 deletions(-) diff --git a/crates/piston-core/src/tensor.rs b/crates/piston-core/src/tensor.rs index 493b03f3..292b8a0d 100644 --- a/crates/piston-core/src/tensor.rs +++ b/crates/piston-core/src/tensor.rs @@ -2125,6 +2125,262 @@ pub fn rand>( ) } +#[derive(Debug, Clone)] +pub enum NormalOrUniform { + Normal, + Uniform, +} + +#[derive(Debug, Clone)] +pub enum KaimingFan { + FanIn, + FanOut, +} + +#[derive(Debug, Clone)] +pub enum KaimingNonLinearity { + ReLU, + Linear, + Sigmoid, + Tanh, + SELU, + LeakyReLU, + ExplicitGain(f32), +} + +pub fn calculate_fan_in_out(shape: &Shape) -> (usize, usize) { + let dims = shape.to_vec(); + let receptive_field_size: usize = dims.iter().skip(2).product(); + + let fan_in = if dims.len() < 2 { + 1 + } else { + dims[1] * receptive_field_size + }; + + let fan_out = if dims.is_empty() { + 1 + } else { + dims[0] * receptive_field_size + }; + + (fan_in, fan_out) +} + +pub fn xavier_impl>( + shape: S, + dist: NormalOrUniform, + gain: Option, + options: TensorOptions, +) -> Result { + let shape = shape.into(); + let dtype = options.dtype_or_default(); + let gain = gain.unwrap_or(1.0); + + if !dtype.is_float() { + return Err(anyhow::anyhow!( + "xavier_init: dtype {:?} is not floating point", + dtype + )); + } + + let (fan_in, fan_out) = calculate_fan_in_out(&shape); + let denom = (fan_in + fan_out) as f32; + + match dist { + NormalOrUniform::Uniform => { + let bound = (6.0f32).sqrt() * gain / denom.sqrt(); + rand_kernel(shape, Some(-bound), Some(bound), options) + } + NormalOrUniform::Normal => { + let std = (2.0f32 / denom).sqrt() * gain; + randn_kernel(shape, Some(0.0), Some(std), options) + } + } +} + +#[tensor_op(variants = [function])] +pub fn xavier_uniform>( + shape: S, + gain: Option, + options: TensorOptions, +) -> Result { + xavier_impl(shape, NormalOrUniform::Uniform, gain, options) +} + +#[tensor_op(variants = [function])] +pub fn xavier_normal>( + shape: S, + gain: Option, + options: TensorOptions, +) -> Result { + xavier_impl(shape, NormalOrUniform::Normal, gain, options) +} + +pub fn kaiming_impl>( + shape: S, + dist: NormalOrUniform, + fan: KaimingFan, + non_linearity: KaimingNonLinearity, + a: Option, + options: TensorOptions, +) -> Result { + let shape = shape.into(); + let dtype = options.dtype_or_default(); + + if !dtype.is_float() { + return Err(anyhow::anyhow!( + "kaiming_init: dtype {:?} is not floating point", + dtype + )); + } + + // Compute params on CPU and fall back to rand/randn + let (fan_in, fan_out) = calculate_fan_in_out(&shape); + let fan_val = match fan { + KaimingFan::FanIn => fan_in as f32, + KaimingFan::FanOut => fan_out as f32, + }; + let gain = match non_linearity { + KaimingNonLinearity::ReLU => 2f32.sqrt(), + KaimingNonLinearity::Tanh => 5.0 / 3.0, + KaimingNonLinearity::Linear | KaimingNonLinearity::Sigmoid => 1.0, + KaimingNonLinearity::SELU => 0.75, + KaimingNonLinearity::LeakyReLU => (2. / (1. + a.unwrap_or(0.01).powi(2))).sqrt(), + KaimingNonLinearity::ExplicitGain(g) => g, + }; + let std = gain / fan_val.sqrt(); + + match dist { + NormalOrUniform::Uniform => { + let bound = std * 3f32.sqrt(); + rand_kernel(shape, Some(-bound), Some(bound), options) + } + NormalOrUniform::Normal => randn_kernel(shape, Some(0.0), Some(std), options), + } +} + +#[tensor_op(variants = [function])] +pub fn kaiming_uniform>( + shape: S, + a: Option, + mode: KaimingFan, + nonlinearity: KaimingNonLinearity, + options: TensorOptions, +) -> Result { + kaiming_impl( + shape, + NormalOrUniform::Uniform, + mode, + nonlinearity, + a, + options, + ) +} + +#[tensor_op(variants = [function])] +pub fn kaiming_normal>( + shape: S, + a: Option, + mode: KaimingFan, + nonlinearity: KaimingNonLinearity, + options: TensorOptions, +) -> Result { + kaiming_impl( + shape, + NormalOrUniform::Normal, + mode, + nonlinearity, + a, + options, + ) +} + +#[tensor_op(variants = [function])] +pub fn orthogonal>( + shape: S, + gain: Option, + options: TensorOptions, +) -> Result { + const EPS: f32 = 1e-6; + const STEPS: usize = 6; + let gain = gain.unwrap_or(1.0); + + let shape = shape.into(); + let dtype = options.dtype_or_default(); + + if !dtype.is_float() { + return Err(anyhow::anyhow!( + "orthogonal: dtype {:?} is not floating point", + dtype + )); + } + + if shape.dim() != 2 { + anyhow::bail!( + "orthogonal: expected 2D shape, got {}D: {:?}", + shape.dim(), + shape + ); + } + + let m = shape[0]; + let n = shape[1]; + + // A ~ N(0, 1) + let a = randn_kernel(shape.clone(), Some(0.0), Some(1.0), options.clone())?; + + // Build symmetric Gram matrix G and set identity size + let (g, d): (OpTensor, usize) = if m >= n { + // G = A^T A (n x n) + let g_raw = matmul_kernel(a.clone(), a.clone(), true, false)?; + let g_sym = mul_kernel::<_, _, OpTensor>( + add_kernel(g_raw.clone(), t_kernel(g_raw.clone())?)?, + 0.5, + )?; + (g_sym, n) + } else { + // G = A A^T (m x m) + let g_raw = matmul_kernel(a.clone(), a.clone(), false, true)?; + let g_sym = mul_kernel::<_, _, OpTensor>( + add_kernel(g_raw.clone(), t_kernel(g_raw.clone())?)?, + 0.5, + )?; + (g_sym, m) + }; + + // mu = ||G||_F + eps + let mu = add_kernel::<_, _, OpTensor>( + norm_kernel(g.clone(), Some(NormOrd::Frobenius), crate::AllDims, false)?, + EPS, + )?; + + let i = eye_kernel(d, None, options)?; + + // X0 = I / sqrt(mu) + let inv_sqrt_mu = recip_kernel(sqrt_kernel(mu.clone())?)?; + let mut x = mul_kernel(i.clone(), inv_sqrt_mu)?; + + // Newton–Schulz iteration (6 iters) + // X <- 0.5 * X @ (3I - G @ (X @ X)) + let three_i = mul_kernel::<_, _, OpTensor>(i.clone(), 3.0)?; + for _ in 0..STEPS { + let x2 = matmul_kernel(x.clone(), x.clone(), false, false)?; + let gx2 = matmul_kernel(g.clone(), x2, false, false)?; + let inner = sub_kernel(three_i.clone(), gx2)?; + let x_inner = matmul_kernel(x.clone(), inner, false, false)?; + x = mul_kernel::<_, _, OpTensor>(x_inner, 0.5)?; + } + + // Orthonormalize columns (m >= n) or rows (m < n) + let q = if m >= n { + matmul_kernel(a.clone(), x.clone(), false, false)? + } else { + matmul_kernel(x.clone(), a.clone(), false, false)? + }; + + mul_kernel::<_, _, OpTensor>(q, gain) +} #[tensor_op(variants = [function])] pub fn eye(n: usize, m: Option, options: TensorOptions) -> Result { @@ -3366,6 +3622,107 @@ impl Tensor { } } +// We special-case a bunch of initialization methods here to have an initialization API more like +// PyTorch. +pub fn init_uniform_(tensor: &Tensor, low: Option, high: Option) -> Result { + Ok(tensor.wrap_inplace_untracked(rand_kernel( + tensor.shape(), + low, + high, + TensorOptions::from_tensor(tensor), + )?)) +} + +pub fn init_normal_(tensor: &Tensor, mean: Option, std: Option) -> Result { + Ok(tensor.wrap_inplace_untracked(randn_kernel( + tensor.shape(), + mean, + std, + TensorOptions::from_tensor(tensor), + )?)) +} + +pub fn init_constant_(tensor: &Tensor, value: f32) -> Result { + Ok(tensor.wrap_inplace_untracked(full_kernel( + tensor.shape(), + value, + TensorOptions::from_tensor(tensor), + )?)) +} + +pub fn init_ones_(tensor: &Tensor) -> Result { + Ok(tensor.wrap_inplace_untracked(ones_kernel( + tensor.shape(), + TensorOptions::from_tensor(tensor), + )?)) +} + +pub fn init_zeros_(tensor: &Tensor) -> Result { + Ok(tensor.wrap_inplace_untracked(zeros_kernel( + tensor.shape(), + TensorOptions::from_tensor(tensor), + )?)) +} + +pub fn init_eye_(tensor: &Tensor) -> Result { + let (n, m) = tensor.shape().dims2()?; + Ok(tensor.wrap_inplace_untracked(eye_kernel(n, Some(m), TensorOptions::from_tensor(tensor))?)) +} + +pub fn init_xavier_uniform_(tensor: &Tensor, gain: Option) -> Result { + Ok(tensor.wrap_inplace_untracked(xavier_uniform_kernel( + tensor.shape(), + gain, + TensorOptions::from_tensor(tensor), + )?)) +} + +pub fn init_xavier_normal_(tensor: &Tensor, gain: Option) -> Result { + Ok(tensor.wrap_inplace_untracked(xavier_normal_kernel( + tensor.shape(), + gain, + TensorOptions::from_tensor(tensor), + )?)) +} + +pub fn init_kaiming_uniform_( + tensor: &Tensor, + a: Option, + mode: KaimingFan, + nonlinearity: KaimingNonLinearity, +) -> Result { + Ok(tensor.wrap_inplace_untracked(kaiming_uniform_kernel( + tensor.shape(), + a, + mode, + nonlinearity, + TensorOptions::from_tensor(tensor), + )?)) +} + +pub fn init_kaiming_normal_( + tensor: &Tensor, + a: Option, + mode: KaimingFan, + nonlinearity: KaimingNonLinearity, +) -> Result { + Ok(tensor.wrap_inplace_untracked(kaiming_normal_kernel( + tensor.shape(), + a, + mode, + nonlinearity, + TensorOptions::from_tensor(tensor), + )?)) +} + +pub fn init_orthogonal_(tensor: &Tensor, gain: Option) -> Result { + Ok(tensor.wrap_inplace_untracked(orthogonal_kernel( + tensor.shape(), + gain, + TensorOptions::from_tensor(tensor), + )?)) +} + impl std::fmt::Debug for Tensor { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{:?}", self.inner_or_source()) diff --git a/crates/piston-web/src/tensor.rs b/crates/piston-web/src/tensor.rs index f7dcac72..8144ffa0 100644 --- a/crates/piston-web/src/tensor.rs +++ b/crates/piston-web/src/tensor.rs @@ -10,8 +10,8 @@ use half::f16; use js_sys::{Array, Function, Object, Reflect}; use parking_lot::RwLock; use piston::{ - AllDims, DType, Dim, IrScalarValue, IrValue, LazyOp, NormOrd, Storage, Tensor, TensorId, - TensorTypeOrScalar, TensorTypeOrScalarEnum, + AllDims, DType, Dim, IrScalarValue, IrValue, KaimingFan, KaimingNonLinearity, LazyOp, NormOrd, + NormalOrUniform, Storage, Tensor, TensorId, TensorTypeOrScalar, TensorTypeOrScalarEnum, }; use piston_macros::js_tensor_web_op; use std::cell::RefCell; @@ -1020,6 +1020,106 @@ pub fn ones(shape: Shape, options: TensorOptions) -> anyhow::Result { piston::ones(shape, options) } +#[js_tensor_web_op(name = "InitUniform", variants = [function])] +pub fn init_uniform_(input: Tensor, low: Option, high: Option) -> anyhow::Result { + piston::init_uniform_(&input, low, high) +} + +#[js_tensor_web_op(name = "InitNormal", variants = [function])] +pub fn init_normal_(input: Tensor, mean: Option, std: Option) -> anyhow::Result { + piston::init_normal_(&input, mean, std) +} + +#[js_tensor_web_op(name = "InitConstant", variants = [function])] +pub fn init_constant_(input: Tensor, value: f32) -> anyhow::Result { + piston::init_constant_(&input, value) +} + +#[js_tensor_web_op(name = "InitOnes", variants = [function])] +pub fn init_ones_(input: Tensor) -> anyhow::Result { + piston::init_ones_(&input) +} + +#[js_tensor_web_op(name = "InitZeros", variants = [function])] +pub fn init_zeros_(input: Tensor) -> anyhow::Result { + piston::init_zeros_(&input) +} + +#[js_tensor_web_op(name = "InitEye", variants = [function])] +pub fn init_eye_(input: Tensor) -> anyhow::Result { + piston::init_eye_(&input) +} + +#[js_tensor_web_op(name = "InitXavierUniform", variants = [function])] +pub fn init_xavier_uniform_(input: Tensor, gain: Option) -> anyhow::Result { + piston::init_xavier_uniform_(&input, gain) +} + +#[js_tensor_web_op(name = "InitXavierNormal", variants = [function])] +pub fn init_xavier_normal_(input: Tensor, gain: Option) -> anyhow::Result { + piston::init_xavier_normal_(&input, gain) +} + +fn init_kaiming_impl( + input: Tensor, + dist: NormalOrUniform, + a: Option, + mode: String, + nonlinearity: String, +) -> anyhow::Result { + let mode = match mode.as_ref() { + "fan_in" => KaimingFan::FanIn, + "fan_out" => KaimingFan::FanOut, + _ => return Err(anyhow::anyhow!("Invalid mode: {:?}", mode)), + }; + let nonlinearity = match nonlinearity.as_ref() { + "relu" => KaimingNonLinearity::ReLU, + "linear" => KaimingNonLinearity::Linear, + "sigmoid" => KaimingNonLinearity::Sigmoid, + "tanh" => KaimingNonLinearity::Tanh, + "selu" => KaimingNonLinearity::SELU, + "leaky_relu" => KaimingNonLinearity::LeakyReLU, + _ => return Err(anyhow::anyhow!("Invalid nonlinearity: {:?}", nonlinearity)), + }; + match dist { + NormalOrUniform::Uniform => piston::init_kaiming_uniform_(&input, a, mode, nonlinearity), + NormalOrUniform::Normal => piston::init_kaiming_normal_(&input, a, mode, nonlinearity), + } +} + +#[js_tensor_web_op(name = "InitKaimingUniform", variants = [function])] +pub fn init_kaiming_uniform_( + input: Tensor, + a: Option, + #[op(default = "fan_in".to_string(), unchecked_type = "'fan_in' | 'fan_out'")] mode: String, + #[op( + default = "leaky_relu".to_string(), + unchecked_type = "'relu' | 'linear' | 'sigmoid' | 'tanh' | 'selu' | 'leaky_relu'" + )] + nonlinearity: String, +) -> anyhow::Result { + init_kaiming_impl(input, NormalOrUniform::Uniform, a, mode, nonlinearity) +} + +#[js_tensor_web_op(name = "InitKaimingNormal", variants = [function])] +pub fn init_kaiming_normal_( + input: Tensor, + a: Option, + #[op(default = "fan_in".to_string(), unchecked_type = "'fan_in' | 'fan_out'")] mode: String, + #[op( + default = "leaky_relu".to_string(), + unchecked_type = "'relu' | 'linear' | 'sigmoid' | 'tanh' | 'selu' | 'leaky_relu'" + )] + nonlinearity: String, +) -> anyhow::Result { + init_kaiming_impl(input, NormalOrUniform::Normal, a, mode, nonlinearity) +} + +#[js_tensor_web_op(name = "InitOrthogonal", variants = [function])] +pub fn init_orthogonal_(input: Tensor, gain: Option) -> anyhow::Result { + piston::init_orthogonal_(&input, gain) +} + #[wasm_bindgen(js_class = Tensor)] impl JsTensor { #[wasm_bindgen(js_name = _clone)] diff --git a/packages/web/src/globals.ts b/packages/web/src/globals.ts index d14ef7f9..d8b52f1d 100644 --- a/packages/web/src/globals.ts +++ b/packages/web/src/globals.ts @@ -32,6 +32,17 @@ import { gelu as gelu_wasm, gpu_wasm, gt as gt_wasm, + initConstant as initConstant_wasm, + initEye as initEye_wasm, + initKaimingNormal as initKaimingNormal_wasm, + initKaimingUniform as initKaimingUniform_wasm, + initNormal as initNormal_wasm, + initOnes as initOnes_wasm, + initOrthogonal as initOrthogonal_wasm, + initUniform as initUniform_wasm, + initXavierNormal as initXavierNormal_wasm, + initXavierUniform as initXavierUniform_wasm, + initZeros as initZeros_wasm, int32_wasm, isinf as isinf_wasm, isnan as isnan_wasm, @@ -137,6 +148,17 @@ export let oneHot: typeof oneHot_wasm; export let topk: typeof topk_wasm; export let split: typeof split_wasm; export let chunk: typeof chunk_wasm; +export let initConstant_: typeof initConstant_wasm; +export let initEye_: typeof initEye_wasm; +export let initKaimingNormal_: typeof initKaimingNormal_wasm; +export let initKaimingUniform_: typeof initKaimingUniform_wasm; +export let initNormal_: typeof initNormal_wasm; +export let initOnes_: typeof initOnes_wasm; +export let initOrthogonal_: typeof initOrthogonal_wasm; +export let initUniform_: typeof initUniform_wasm; +export let initXavierNormal_: typeof initXavierNormal_wasm; +export let initXavierUniform_: typeof initXavierUniform_wasm; +export let initZeros_: typeof initZeros_wasm; export let tensor: { ( @@ -330,6 +352,17 @@ export async function initGlobals() { zerosLike = wrapWithParam(wrapWithLibTensor(zerosLike_wasm)); ones = wrapWithParam(wrapWithLibTensor(ones_wasm)); onesLike = wrapWithParam(wrapWithLibTensor(onesLike_wasm)); + initConstant_ = wrapWithLibTensor(initConstant_wasm); + initEye_ = wrapWithLibTensor(initEye_wasm); + initKaimingNormal_ = wrapWithLibTensor(initKaimingNormal_wasm); + initKaimingUniform_ = wrapWithLibTensor(initKaimingUniform_wasm); + initNormal_ = wrapWithLibTensor(initNormal_wasm); + initOnes_ = wrapWithLibTensor(initOnes_wasm); + initOrthogonal_ = wrapWithLibTensor(initOrthogonal_wasm); + initUniform_ = wrapWithLibTensor(initUniform_wasm); + initXavierNormal_ = wrapWithLibTensor(initXavierNormal_wasm); + initXavierUniform_ = wrapWithLibTensor(initXavierUniform_wasm); + initZeros_ = wrapWithLibTensor(initZeros_wasm); tensor = wrapWithParam( wrapWithLibTensor( ( diff --git a/packages/web/src/wasm.ts b/packages/web/src/wasm.ts index 10204647..fd37a9b0 100644 --- a/packages/web/src/wasm.ts +++ b/packages/web/src/wasm.ts @@ -31,6 +31,17 @@ export { gelu, gpu as gpu_wasm, gt, + initConstant, + initEye, + initKaimingNormal, + initKaimingUniform, + initNormal, + initOnes, + initOrthogonal, + initUniform, + initXavierNormal, + initXavierUniform, + initZeros, int32 as int32_wasm, isinf, isnan, From 9b370ce84eafff0c6dade3b11056327fdb6022dc Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 23:29:09 -0600 Subject: [PATCH 468/590] Remove AllDims import embedding --- crates/piston-nn/src/embedding.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/piston-nn/src/embedding.rs b/crates/piston-nn/src/embedding.rs index ba713fc0..fb189537 100644 --- a/crates/piston-nn/src/embedding.rs +++ b/crates/piston-nn/src/embedding.rs @@ -1,6 +1,6 @@ use crate::Module; use maybe_async::maybe_async; -use piston::{AllDims, D, DType, Shape, Tensor}; +use piston::{D, DType, Shape, Tensor}; use piston_macros::scoped_module; /// # Embedding From 2ca1950fb7a6cc9aa1b719751125be54c77e6bab Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 23:31:20 -0600 Subject: [PATCH 469/590] Fix comment --- crates/piston-core/src/tensor.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/piston-core/src/tensor.rs b/crates/piston-core/src/tensor.rs index 292b8a0d..003ae3ed 100644 --- a/crates/piston-core/src/tensor.rs +++ b/crates/piston-core/src/tensor.rs @@ -3170,8 +3170,8 @@ impl OpTensor { let buffer = gpu_device.get_or_create_buffer( &BufferDescriptor { size: self.num_bytes() as _, - // If we want the values in CPU land, we'll eventually have to - // copy again to a buffer with a usage of COPY_DST | MAP_READ. + // If we want the values in CPU land, we'll eventually have to copy again to a + // buffer with a usage of COPY_DST | MAP_READ. usage: wgpu::BufferUsages::standard(), mapped_at_creation: false, }, From 8a0ff89f483a204b6b59b51391200cc8cc0daba3 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 23:45:11 -0600 Subject: [PATCH 470/590] Add generated op name support --- crates/piston-macros/src/js_tensor_web_op.rs | 23 +++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/crates/piston-macros/src/js_tensor_web_op.rs b/crates/piston-macros/src/js_tensor_web_op.rs index 822cc676..efaec446 100644 --- a/crates/piston-macros/src/js_tensor_web_op.rs +++ b/crates/piston-macros/src/js_tensor_web_op.rs @@ -379,6 +379,7 @@ struct OpParamMeta { keyword: bool, raw_js: bool, ts_type_override: Option, + name: Option, } fn parse_op_param_meta(attrs: &[Attribute]) -> SynResult { @@ -400,6 +401,10 @@ fn parse_op_param_meta(attrs: &[Attribute]) -> SynResult { let lit: LitStr = nested.value()?.parse()?; meta.ts_type_override = Some(lit.value()); Ok(()) + } else if nested.path.is_ident("name") { + let lit: LitStr = nested.value()?.parse()?; + meta.name = Some(lit.value()); + Ok(()) } else { Err(nested.error("Unknown op(...) attribute key")) } @@ -659,7 +664,11 @@ fn build_options_struct( } let name_ident = p.ident.clone(); let name_str = name_ident.to_string(); - let camel = name_str.to_lower_camel_case(); + let camel = if let Some(ref custom) = p.meta.name { + custom.clone() + } else { + name_str.to_lower_camel_case() + }; // Required fields remain non-Option; optional fields become Option<...> let is_optional = p.is_option || p.meta.default_expr.is_some(); // Avoid Option> in the generated options struct by mapping Option to T first @@ -1460,7 +1469,11 @@ pub fn process_js_tensor_web_op(attr: JsTensorWebOpAttr, item: ItemFn) -> SynRes } let p = ¶ms[param_idx]; let name_str = p.ident.to_string(); - let camel = name_str.to_lower_camel_case(); + let camel = if let Some(ref custom) = p.meta.name { + custom.clone() + } else { + name_str.to_lower_camel_case() + }; let mut attrs_tokens: Vec = Vec::new(); // js_name override when camelCase differs if camel != name_str { @@ -1509,7 +1522,11 @@ pub fn process_js_tensor_web_op(attr: JsTensorWebOpAttr, item: ItemFn) -> SynRes let name = p.ident.clone(); // camelCase rename for param if different let name_str = name.to_string(); - let camel = name_str.to_lower_camel_case(); + let camel = if let Some(ref custom) = p.meta.name { + custom.clone() + } else { + name_str.to_lower_camel_case() + }; let mut attrs = Vec::::new(); if camel != name_str { let jsname_lit = syn::LitStr::new(&camel, Span::call_site()); From 26aadb5815cf41744533eb8ba2198846135c0110 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 23:47:29 -0600 Subject: [PATCH 471/590] Allow returning Vec --- crates/piston-macros/src/js_tensor_web_op.rs | 59 ++++++++-- crates/piston-macros/src/ops.rs | 117 +++++++++++++++++-- 2 files changed, 161 insertions(+), 15 deletions(-) diff --git a/crates/piston-macros/src/js_tensor_web_op.rs b/crates/piston-macros/src/js_tensor_web_op.rs index efaec446..76c6fa37 100644 --- a/crates/piston-macros/src/js_tensor_web_op.rs +++ b/crates/piston-macros/src/js_tensor_web_op.rs @@ -548,6 +548,27 @@ fn is_return_type_option(func: &ItemFn) -> bool { false } +fn is_return_type_vec_tensor(func: &ItemFn) -> bool { + use syn::ReturnType; + let mut ty_opt: Option<&Type> = None; + if let ReturnType::Type(_, ty) = &func.sig.output { + ty_opt = Some(ty); + } + if let Some(ty) = ty_opt { + // Unwrap Result -> Ok + let ok_ty = if let Some((inner, _)) = get_inner_type(ty, "Result") { + inner + } else { + ty + }; + // Vec + if let Some((inner, _)) = get_inner_type(ok_ty, "Vec") { + return is_type(inner, "JsTensor") || is_type(inner, "Tensor"); + } + } + false +} + fn param_unchecked_ts_type(ty: &Type, optional: bool) -> String { let (kind, meta) = classify_param_kind(ty); let is_optional = optional || meta.optional; @@ -1017,13 +1038,25 @@ fn build_options_parsing( typed_fields.push(quote! { #field_ident }); } ParamKind::TensorOrScalar => { - prelude.push(quote! { - let __js = crate::js_util::to_option(#opts_ident.#field_ident.clone()).unwrap_or(wasm_bindgen::JsValue::UNDEFINED); - let #field_ident: crate::tensor::JsTensorOrScalar = crate::tensor::JsTensorOrScalar { inner: __js }; - }); - typed_fields.push( - quote! { #field_ident.tensor_or_scalar().map_err(|e| e.into_js_error())? }, - ); + if p.is_option { + prelude.push(quote! { + let #field_ident: Option = #opts_ident.#field_ident.clone(); + }); + typed_fields.push(quote! { + #field_ident + .map(|v| v.tensor_or_scalar()) + .transpose() + .map_err(|e| e.into_js_error())? + }); + } else { + prelude.push(quote! { + let __js = crate::js_util::to_option(#opts_ident.#field_ident.clone()).unwrap_or(wasm_bindgen::JsValue::UNDEFINED); + let #field_ident: crate::tensor::JsTensorOrScalar = crate::tensor::JsTensorOrScalar { inner: __js }; + }); + typed_fields.push( + quote! { #field_ident.tensor_or_scalar().map_err(|e| e.into_js_error())? }, + ); + } } ParamKind::Shape => { if p.is_option || p.meta.default_expr.is_some() { @@ -1236,8 +1269,11 @@ pub fn process_js_tensor_web_op(attr: JsTensorWebOpAttr, item: ItemFn) -> SynRes quote! {} }; let output_is_option = is_return_type_option(&item); + let output_is_vec_tensor = is_return_type_vec_tensor(&item); let unchecked_ret_ts = if output_is_option { syn::LitStr::new("Tensor | undefined", Span::call_site()) + } else if output_is_vec_tensor { + syn::LitStr::new("Tensor[]", Span::call_site()) } else { syn::LitStr::new("Tensor", Span::call_site()) }; @@ -1417,6 +1453,15 @@ pub fn process_js_tensor_web_op(attr: JsTensorWebOpAttr, item: ItemFn) -> SynRes .map(|js_t| js_t.into()) .unwrap_or(wasm_bindgen::JsValue::UNDEFINED)) } + } else if output_is_vec_tensor { + quote! { + let result = #invocation_tokens.map_err(|e| e.into_js_error())?; + let array = js_sys::Array::new(); + for t in result.into_iter() { + array.push(&crate::tensor::JsTensor::new(t).into()); + } + Ok(array.into()) + } } else { quote! { let result = #invocation_tokens.map_err(|e| e.into_js_error())?; diff --git a/crates/piston-macros/src/ops.rs b/crates/piston-macros/src/ops.rs index af2b6f11..05f7fd76 100644 --- a/crates/piston-macros/src/ops.rs +++ b/crates/piston-macros/src/ops.rs @@ -1,5 +1,5 @@ use proc_macro2::{Span, TokenStream as TokenStream2}; -use quote::quote; +use quote::{ToTokens, quote}; use std::collections::HashMap; use syn::{ Attribute, Error, Expr, FnArg, GenericParam, Ident, ItemFn, Pat, PatIdent, PatType, Path, @@ -699,7 +699,7 @@ fn generate_function_variant( } func.sig.inputs = new_inputs; - // Replace OpTensor with Tensor in return type + // Replace OpTensor with Tensor in return type, including containers like RVec or Vec if let ReturnType::Type(_, ref mut ty) = func.sig.output { replace_op_tensor_with_tensor(ty, false); } @@ -737,9 +737,59 @@ fn generate_function_variant( call_args.push(quote!(#param_name)); } - func.block = syn::parse_quote!({ - #kernel_name(#(#call_args),*).map(Tensor::wrap) - }); + // Detect if return type is Result> where Container is RVec or Vec + let returns_container_of_optensor = (|| { + if let ReturnType::Type(_, ty) = &original_fn.sig.output { + // Unwrap Result to Ok + let ok_ty: &Type = if let Type::Path(tp) = &**ty + && tp.path.segments.last().map(|s| s.ident.to_string()) + == Some("Result".to_string()) + && matches!( + tp.path.segments.last().unwrap().arguments, + syn::PathArguments::AngleBracketed(_) + ) { + if let syn::PathArguments::AngleBracketed(args) = + &tp.path.segments.last().unwrap().arguments + { + if let Some(syn::GenericArgument::Type(t)) = args.args.first() { + t + } else { + ty + } + } else { + ty + } + } else { + ty + }; + + if let Type::Path(tp2) = ok_ty + && let Some(seg) = tp2.path.segments.last() + { + let name = seg.ident.to_string(); + if (name == "RVec" || name == "Vec") + && let syn::PathArguments::AngleBracketed(args) = &seg.arguments + && let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() + && let Type::Path(inner_path) = inner_ty + { + return is_op_tensor_path(&inner_path.path); + } + } + } + // Fallback: string match on signature tokens + let sig = original_fn.sig.output.to_token_stream().to_string(); + sig.contains("RVec < OpTensor >") || sig.contains("Vec < OpTensor >") + })(); + + if returns_container_of_optensor { + func.block = syn::parse_quote!({ + #kernel_name(#(#call_args),*).map(|v| v.into_iter().map(Tensor::wrap).collect()) + }); + } else { + func.block = syn::parse_quote!({ + #kernel_name(#(#call_args),*).map(Tensor::wrap) + }); + } Ok(func) } @@ -800,9 +850,60 @@ fn generate_method_variant( replace_op_tensor_with_tensor(ty, true); } - let method = quote! { - pub fn #method_name #generics (self, #(#method_params),*) #return_type { - #kernel_name(#(#call_args),*).map(Self::wrap) + // Detect if return type is Result> where Container is RVec or Vec + let returns_container_of_optensor = (|| { + if let ReturnType::Type(_, ty) = &original_fn.sig.output { + // Unwrap Result to Ok + let ok_ty: &Type = if let Type::Path(tp) = &**ty + && tp.path.segments.last().map(|s| s.ident.to_string()) + == Some("Result".to_string()) + && matches!( + tp.path.segments.last().unwrap().arguments, + syn::PathArguments::AngleBracketed(_) + ) { + if let syn::PathArguments::AngleBracketed(args) = + &tp.path.segments.last().unwrap().arguments + { + if let Some(syn::GenericArgument::Type(t)) = args.args.first() { + t + } else { + ty + } + } else { + ty + } + } else { + ty + }; + + if let Type::Path(tp2) = ok_ty + && let Some(seg) = tp2.path.segments.last() + { + let name = seg.ident.to_string(); + if (name == "RVec" || name == "Vec") + && let syn::PathArguments::AngleBracketed(args) = &seg.arguments + && let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() + && let Type::Path(inner_path) = inner_ty + { + return is_op_tensor_path(&inner_path.path); + } + } + } + let sig = original_fn.sig.output.to_token_stream().to_string(); + sig.contains("RVec < OpTensor >") || sig.contains("Vec < OpTensor >") + })(); + + let method = if returns_container_of_optensor { + quote! { + pub fn #method_name #generics (self, #(#method_params),*) #return_type { + #kernel_name(#(#call_args),*).map(|v| v.into_iter().map(Self::wrap).collect()) + } + } + } else { + quote! { + pub fn #method_name #generics (self, #(#method_params),*) #return_type { + #kernel_name(#(#call_args),*).map(Self::wrap) + } } }; From 6e6ee22266f5350129cd93b2603af0ade5a0d68d Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Fri, 24 Oct 2025 23:48:09 -0600 Subject: [PATCH 472/590] Add "2" to generated norm ord --- crates/piston-macros/src/js_tensor_web_op.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/piston-macros/src/js_tensor_web_op.rs b/crates/piston-macros/src/js_tensor_web_op.rs index 76c6fa37..5e6e8e30 100644 --- a/crates/piston-macros/src/js_tensor_web_op.rs +++ b/crates/piston-macros/src/js_tensor_web_op.rs @@ -576,7 +576,9 @@ fn param_unchecked_ts_type(ty: &Type, optional: bool) -> String { ParamKind::Tensor => "Tensor".to_string(), ParamKind::VecTensor => "Tensor[]".to_string(), ParamKind::TensorOrScalar => "Tensor | number".to_string(), - ParamKind::NormOrd => "'fro' | 'inf' | '-inf' | '0' | '1' | '-1' | number".to_string(), + ParamKind::NormOrd => { + "'fro' | 'inf' | '-inf' | '0' | '1' | '-1' | '2' | number".to_string() + } ParamKind::Shape => "Uint32Array | number[] | number".to_string(), ParamKind::ShapeWithOneHole | ParamKind::Dims => { "Int32Array | number[] | number".to_string() From a49886795d99daebec2ba75b4e20a597f7a1344c Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 25 Oct 2025 00:00:14 -0600 Subject: [PATCH 473/590] Serialize to ArrayBuffer instead of URL --- crates/piston-web/src/serialization.rs | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/crates/piston-web/src/serialization.rs b/crates/piston-web/src/serialization.rs index 03a47cb4..59ae141b 100644 --- a/crates/piston-web/src/serialization.rs +++ b/crates/piston-web/src/serialization.rs @@ -4,22 +4,18 @@ use crate::error::IntoJsError; use crate::tensor::JsTensor; use futures::lock::Mutex; use js_sys::{Object, Reflect}; -use piston::Device; -use piston::OpTensor; -use piston::Tensor; +use piston::{Device, OpTensor, Tensor}; use wasm_bindgen::JsValue; use wasm_bindgen::prelude::*; use wasm_bindgen_futures::JsFuture; use wasm_bindgen_futures::future_to_promise; -use web_sys::Blob; -use web_sys::Url; -/// Saves tensors to a safetensors format and returns a URL for download +/// Saves tensors to a safetensors format and returns an ArrayBuffer /// /// The input is expected to be a JavaScript object mapping tensor ID strings to /// either JsTensor or JsParameter objects. #[wasm_bindgen] -pub async fn save(data: JsValue) -> anyhow::Result { +pub async fn save(data: JsValue) -> Result { // Check if the data is a JavaScript object if !data.is_object() { return Err(JsError::new( @@ -90,13 +86,6 @@ pub async fn save(data: JsValue) -> anyhow::Result { let serialized = safetensors::tensor::serialize(data_ref.iter().map(|(k, v)| (k, v)), &None) .map_err(|e| JsError::new(&format!("Failed to serialize tensors: {e}")))?; - // Create a Uint8Array from the serialized data - let uint8_array = js_sys::Uint8Array::from(&serialized[..]); - let blob = Blob::new_with_u8_array_sequence(&js_sys::Array::of1(&JsValue::from(uint8_array))) - .map_err(|e| e.into_js_error())?; - - // Create a download URL - let url = Url::create_object_url_with_blob(&blob).map_err(|e| e.into_js_error())?; - - Ok(url) + // Create an ArrayBuffer from the serialized data + Ok(js_sys::Uint8Array::from(&serialized[..])) } From 1bf15b58736f625958b380ac6b14a715797cf241 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 25 Oct 2025 00:04:39 -0600 Subject: [PATCH 474/590] Save extra JSON in serialization --- crates/piston-web/src/serialization.rs | 13 ++++++++++--- packages/web/src/serialization.ts | 3 ++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/crates/piston-web/src/serialization.rs b/crates/piston-web/src/serialization.rs index 59ae141b..0c5bb7f6 100644 --- a/crates/piston-web/src/serialization.rs +++ b/crates/piston-web/src/serialization.rs @@ -5,6 +5,7 @@ use crate::tensor::JsTensor; use futures::lock::Mutex; use js_sys::{Object, Reflect}; use piston::{Device, OpTensor, Tensor}; +use std::collections::HashMap; use wasm_bindgen::JsValue; use wasm_bindgen::prelude::*; use wasm_bindgen_futures::JsFuture; @@ -15,7 +16,10 @@ use wasm_bindgen_futures::future_to_promise; /// The input is expected to be a JavaScript object mapping tensor ID strings to /// either JsTensor or JsParameter objects. #[wasm_bindgen] -pub async fn save(data: JsValue) -> Result { +pub async fn save( + data: JsValue, + #[wasm_bindgen(js_name = configSerialized)] config_serialized: Option, +) -> Result { // Check if the data is a JavaScript object if !data.is_object() { return Err(JsError::new( @@ -83,8 +87,11 @@ pub async fn save(data: JsValue) -> Result { .collect::>(); // Serialize to safetensors format - let serialized = safetensors::tensor::serialize(data_ref.iter().map(|(k, v)| (k, v)), &None) - .map_err(|e| JsError::new(&format!("Failed to serialize tensors: {e}")))?; + let serialized = safetensors::tensor::serialize( + data_ref.iter().map(|(k, v)| (k, v)), + &config_serialized.map(|s| HashMap::from([("piston_extra".to_string(), s)])), + ) + .map_err(|e| JsError::new(&format!("Failed to serialize tensors: {e}")))?; // Create an ArrayBuffer from the serialized data Ok(js_sys::Uint8Array::from(&serialized[..])) diff --git a/packages/web/src/serialization.ts b/packages/web/src/serialization.ts index 2b4d712e..64acaba6 100644 --- a/packages/web/src/serialization.ts +++ b/packages/web/src/serialization.ts @@ -2,7 +2,7 @@ import { Buffer, Parameter } from "@/nn/parameter"; import { Tensor } from "@/tensor"; import { save_wasm } from "@/wasm"; -export function save(stateDict: Record) { +export function save(stateDict: Record, extra?: unknown) { return save_wasm( Object.fromEntries( Object.entries(stateDict).map(([name, paramOrBuffer]) => { @@ -16,5 +16,6 @@ export function save(stateDict: Record) { ); }), ), + extra ? JSON.stringify(extra) : undefined, ); } From cdd8393ed51f21f5fb98fc5ce6bbaa3b87307dcd Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 25 Oct 2025 00:06:16 -0600 Subject: [PATCH 475/590] Add basic deserialization --- crates/piston-web/src/serialization.rs | 84 +++++++++++++++++++++++++- packages/web/src/core.ts | 2 +- packages/web/src/serialization.ts | 15 ++++- packages/web/src/wasm.ts | 1 + 4 files changed, 99 insertions(+), 3 deletions(-) diff --git a/crates/piston-web/src/serialization.rs b/crates/piston-web/src/serialization.rs index 0c5bb7f6..c8c1d8cc 100644 --- a/crates/piston-web/src/serialization.rs +++ b/crates/piston-web/src/serialization.rs @@ -1,10 +1,11 @@ use std::sync::Arc; +use crate::device::JsDevice; use crate::error::IntoJsError; use crate::tensor::JsTensor; use futures::lock::Mutex; use js_sys::{Object, Reflect}; -use piston::{Device, OpTensor, Tensor}; +use piston::{DType, Device, OpTensor, Tensor, TensorOptions}; use std::collections::HashMap; use wasm_bindgen::JsValue; use wasm_bindgen::prelude::*; @@ -96,3 +97,84 @@ pub async fn save( // Create an ArrayBuffer from the serialized data Ok(js_sys::Uint8Array::from(&serialized[..])) } + +/// Loads tensors from a safetensors byte buffer and returns a JavaScript object +/// mapping tensor names to Tensor objects. +#[wasm_bindgen(unchecked_return_type = "{ state: Record; extra?: unknown }")] +pub fn load( + bytes: js_sys::Uint8Array, + #[wasm_bindgen(js_name = mapDevice)] map_device: Option, +) -> Result { + let device = map_device.unwrap_or_else(|| JsDevice { inner: Device::CPU }); + + // Copy bytes from the Uint8Array into a Rust Vec + let mut buf = vec![0u8; bytes.length() as usize]; + bytes.copy_to(&mut buf[..]); + + // Deserialize safetensors from bytes + let st = safetensors::SafeTensors::deserialize(&buf) + .map_err(|e| JsError::new(&format!("Failed to deserialize safetensors: {e}")))?; + + let obj = js_sys::Object::new(); + + for name in st.names() { + let view = st + .tensor(&name) + .map_err(|e| JsError::new(&format!("Failed to read tensor '{name}': {e}")))?; + + // Map safetensors dtype to piston dtype + let dtype = match view.dtype() { + safetensors::Dtype::F32 => DType::F32, + safetensors::Dtype::F16 => DType::F16, + safetensors::Dtype::BF16 => DType::BF16, + safetensors::Dtype::I32 => DType::I32, + safetensors::Dtype::U32 => DType::U32, + other => { + return Err(JsError::new(&format!( + "Unsupported dtype in safetensors: {:?}", + other + ))); + } + }; + + let shape = view.shape(); + let data = view.data(); + + let tensor = Tensor::from_bytes( + data, + shape, + TensorOptions::new() + .dtype(dtype) + .device(device.inner.clone()) + .requires_grad(false), + ) + .map_err(|e| JsError::new(&format!("Failed to construct tensor '{name}': {e}")))?; + + // Wrap as a JS-visible Tensor + let js_tensor = JsTensor::new(tensor); + js_sys::Reflect::set(&obj, &JsValue::from_str(&name), &JsValue::from(js_tensor)) + .map_err(|e| JsError::new(&format!("Failed to set property '{name}': {e:?}")))?; + } + + // Build return object: { state, extra } + let out = js_sys::Object::new(); + + // state + js_sys::Reflect::set(&out, &JsValue::from_str("state"), &obj) + .map_err(|e| JsError::new(&format!("Failed to set state on return object: {e:?}")))?; + + // extra (parse JSON if present) + let extra_js = safetensors::SafeTensors::read_metadata(&buf) + .ok() + .and_then(|(_, meta)| { + meta.metadata().as_ref().and_then(|map| { + map.get("piston_extra") + .and_then(|s| js_sys::JSON::parse(s).ok()) + }) + }) + .unwrap_or(JsValue::UNDEFINED); + js_sys::Reflect::set(&out, &JsValue::from_str("extra"), &extra_js) + .map_err(|e| JsError::new(&format!("Failed to set extra on return object: {e:?}")))?; + + Ok(out.into()) +} diff --git a/packages/web/src/core.ts b/packages/web/src/core.ts index 7d2b732b..4d11c77c 100644 --- a/packages/web/src/core.ts +++ b/packages/web/src/core.ts @@ -50,7 +50,7 @@ export { debugBufferHook } from "@/nn/moduleUtils"; export { Buffer, Parameter } from "@/nn/parameter"; export * as optim from "@/optim"; export * from "@/optim"; -export { save } from "@/serialization"; +export { load, save } from "@/serialization"; export { init }; export { Tensor } from "@/tensor"; export * from "@/utils"; diff --git a/packages/web/src/serialization.ts b/packages/web/src/serialization.ts index 64acaba6..95256ee2 100644 --- a/packages/web/src/serialization.ts +++ b/packages/web/src/serialization.ts @@ -1,6 +1,6 @@ import { Buffer, Parameter } from "@/nn/parameter"; import { Tensor } from "@/tensor"; -import { save_wasm } from "@/wasm"; +import { Device, load_wasm, save_wasm } from "@/wasm"; export function save(stateDict: Record, extra?: unknown) { return save_wasm( @@ -19,3 +19,16 @@ export function save(stateDict: Record, extra?: unkn extra ? JSON.stringify(extra) : undefined, ); } + +export function load( + bytes: Uint8Array, + mapDevice?: Device, +): { state: Record; extra?: unknown } { + const result = load_wasm(bytes, mapDevice?._clone()); + return { + state: Object.fromEntries( + Object.entries(result.state).map(([key, value]) => [key, Tensor._wrap(value)]), + ), + extra: result.extra, + }; +} diff --git a/packages/web/src/wasm.ts b/packages/web/src/wasm.ts index fd37a9b0..4c744c6d 100644 --- a/packages/web/src/wasm.ts +++ b/packages/web/src/wasm.ts @@ -46,6 +46,7 @@ export { isinf, isnan, le, + load as load_wasm, log, logicalAnd, logicalNot, From a323c1a53e2e64d18af8a150cfd4c7cb5ebe6eb6 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 25 Oct 2025 00:19:42 -0600 Subject: [PATCH 476/590] Add mark-step mode (like function mode) --- crates/piston-web/src/device.rs | 8 +- crates/piston-web/src/function.rs | 158 ++++++++++++++++++++++++++++++ packages/web/src/core.ts | 4 +- packages/web/src/function.ts | 21 +++- packages/web/src/wasm.ts | 3 + 5 files changed, 186 insertions(+), 8 deletions(-) diff --git a/crates/piston-web/src/device.rs b/crates/piston-web/src/device.rs index 8fa136c6..5818871e 100644 --- a/crates/piston-web/src/device.rs +++ b/crates/piston-web/src/device.rs @@ -31,13 +31,9 @@ impl JsDevice { #[wasm_bindgen(js_name = markStep)] pub async fn mark_step(&self) -> Result<(), JsValue> { - self.inner - .try_gpu() - .unwrap() - .mark_step() + crate::function::handle_mark_step(self) .await - .map_err(|e| e.to_string())?; - Ok(()) + .map_err(|e| e.into()) } #[wasm_bindgen(js_name = beginPass)] diff --git a/crates/piston-web/src/function.rs b/crates/piston-web/src/function.rs index 5e7ff823..aefdbd6a 100644 --- a/crates/piston-web/src/function.rs +++ b/crates/piston-web/src/function.rs @@ -6,6 +6,7 @@ use std::cell::RefCell; use wasm_bindgen::JsCast; use wasm_bindgen::closure::Closure; use wasm_bindgen::prelude::*; +use wasm_bindgen_futures::{JsFuture, future_to_promise}; use crate::error::IntoJsError; use crate::js_util::is_subclass; @@ -24,6 +25,19 @@ pub fn set_function_mode_constructor(constructor: &Function) { }); } +// MarkStep-mode constructor set from JavaScript +thread_local! { + static MARK_STEP_MODE_CONSTRUCTOR: RefCell> = const { RefCell::new(None) }; +} + +/// Register the base MarkStepMode constructor from JS. +#[wasm_bindgen(js_name = _setMarkStepModeConstructor)] +pub fn set_mark_step_mode_constructor(constructor: &Function) { + MARK_STEP_MODE_CONSTRUCTOR.with(|cell| { + cell.borrow_mut().replace(constructor.clone()); + }); +} + #[inline] fn get_type_of_jsvalue(obj_or_type: &JsValue) -> JsValue { if obj_or_type.is_instance_of::() { @@ -364,3 +378,147 @@ impl Drop for StashFunctionModeGuard { } } } + +/// MarkStep mode: simpler analog to FunctionMode for intercepting mark_step. +#[derive(Clone, Debug)] +pub struct MarkStepMode { + js_mode_obj: JsValue, +} + +thread_local! { + static MARK_STEP_MODE_STACK: RefCell> = const { RefCell::new(Vec::new()) }; +} + +pub fn push_mark_step_mode(mode: MarkStepMode) { + MARK_STEP_MODE_STACK.with(|stack| stack.borrow_mut().push(mode)); +} + +pub fn pop_mark_step_mode() -> Option { + MARK_STEP_MODE_STACK.with(|stack| stack.borrow_mut().pop()) +} + +#[wasm_bindgen(js_name = _pushMarkStepMode)] +pub fn push_mark_step_mode_js(mode_obj: &JsValue) -> Result<(), JsValue> { + if mode_obj.is_undefined() || mode_obj.is_null() { + return Ok(()); + } + push_mark_step_mode(MarkStepMode { + js_mode_obj: mode_obj.clone(), + }); + Ok(()) +} + +#[wasm_bindgen(js_name = _popMarkStepMode)] +pub fn pop_mark_step_mode_js() -> JsValue { + pop_mark_step_mode() + .map(|m| m.js_mode_obj) + .unwrap_or_else(JsValue::undefined) +} + +pub fn is_mark_step_mode_active() -> bool { + MARK_STEP_MODE_STACK.with(|stack| !stack.borrow().is_empty()) +} + +/// Handle mark_step dispatch via the active MarkStepMode if present. +/// If the mode handler returns undefined/null, fall back to the default implementation. +pub async fn handle_mark_step(device: &crate::device::JsDevice) -> Result<(), JsError> { + if !is_mark_step_mode_active() { + // No mode active: run default behavior + device + .inner + .try_gpu() + .unwrap() + .mark_step() + .await + .map_err(|e| JsError::new(&e.to_string()))?; + return Ok(()); + } + + let mut _mode_guard = StashMarkStepModeGuard::new(); + let mode_obj = &_mode_guard + .current_mode + .as_ref() + .expect("No MarkStep mode object") + .js_mode_obj; + + let piston_mark_step_func: Function = + Reflect::get(mode_obj, &JsValue::from_str("_pistonMarkStep")) + .and_then(|v: JsValue| v.dyn_into()) + .map_err(|_| JsError::new("Mode object does not have a _pistonMarkStep method"))?; + + // Create original function closure returning a Promise + let device_inner = std::rc::Rc::new(device.inner.clone()); + let device_inner_for_closure = device_inner.clone(); + let original_fn_closure = Closure::wrap(Box::new(move || -> JsValue { + let device_inner = device_inner_for_closure.clone(); + let fut = async move { + device_inner + .try_gpu() + .unwrap() + .mark_step() + .await + .map_err(|e| JsValue::from_str(&e.to_string()))?; + Ok(JsValue::UNDEFINED) + }; + future_to_promise(fut).unchecked_into::() + }) as Box JsValue>); + // Cast to Function for JS .apply. Keep the Closure alive in scope until we finish. + let original_fn: Function = original_fn_closure + .as_ref() + .unchecked_ref::() + .clone(); + + let ret = piston_mark_step_func + .apply(mode_obj, &Array::of1(&original_fn)) + .map_err(|e| e.into_js_error())?; + + if let Some(promise) = ret.dyn_ref::() { + // Keep mode disabled until promise settles, then restore it + let saved_mode = _mode_guard.current_mode.take(); + JsFuture::from(promise.clone()) + .await + .map_err(|e| e.into_js_error())?; + if let Some(mode) = saved_mode { + push_mark_step_mode(mode); + } + // original_fn_closure drops here + return Ok(()); + } + + // Immediate value: if undefined/null, fall back to default implementation + if ret.is_undefined() || ret.is_null() { + device + .inner + .try_gpu() + .unwrap() + .mark_step() + .await + .map_err(|e| JsError::new(&e.to_string()))?; + Ok(()) + } else { + // Non-null immediate indicates the handler completed the step + Ok(()) + } +} + +/// RAII guard for temporarily stashing the current MarkStepMode +pub struct StashMarkStepModeGuard { + current_mode: Option, +} + +impl StashMarkStepModeGuard { + pub fn new() -> Self { + let saved_mode = pop_mark_step_mode(); + Self { + current_mode: saved_mode, + } + } +} + +impl Drop for StashMarkStepModeGuard { + fn drop(&mut self) { + if let Some(mode) = self.current_mode.take() { + push_mark_step_mode(mode); + } + } +} diff --git a/packages/web/src/core.ts b/packages/web/src/core.ts index 4d11c77c..a7db4fcd 100644 --- a/packages/web/src/core.ts +++ b/packages/web/src/core.ts @@ -1,11 +1,12 @@ // Import the full wasm module namespace so Rust can reflect on its exports import * as pistonWasmExports from "@piston-ml/piston-web-wasm"; -import { PistonFunctionMode } from "@/function"; +import { PistonFunctionMode, PistonMarkStepMode } from "@/function"; import { initGlobals } from "@/globals"; import { Tensor } from "@/tensor"; import { _setFunctionModeConstructor, + _setMarkStepModeConstructor, _setPistonWebModule, _setTensorConstructor, wasmInit, @@ -37,6 +38,7 @@ async function init(): Promise { // Setup references to JS objects we need from Rust _setPistonWebModule(pistonWasmExports as unknown as object); _setFunctionModeConstructor(PistonFunctionMode); + _setMarkStepModeConstructor(PistonMarkStepMode); _setTensorConstructor(Tensor); await initGlobals(); diff --git a/packages/web/src/function.ts b/packages/web/src/function.ts index 8c56ee40..56a72acc 100644 --- a/packages/web/src/function.ts +++ b/packages/web/src/function.ts @@ -1,4 +1,4 @@ -import { _popFunctionMode, _pushFunctionMode } from "@/wasm"; +import { _popFunctionMode, _popMarkStepMode, _pushFunctionMode, _pushMarkStepMode } from "@/wasm"; export abstract class PistonFunctionMode { constructor() { @@ -42,3 +42,22 @@ export class FunctionModeGuard { _pushFunctionMode(this.mode); } } + +export abstract class PistonMarkStepMode { + constructor() { + _pushMarkStepMode(this); + } + + [Symbol.dispose]() { + _popMarkStepMode(); + } + + // If returns undefined/null, default mark_step is executed. + abstract _pistonMarkStep(original: () => Promise): void | Promise; +} + +export class BasePistonMarkStepMode extends PistonMarkStepMode { + _pistonMarkStep(original: () => Promise): void | Promise { + return original(); + } +} diff --git a/packages/web/src/wasm.ts b/packages/web/src/wasm.ts index 4c744c6d..ae094d85 100644 --- a/packages/web/src/wasm.ts +++ b/packages/web/src/wasm.ts @@ -1,8 +1,11 @@ export { __pistonActiveTensors, _popFunctionMode, + _popMarkStepMode, _pushFunctionMode, + _pushMarkStepMode, _setFunctionModeConstructor, + _setMarkStepModeConstructor, _setPistonWebModule, _setTensorConstructor, abs, From 9eafe45dcafd6a8e5e43f9903a57c9f93934a85a Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 25 Oct 2025 00:25:26 -0600 Subject: [PATCH 477/590] Make function modes more resilient --- crates/piston-web/src/function.rs | 3 +++ packages/web/src/function.ts | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/piston-web/src/function.rs b/crates/piston-web/src/function.rs index aefdbd6a..18eff594 100644 --- a/crates/piston-web/src/function.rs +++ b/crates/piston-web/src/function.rs @@ -313,6 +313,9 @@ pub fn pop_function_mode() -> Option { /// Push a new JS function mode object onto the stack. #[wasm_bindgen(js_name = _pushFunctionMode)] pub fn push_function_mode_js(mode_obj: &JsValue) -> Result<(), JsValue> { + if mode_obj.is_undefined() || mode_obj.is_null() { + return Ok(()); + } push_function_mode(FunctionMode { js_mode_obj: mode_obj.clone(), }); diff --git a/packages/web/src/function.ts b/packages/web/src/function.ts index 56a72acc..c4e1667c 100644 --- a/packages/web/src/function.ts +++ b/packages/web/src/function.ts @@ -39,7 +39,9 @@ export class FunctionModeGuard { } [Symbol.dispose]() { - _pushFunctionMode(this.mode); + if (this.mode) { + _pushFunctionMode(this.mode); + } } } From 025d7080122c7efa37400ce893a5d5646b719ca5 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 25 Oct 2025 00:26:04 -0600 Subject: [PATCH 478/590] Add Symbol.dispose polyfill to function --- packages/web/src/function.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/web/src/function.ts b/packages/web/src/function.ts index c4e1667c..6d62017e 100644 --- a/packages/web/src/function.ts +++ b/packages/web/src/function.ts @@ -1,5 +1,8 @@ import { _popFunctionMode, _popMarkStepMode, _pushFunctionMode, _pushMarkStepMode } from "@/wasm"; +// @ts-expect-error polyfill +Symbol.dispose ||= Symbol.for("Symbol.dispose"); + export abstract class PistonFunctionMode { constructor() { _pushFunctionMode(this); From 331f2ccb2ad019eeaeaa3e2beb2cb3df2e42e59b Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 25 Oct 2025 00:34:35 -0600 Subject: [PATCH 479/590] mark has_self as unused --- crates/piston-macros/src/js_tensor_web_op.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/piston-macros/src/js_tensor_web_op.rs b/crates/piston-macros/src/js_tensor_web_op.rs index 5e6e8e30..b93368c4 100644 --- a/crates/piston-macros/src/js_tensor_web_op.rs +++ b/crates/piston-macros/src/js_tensor_web_op.rs @@ -999,7 +999,7 @@ fn build_options_parsing( params: &[ParamInfo], pack_start: usize, include_tensor_options: bool, - has_self: bool, + _has_self: bool, ) -> (TokenStream2, Vec) { let options_ident = format_ident!("{}Options", op_name_pascal); if pack_start >= params.len() && !include_tensor_options { From 867335de869c176eb3659c0569076f8089f911e6 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 25 Oct 2025 13:16:47 -0600 Subject: [PATCH 480/590] Globals formatting --- packages/web/src/globals.ts | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/packages/web/src/globals.ts b/packages/web/src/globals.ts index d8b52f1d..27a5d8c4 100644 --- a/packages/web/src/globals.ts +++ b/packages/web/src/globals.ts @@ -1,11 +1,6 @@ import { Parameter } from "@/nn/parameter"; import { Tensor } from "@/tensor"; -import { - NestedNumberList, - OptionalShapeConfig, - ShapeType, - TensorOptions, -} from "@/types"; +import { NestedNumberList, OptionalShapeConfig, ShapeType, TensorOptions } from "@/types"; import { abs as abs_wasm, add as add_wasm, @@ -161,10 +156,7 @@ export let initXavierUniform_: typeof initXavierUniform_wasm; export let initZeros_: typeof initZeros_wasm; export let tensor: { - ( - data: CreateTensorData, - config: TensorOptions & OptionalShapeConfig, - ): Parameter; + (data: CreateTensorData, config: TensorOptions & OptionalShapeConfig): Parameter; (data: CreateTensorData, config?: TensorOptions & OptionalShapeConfig): Tensor; }; From 34f7907fa272355219d4bb99aaa709a5bb2d3398 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 25 Oct 2025 13:25:59 -0600 Subject: [PATCH 481/590] Add result to outdated test --- crates/piston-nn/tests/optim.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/piston-nn/tests/optim.rs b/crates/piston-nn/tests/optim.rs index 3767b966..52da5928 100644 --- a/crates/piston-nn/tests/optim.rs +++ b/crates/piston-nn/tests/optim.rs @@ -11,13 +11,13 @@ fn run_linear_regression(optimizer: OptimizerFactory) -> anyhow let _ = env_logger::builder().is_test(true).try_init(); let device = Device::request_device(DeviceRequest::GPU).unwrap(); let w_gen = Tensor::from_data(vec![3f32, 1.], (1, 2), TensorOptions::new())?.to(&device)?; - let b_gen = Tensor::from_data(vec![-2f32], (1, 1), TensorOptions::new()).to(&device)?; + let b_gen = Tensor::from_data(vec![-2f32], (1, 1), TensorOptions::new())?.to(&device)?; let r#gen = Linear::new(w_gen, Some(b_gen)); let sample_xs = Tensor::from_data( vec![2f32, 1., 7., 4., -4., 12., 5., 8.], (4, 2), TensorOptions::new(), - ); + )?; let sample_xs = sample_xs.to(&device)?; let sample_ys = r#gen.schedule(sample_xs.clone())?; From 331de0ae828c572b1b67670b5e91668c4f4e9f3d Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 25 Oct 2025 13:26:57 -0600 Subject: [PATCH 482/590] Clean out old wasm-pack stuff --- crates/piston-web/Cargo.toml | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/crates/piston-web/Cargo.toml b/crates/piston-web/Cargo.toml index 08f77a5e..64620c32 100644 --- a/crates/piston-web/Cargo.toml +++ b/crates/piston-web/Cargo.toml @@ -15,18 +15,11 @@ crate-type = ["cdylib", "rlib"] [package.metadata.docs.rs] default-target = "wasm32-unknown-unknown" -[package.metadata.wasm-pack.profile.dev.wasm-bindgen] -debug-js-glue = true -demangle-name-section = true -dwarf-debug-info = true -[package.metadata.wasm-pack.profile.release] -wasm-opt = [ - '-O4', - '--enable-simd', - '--flexible-inline-max-function-size', - '4294967295', -] +[profile.release] +opt-level = 3 +lto = "fat" +strip = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] From d5d6db066d3b2eb84ee681d559211781ea8f482e Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 25 Oct 2025 13:27:31 -0600 Subject: [PATCH 483/590] Add guard for zero-sized dims in Embedding --- packages/web/src/nn/embedding.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/web/src/nn/embedding.ts b/packages/web/src/nn/embedding.ts index bfe41870..2ed2dd9b 100644 --- a/packages/web/src/nn/embedding.ts +++ b/packages/web/src/nn/embedding.ts @@ -31,6 +31,13 @@ export class Embedding extends Module<[Tensor], Tensor> { finalDims.push(this.hiddenSize); const indexes = input.flatten(); const values = this.weight.indexSelect(indexes, 0); + // Basic guard against accidental zero-sized dims which would break view + const inputSize = input.size(); + for (let i = 0; i < inputSize.length; i++) { + if (inputSize[i] === 0) { + throw new Error("Input tensor has a dimension with size 0, which is not allowed"); + } + } return values.view(finalDims); } } From 6c5a7eee406e45d48b328cd7cb0b84acd3a6ade6 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 25 Oct 2025 13:28:31 -0600 Subject: [PATCH 484/590] Export CrossEntropyLossConfig --- packages/web/src/nn/index.ts | 2 +- packages/web/src/nn/loss.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/web/src/nn/index.ts b/packages/web/src/nn/index.ts index 97173307..5ea6b752 100644 --- a/packages/web/src/nn/index.ts +++ b/packages/web/src/nn/index.ts @@ -3,7 +3,7 @@ export { AlibiEmbedding } from "./alibi"; export { Dropout } from "./dropout"; export { Embedding } from "./embedding"; export { Linear } from "./linear"; -export { CrossEntropyLoss } from "./loss"; +export { CrossEntropyLoss, type CrossEntropyLossConfig } from "./loss"; export { type BufferRegistrationHook, Module, diff --git a/packages/web/src/nn/loss.ts b/packages/web/src/nn/loss.ts index 5ff1025f..d9e58c84 100644 --- a/packages/web/src/nn/loss.ts +++ b/packages/web/src/nn/loss.ts @@ -7,7 +7,7 @@ import { Tensor } from "@/tensor"; * Configuration options for CrossEntropyLoss * @interface CrossEntropyLossConfig */ -interface CrossEntropyLossConfig { +export interface CrossEntropyLossConfig { /** * Index to ignore in the target tensor * @default -100 From 287d1c7077229616d1d3a09fe52fa77762e46b3f Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 25 Oct 2025 13:29:51 -0600 Subject: [PATCH 485/590] Add Sequential module --- packages/web/src/nn/index.ts | 1 + packages/web/src/nn/module.ts | 247 +++++++++++++++++++++++++++++++++- 2 files changed, 246 insertions(+), 2 deletions(-) diff --git a/packages/web/src/nn/index.ts b/packages/web/src/nn/index.ts index 5ea6b752..d15a41d0 100644 --- a/packages/web/src/nn/index.ts +++ b/packages/web/src/nn/index.ts @@ -14,6 +14,7 @@ export { registerModuleBufferRegistrationHook, registerModuleModuleRegistrationHook, registerModuleParameterRegistrationHook, + Sequential, } from "./module"; export { LayerNorm, RMSNorm } from "./normalization"; export { RotaryEmbedding } from "./rope"; diff --git a/packages/web/src/nn/module.ts b/packages/web/src/nn/module.ts index 5e468548..9c62f0af 100644 --- a/packages/web/src/nn/module.ts +++ b/packages/web/src/nn/module.ts @@ -944,8 +944,251 @@ export class ModuleList extends Module { - return this._proxy; +/** + * Sequential container for composing a stack of modules that are executed in order. + * The output of each module is provided as the input to the next. + * By default, modules are registered with numeric string keys ("0", "1", ...). + * Optionally, named modules can be provided to register using user-specified keys. + * This is similar to torch.nn.Sequential in PyTorch. + */ +export class Sequential extends Module { + private modulesList: Module[]; + private moduleNames: string[]; + + constructor(); + constructor(modules: Module[]); + constructor(...modules: Module[]); + constructor(namedModules: Record); + constructor(...args: unknown[]) { + super(); + this.modulesList = []; + this.moduleNames = []; + + if (args.length === 0) { + return; + } + + if (args.length === 1) { + const only = args[0]; + if (Array.isArray(only)) { + this.extend(only as Module[]); + return; + } + if (only instanceof Module) { + this.append(only as Module); + return; + } + if (only && typeof only === "object") { + // Treat as named modules + for (const [name, mod] of Object.entries(only as Record)) { + this.appendNamed(name, mod); + } + return; + } + } + + // Varargs modules + for (const mod of args) { + if (mod instanceof Module) { + this.append(mod); + } + } + } + + /** + * Append a module and register it with an auto-generated numeric key. + */ + append(module: Module): Sequential { + const key = this.modulesList.length.toString(); + this.addModule(key, module); + this.modulesList.push(module); + this.moduleNames.push(key); + return this; + } + + /** + * Append a named module and register it with the provided key. + */ + appendNamed(name: string, module: Module): Sequential { + if (typeof name !== "string" || name.length === 0) { + throw new Error("Sequential.appendNamed: name must be a non-empty string"); + } + if (name.includes(".")) { + throw new Error(`Sequential.appendNamed: name can't contain ".", got: ${name}`); + } + if (this.moduleNames.includes(name)) { + throw new Error(`Sequential.appendNamed: duplicate name '${name}'`); + } + this.addModule(name, module); + this.modulesList.push(module); + this.moduleNames.push(name); + return this; + } + + /** + * Extend with an iterable of modules (auto-numbered). + */ + extend(modules: Iterable): Sequential { + for (const m of modules) { + this.append(m); + } + return this; + } + + /** + * Extend with a mapping of name -> module. + */ + extendNamed(named: Record): Sequential { + for (const [name, m] of Object.entries(named)) { + this.appendNamed(name, m); + } + return this; + } + + /** + * Insert a module at a specific index. If name is omitted, a numeric key is re-generated. + */ + insert(index: number, module: Module, name?: string): Sequential { + if (index < 0) { + index = Math.max(0, this.modulesList.length + index + 1); + } + this.modulesList.splice(index, 0, module); + if (name) { + if (this.moduleNames.includes(name)) { + throw new Error(`Sequential.insert: duplicate name '${name}'`); + } + this.moduleNames.splice(index, 0, name); + } else { + // placeholder; will be regenerated + this.moduleNames.splice(index, 0, ""); + } + this.recreateModules(); + return this; + } + + /** + * Remove the first occurrence of a specific module instance. + */ + remove(module: Module): Sequential { + const idx = this.modulesList.indexOf(module); + if (idx !== -1) { + this.modulesList.splice(idx, 1); + this.moduleNames.splice(idx, 1); + this.recreateModules(); + } + return this; + } + + /** + * Delete a module by its registered name. + */ + delete(name: string): Sequential { + const idx = this.moduleNames.indexOf(name); + if (idx !== -1) { + this.modulesList.splice(idx, 1); + this.moduleNames.splice(idx, 1); + this.recreateModules(); + } + return this; + } + + /** + * Clear all modules. + */ + clear(): Sequential { + this.modulesList = []; + this.moduleNames = []; + // Remove all registered children + const keys = Array.from(this.namedChildrenIter()).map(([k]) => k); + for (const k of keys) { + this.addModule(k, null); + } + return this; + } + + /** + * Number of modules. + */ + get length(): number { + return this.modulesList.length; + } + + /** + * Get module by index. + */ + at(index: number): Module | undefined { + if (index < 0) { + index = this.modulesList.length + index; + } + return this.modulesList[index]; + } + + /** + * Get module by index or name. + */ + get(key: number | string): Module | undefined { + if (typeof key === "number") return this.at(key); + const idx = this.moduleNames.indexOf(key); + return idx >= 0 ? this.modulesList[idx] : undefined; + } + + /** + * Replace module at index; preserves or sets the name if provided. + */ + set(index: number, module: Module, name?: string): Sequential { + if (index < 0) { + index = this.modulesList.length + index; + } + if (index < 0 || index > this.modulesList.length) { + throw new Error("Sequential.set: index out of bounds"); + } + if (index === this.modulesList.length) { + // append + return name ? this.insert(index, module, name) : this.insert(index, module); + } + this.modulesList[index] = module; + if (name) { + if (this.moduleNames.includes(name) && this.moduleNames[index] !== name) { + throw new Error(`Sequential.set: duplicate name '${name}'`); + } + this.moduleNames[index] = name; + } + this.recreateModules(); + return this; + } + + /** + * Re-register children after any structural change to keep names and indices consistent. + */ + private recreateModules(): void { + // Remove existing children + const keys = Array.from(this.namedChildrenIter()).map(([k]) => k); + for (const k of keys) { + this.addModule(k, null); + } + + // Re-add with normalized names + for (let i = 0; i < this.modulesList.length; i++) { + const explicit = this.moduleNames[i]; + const name = explicit && explicit.length > 0 ? explicit : i.toString(); + this.moduleNames[i] = name; // ensure any placeholders are filled + this.addModule(name, this.modulesList[i]); + } + } + + /** + * Forward pass: feed inputs through each module in order. + * If a module returns an array, it is expanded as multiple arguments to the next module. + */ + forward(...args: unknown[]): unknown { + let current: unknown[] = args; + for (const module of this.modulesList) { + // Call with current as varargs; hooks are applied by Module's proxy + const out = (module.forward as (...xs: unknown[]) => unknown)(...current); + // Prepare next arguments + current = Array.isArray(out) ? (out as unknown[]) : [out]; + } + return current.length === 1 ? current[0] : current; } } From 2ead3d5a1506a7c2efc9ded17c087a1d99778db1 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 25 Oct 2025 13:30:39 -0600 Subject: [PATCH 486/590] Actually export Buffer, Parameter --- packages/web/src/nn/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/web/src/nn/index.ts b/packages/web/src/nn/index.ts index d15a41d0..3bec1a6f 100644 --- a/packages/web/src/nn/index.ts +++ b/packages/web/src/nn/index.ts @@ -17,6 +17,7 @@ export { Sequential, } from "./module"; export { LayerNorm, RMSNorm } from "./normalization"; +export { Buffer, Parameter } from "./parameter"; export { RotaryEmbedding } from "./rope"; export { SinusoidalEmbedding } from "./sinusoidal"; export { From 3f364876be983bb2095ccde04ae31dffdaa6042e Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 25 Oct 2025 13:36:11 -0600 Subject: [PATCH 487/590] Module deunderscoring --- packages/web/src/nn/module.ts | 104 +++++++++++++++++----------------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/packages/web/src/nn/module.ts b/packages/web/src/nn/module.ts index 9c62f0af..c1ebc7d6 100644 --- a/packages/web/src/nn/module.ts +++ b/packages/web/src/nn/module.ts @@ -22,9 +22,9 @@ export type ModuleRegistrationHook = ( submodule: Module | null, ) => void; -const _globalParameterRegistrationHooks = new Map(); -const _globalBufferRegistrationHooks = new Map(); -const _globalModuleRegistrationHooks = new Map(); +const globalParameterRegistrationHooks = new Map(); +const globalBufferRegistrationHooks = new Map(); +const globalModuleRegistrationHooks = new Map(); /** * Register a global parameter registration hook. @@ -36,8 +36,8 @@ const _globalModuleRegistrationHooks = new Map() export function registerModuleParameterRegistrationHook( hook: ParameterRegistrationHook, ): RemovableHandle { - const handle = new RemovableHandle(_globalParameterRegistrationHooks); - _globalParameterRegistrationHooks.set(handle.id, hook); + const handle = new RemovableHandle(globalParameterRegistrationHooks); + globalParameterRegistrationHooks.set(handle.id, hook); return handle; } @@ -51,8 +51,8 @@ export function registerModuleParameterRegistrationHook( export function registerModuleBufferRegistrationHook( hook: BufferRegistrationHook, ): RemovableHandle { - const handle = new RemovableHandle(_globalBufferRegistrationHooks); - _globalBufferRegistrationHooks.set(handle.id, hook); + const handle = new RemovableHandle(globalBufferRegistrationHooks); + globalBufferRegistrationHooks.set(handle.id, hook); return handle; } @@ -66,8 +66,8 @@ export function registerModuleBufferRegistrationHook( export function registerModuleModuleRegistrationHook( hook: ModuleRegistrationHook, ): RemovableHandle { - const handle = new RemovableHandle(_globalModuleRegistrationHooks); - _globalModuleRegistrationHooks.set(handle.id, hook); + const handle = new RemovableHandle(globalModuleRegistrationHooks); + globalModuleRegistrationHooks.set(handle.id, hook); return handle; } @@ -111,7 +111,7 @@ export class Module { } // Handle attribute setting through _setAttr - return target._setAttr(prop.toString(), value); + return target.setAttr(prop.toString(), value); }, get: (target: Module, prop: string | symbol, receiver: unknown): unknown => { @@ -158,7 +158,7 @@ export class Module { }) as Module; } - _setAttr(name: string, value: unknown): boolean { + private setAttr(name: string, value: unknown): boolean { // Helper function to remove attribute from various collections const removeFrom = ( ...containers: Array | Set | object> @@ -245,7 +245,7 @@ export class Module { } // Call global parameter registration hooks - for (const hook of _globalParameterRegistrationHooks.values()) { + for (const hook of globalParameterRegistrationHooks.values()) { try { hook(this as Module, name, param); } catch (error) { @@ -285,7 +285,7 @@ export class Module { } // Call global buffer registration hooks - for (const hook of _globalBufferRegistrationHooks.values()) { + for (const hook of globalBufferRegistrationHooks.values()) { try { hook(this as Module, name, buffer); } catch (error) { @@ -322,7 +322,7 @@ export class Module { this._modules[name] = module as Module; // Call global module registration hooks - for (const hook of _globalModuleRegistrationHooks.values()) { + for (const hook of globalModuleRegistrationHooks.values()) { try { hook(this as Module, name, module as Module | null); } catch (error) { @@ -336,7 +336,7 @@ export class Module { /** * Helper method that returns an iterator over named members of the module. */ - private *_namedMembers( + private *namedMembers( getMembersFn: ( module: Module, ) => Iterable<[string, MemberType]>, @@ -368,7 +368,7 @@ export class Module { const submodulePrefix = prefix + (prefix ? "." : "") + name; // Create a generator from the child module's named members - const submoduleGen = module._namedMembers( + const submoduleGen = module.namedMembers( getMembersFn, submodulePrefix, recurse, @@ -414,7 +414,7 @@ export class Module { recurse: boolean = true, removeDuplicate: boolean = true, ): Generator<[string, Parameter]> { - const gen = this._namedMembers( + const gen = this.namedMembers( (module) => Object.entries(module._parameters), prefix, recurse, @@ -471,7 +471,7 @@ export class Module { recurse: boolean = true, removeDuplicate: boolean = true, ): Generator<[string, Buffer]> { - const gen = this._namedMembers( + const gen = this.namedMembers( (module) => Object.entries(module._buffers), prefix, recurse, @@ -486,14 +486,14 @@ export class Module { * * @param prefix - Prefix to prepend to all buffer names * @param recurse - If true, recurses into child modules - * @param remove_duplicate - If true, removes duplicate buffers + * @param removeDuplicate - If true, removes duplicate buffers */ namedBuffers( prefix: string = "", recurse: boolean = true, - remove_duplicate: boolean = true, + removeDuplicate: boolean = true, ): Array<[string, Buffer]> { - return Array.from(this.namedBuffersIter(prefix, recurse, remove_duplicate)); + return Array.from(this.namedBuffersIter(prefix, recurse, removeDuplicate)); } /** @@ -554,15 +554,15 @@ export class Module { * * @param memo - Set used to track already seen modules * @param prefix - Prefix to prepend to all module names - * @param remove_duplicate - If true, removes duplicate modules + * @param removeDuplicate - If true, removes duplicate modules */ *namedModulesIter( memo: Set = new Set(), prefix: string = "", - remove_duplicate: boolean = true, + removeDuplicate: boolean = true, ): Generator<[string, Module]> { - if (!memo.has(this as unknown as Module) || !remove_duplicate) { - if (remove_duplicate) { + if (!memo.has(this as unknown as Module) || !removeDuplicate) { + if (removeDuplicate) { memo.add(this as unknown as Module); } @@ -576,7 +576,7 @@ export class Module { const submodulePrefix = prefix + (prefix ? "." : "") + name; // Get generator from submodule's named modules - yield* module.namedModulesIter(memo, submodulePrefix, remove_duplicate); + yield* module.namedModulesIter(memo, submodulePrefix, removeDuplicate); } } } @@ -586,14 +586,14 @@ export class Module { * * @param memo - Set used to track already seen modules * @param prefix - Prefix to prepend to all module names - * @param remove_duplicate - If true, removes duplicate modules + * @param removeDuplicate - If true, removes duplicate modules */ namedModules( memo: Set = new Set(), prefix: string = "", - remove_duplicate: boolean = true, + removeDuplicate: boolean = true, ): Array<[string, Module]> { - return Array.from(this.namedModulesIter(memo, prefix, remove_duplicate)); + return Array.from(this.namedModulesIter(memo, prefix, removeDuplicate)); } /** @@ -726,13 +726,13 @@ export class Module { * This is similar to torch.nn.ModuleList in PyTorch. */ export class ModuleList extends Module { - private _modules_list: Module[]; - private _proxy: ModuleList; + private modulesList: Module[]; + private _proxy: ModuleList; [key: number]: Module; constructor(modules: Module[] = []) { super(); - this._modules_list = []; + this.modulesList = []; // Add modules if provided in constructor if (modules && modules.length > 0) { @@ -785,9 +785,9 @@ export class ModuleList extends Module { - const idx = this._modules_list.length; + const idx = this.modulesList.length; this.addModule(idx.toString(), module); - this._modules_list.push(module); + this.modulesList.push(module); return this; } @@ -813,14 +813,14 @@ export class ModuleList extends Module { if (index < 0) { - index = Math.max(0, this._modules_list.length + index + 1); + index = Math.max(0, this.modulesList.length + index + 1); } // Insert the module in our list - this._modules_list.splice(index, 0, module); + this.modulesList.splice(index, 0, module); // Re-register all modules with updated indices - this._recreateModules(); + this.recreateModules(); return this; } @@ -832,11 +832,11 @@ export class ModuleList extends Module { - const index = this._modules_list.indexOf(module); + const index = this.modulesList.indexOf(module); if (index !== -1) { - this._modules_list.splice(index, 1); + this.modulesList.splice(index, 1); // Re-register all modules with updated indices - this._recreateModules(); + this.recreateModules(); } return this; } @@ -847,7 +847,7 @@ export class ModuleList extends Module { - this._modules_list = []; + this.modulesList = []; // Get all registered module keys using named modules iterator const moduleKeys = Array.from(this.namedChildrenIter()).map(([key]) => key); for (const key of moduleKeys) { @@ -860,7 +860,7 @@ export class ModuleList extends Module extends Module extends Module { if (index < 0) { - index = this._modules_list.length + index; + index = this.modulesList.length + index; } - if (index >= 0 && index < this._modules_list.length) { + if (index >= 0 && index < this.modulesList.length) { // Remove old module registration this.addModule(index.toString(), null); // Add new module this.addModule(index.toString(), module); // Update in the array - this._modules_list[index] = module; - } else if (index === this._modules_list.length) { + this.modulesList[index] = module; + } else if (index === this.modulesList.length) { // If appending to the end, use append this.append(module); } else { @@ -909,7 +909,7 @@ export class ModuleList extends Module key); @@ -919,8 +919,8 @@ export class ModuleList extends Module extends Module Date: Sat, 25 Oct 2025 13:36:40 -0600 Subject: [PATCH 488/590] Make sure hooks apply to proxied module --- packages/web/src/nn/module.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/web/src/nn/module.ts b/packages/web/src/nn/module.ts index c1ebc7d6..f6b89419 100644 --- a/packages/web/src/nn/module.ts +++ b/packages/web/src/nn/module.ts @@ -118,10 +118,12 @@ export class Module { // Handle hooks if (prop === "forward" && typeof target.forward === "function") { return function (...args: unknown[]) { + const proxiedModule = receiver as Module; + // Apply pre-hooks let hookInput = args; for (const hook of target._forwardPreHooks.values()) { - const result = hook(target, args as Input); + const result = hook(proxiedModule, args as Input); if (result !== undefined) { hookInput = Array.isArray(result) ? result : [result]; } @@ -132,7 +134,7 @@ export class Module { // Apply post-hooks let hookOutput = output; for (const hook of target._forwardHooks.values()) { - const result = hook(target, hookInput as Input, hookOutput); + const result = hook(proxiedModule, hookInput as Input, hookOutput); if (result !== undefined) { hookOutput = result; } From 5799f35805afb82ed340848abbf17c91925632e0 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 25 Oct 2025 13:38:42 -0600 Subject: [PATCH 489/590] Undo overzealous typecasting in Module --- packages/web/src/nn/module.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/web/src/nn/module.ts b/packages/web/src/nn/module.ts index f6b89419..5cf75d45 100644 --- a/packages/web/src/nn/module.ts +++ b/packages/web/src/nn/module.ts @@ -233,7 +233,7 @@ export class Module { registerParameter(name: string, param: Parameter | null): Module { if (param !== null && !(param instanceof Parameter)) { throw new TypeError( - `Cannot assign '${(param as unknown)?.constructor?.name || typeof param}' as parameter '${name}'`, + `Cannot assign '${(param as Module)?.constructor?.name || typeof param}' as parameter '${name}'`, ); } @@ -311,7 +311,7 @@ export class Module { module: Module | null, ): Module { if (module !== null && !(module instanceof Module)) { - throw new Error(`${(module as unknown)?.constructor?.name || typeof module} is not a Module`); + throw new Error(`${(module as Module)?.constructor?.name || typeof module} is not a Module`); } else if (typeof name !== "string") { throw new Error(`module name should be a string. Got ${typeof name}`); } else if (name in this && !(name in this._modules)) { @@ -326,7 +326,7 @@ export class Module { // Call global module registration hooks for (const hook of globalModuleRegistrationHooks.values()) { try { - hook(this as Module, name, module as Module | null); + hook(this as Module, name, module as Module | null); } catch (error) { throw new Error(`Error in module registration hook: ${error}`, { cause: error }); } @@ -563,12 +563,12 @@ export class Module { prefix: string = "", removeDuplicate: boolean = true, ): Generator<[string, Module]> { - if (!memo.has(this as unknown as Module) || !removeDuplicate) { + if (!memo.has(this as Module) || !removeDuplicate) { if (removeDuplicate) { - memo.add(this as unknown as Module); + memo.add(this as Module); } - yield [prefix, this as unknown as Module]; + yield [prefix, this as Module]; for (const [name, module] of Object.entries(this._modules)) { if (module === null) { @@ -614,7 +614,7 @@ export class Module { module.train(mode); } - return this as unknown as Module; + return this as Module; } /** From 55d7d1e9bfc74d6933b518e71f74bcea59af998d Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 25 Oct 2025 13:39:13 -0600 Subject: [PATCH 490/590] Improve ModuleList typing --- packages/web/src/nn/module.ts | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/packages/web/src/nn/module.ts b/packages/web/src/nn/module.ts index 5cf75d45..46aa4778 100644 --- a/packages/web/src/nn/module.ts +++ b/packages/web/src/nn/module.ts @@ -721,16 +721,24 @@ export class Module { } } +type TupleToObject = { + [K in keyof T as K extends `${number}` ? K : never]: T[K]; +}; + /** * ModuleList container for a sequence of modules * Modules can be added as ordered members of the ModuleList. * ModuleList can be indexed like an array to access contained modules. * This is similar to torch.nn.ModuleList in PyTorch. */ -export class ModuleList extends Module { +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export class ModuleList[] = Module[]> extends Module< + unknown, + unknown +> { private modulesList: Module[]; private _proxy: ModuleList; - [key: number]: Module; + [key: number]: ListType[number]; constructor(modules: Module[] = []) { super(); @@ -745,7 +753,7 @@ export class ModuleList extends Module, prop: string | symbol): unknown => { + get: (target: ModuleList, prop: string | symbol): unknown => { // Forward numeric indices and string numeric indices to at() if (typeof prop === "string" && /^\d+$/.test(prop)) { return target.at(parseInt(prop, 10)); @@ -756,7 +764,7 @@ export class ModuleList extends Module, prop: string | symbol, value: unknown): boolean => { + set: (target: ModuleList, prop: string | symbol, value: unknown): boolean => { // Handle numeric indices and string numeric indices if (typeof prop === "string" && /^\d+$/.test(prop) && value instanceof Module) { const index = parseInt(prop, 10); @@ -786,7 +794,7 @@ export class ModuleList extends Module { + append(module: Module): ModuleList { const idx = this.modulesList.length; this.addModule(idx.toString(), module); this.modulesList.push(module); @@ -799,7 +807,7 @@ export class ModuleList extends Module): ModuleList { + extend(modules: Iterable): ModuleList { for (const module of modules) { this.append(module); } @@ -813,7 +821,7 @@ export class ModuleList extends Module { + insert(index: number, module: Module): ModuleList { if (index < 0) { index = Math.max(0, this.modulesList.length + index + 1); } @@ -833,7 +841,7 @@ export class ModuleList extends Module { + remove(module: Module): ModuleList { const index = this.modulesList.indexOf(module); if (index !== -1) { this.modulesList.splice(index, 1); @@ -848,7 +856,7 @@ export class ModuleList extends Module { + clear(): ModuleList { this.modulesList = []; // Get all registered module keys using named modules iterator const moduleKeys = Array.from(this.namedChildrenIter()).map(([key]) => key); @@ -885,7 +893,7 @@ export class ModuleList extends Module { + set(index: number, module: Module): ModuleList { if (index < 0) { index = this.modulesList.length + index; } @@ -946,6 +954,11 @@ export class ModuleList extends Module & TupleToObject { + return this._proxy as ModuleList & TupleToObject; + } +} + /** * Sequential container for composing a stack of modules that are executed in order. * The output of each module is provided as the input to the next. From c23ca21f20c24983da6ca3d9f93d75f2c0231b50 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 25 Oct 2025 13:39:29 -0600 Subject: [PATCH 491/590] Add apply method to Module --- packages/web/src/nn/module.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/web/src/nn/module.ts b/packages/web/src/nn/module.ts index 46aa4778..d26fc447 100644 --- a/packages/web/src/nn/module.ts +++ b/packages/web/src/nn/module.ts @@ -335,6 +335,14 @@ export class Module { return this; } + apply(fn: (module: Module) => void): Module { + for (const module of this.children()) { + module.apply(fn); + } + fn(this as Module); + return this; + } + /** * Helper method that returns an iterator over named members of the module. */ From 3d6042d80143825fdc22c4e9d0c4200e1e54ff19 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 25 Oct 2025 13:39:48 -0600 Subject: [PATCH 492/590] Make to(device) accept strings as well --- packages/web/src/nn/module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web/src/nn/module.ts b/packages/web/src/nn/module.ts index d26fc447..4a90a66e 100644 --- a/packages/web/src/nn/module.ts +++ b/packages/web/src/nn/module.ts @@ -703,7 +703,7 @@ export class Module { * @param device - The device to move to (e.g., "cpu", "gpu") * @returns The module instance for chaining */ - async to(device: string): Promise> { + async to(device: Device | "cpu" | "gpu" | "webgpu"): Promise> { // Move all parameters to the new device for (const [name, param] of Object.entries(this._parameters)) { if (param !== null) { From 1dbc14dfedd603223c9ac8a967b0b9252fc79e52 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 25 Oct 2025 13:40:23 -0600 Subject: [PATCH 493/590] Add loadStateDict to Module --- packages/web/src/nn/module.ts | 64 +++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/packages/web/src/nn/module.ts b/packages/web/src/nn/module.ts index 4a90a66e..d63cb4d2 100644 --- a/packages/web/src/nn/module.ts +++ b/packages/web/src/nn/module.ts @@ -1,5 +1,7 @@ import { Buffer, Parameter } from "@/nn/parameter"; +import { Tensor } from "@/tensor"; import { RemovableHandle } from "@/utils"; +import { Device } from "@/wasm"; /** * Type definitions for registration hooks @@ -727,6 +729,68 @@ export class Module { return this; } + + /** + * Load parameters and buffers from a flat state dict with dotted keys. + * Returns missing and unexpected keys; throws if strict and either is non-empty. + */ + loadStateDict( + state: Record, + options?: { strict?: boolean }, + ): { missingKeys: string[]; unexpectedKeys: string[] } { + const strict = options?.strict ?? true; + + const expectedParamNames = this.namedParameters("", true, true).map(([n]) => n); + const expectedBufferNames = this.namedBuffers("", true, true).map(([n]) => n); + const expected = new Set([...expectedParamNames, ...expectedBufferNames]); + + const incoming = Object.keys(state); + const unexpectedKeys = incoming.filter((k) => !expected.has(k)).sort(); + const missingKeys = Array.from(expected) + .filter((k) => !(k in state)) + .sort(); + + if (strict && (unexpectedKeys.length > 0 || missingKeys.length > 0)) { + throw new Error( + `loadStateDict(strict=true) found mismatched keys: missing=[${missingKeys.join(", ")}] unexpected=[${unexpectedKeys.join(", ")}]`, + ); + } + + // Helper to fetch a module by a dotted path + const getModuleByPath = (root: Module, path: string[]): Module => { + let cursor: Module = root; + for (const seg of path) { + const next = cursor._modules[seg]; + if (!next) { + throw new Error(`loadStateDict: missing submodule at path '${path.join(".")}'`); + } + cursor = next; + } + return cursor; + }; + + for (const [fullName, tensor] of Object.entries(state)) { + if (!expected.has(fullName)) continue; // skip unexpected in non-strict mode + const parts = fullName.split("."); + const leaf = parts.pop() as string; + const target = getModuleByPath(this as Module, parts); + + const hasParam = Object.prototype.hasOwnProperty.call(target._parameters, leaf); + const hasBuffer = Object.prototype.hasOwnProperty.call(target._buffers, leaf); + + if (hasParam) { + // Replace parameter + target.registerParameter(leaf, new Parameter(tensor)); + } else if (hasBuffer) { + // Replace buffer (persistent by default) + target.registerBuffer(leaf, new Buffer(tensor), true); + } else if (strict) { + throw new Error(`loadStateDict: '${fullName}' does not match a parameter or buffer`); + } + } + + return { missingKeys, unexpectedKeys }; + } } type TupleToObject = { From ba79ef49e32b81c0f503856f508c5e33f0042099 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 25 Oct 2025 13:40:51 -0600 Subject: [PATCH 494/590] Leftover debugTensor property access change --- packages/web/src/nn/moduleUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web/src/nn/moduleUtils.ts b/packages/web/src/nn/moduleUtils.ts index 6cc640e1..2958a465 100644 --- a/packages/web/src/nn/moduleUtils.ts +++ b/packages/web/src/nn/moduleUtils.ts @@ -4,7 +4,7 @@ import { Tensor } from "@/tensor"; export function debugBufferHook(module: Module): Promise { const promise = new Promise((resolve) => { const hook = module.registerForwardHook((_module, _input: Input, output: Tensor) => { - resolve(output.debugTensor()); + resolve(output.debugTensor); hook.remove(); return output; }); From 7d5a9cb7d68a48a1f7e118d7f9cbcf7da776a024 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 25 Oct 2025 13:41:22 -0600 Subject: [PATCH 495/590] Remove weird Device typing in sinusoidal --- packages/web/src/nn/sinusoidal.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web/src/nn/sinusoidal.ts b/packages/web/src/nn/sinusoidal.ts index 3997e688..41bf8368 100644 --- a/packages/web/src/nn/sinusoidal.ts +++ b/packages/web/src/nn/sinusoidal.ts @@ -16,7 +16,7 @@ export class SinusoidalEmbedding extends Module<[Tensor], Tensor> { * @param dim The embedding dimension. Must be even. * @param device Optional device to create the internal tensors on. */ - constructor(dim: number, device?: Device | "cpu") { + constructor(dim: number, device?: Device) { super(); if (dim % 2 !== 0) { throw new Error("Embedding dimension must be even."); From 015ba4d09019cf8607969b5a38a52ff95e4967c7 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 25 Oct 2025 13:42:16 -0600 Subject: [PATCH 496/590] Update tracking.ts to use hooks and modes --- packages/web/src/nn/index.ts | 12 +- packages/web/src/nn/tracking.ts | 381 +++++++++++++++++++++++--------- 2 files changed, 283 insertions(+), 110 deletions(-) diff --git a/packages/web/src/nn/index.ts b/packages/web/src/nn/index.ts index 3bec1a6f..b8ad157c 100644 --- a/packages/web/src/nn/index.ts +++ b/packages/web/src/nn/index.ts @@ -21,16 +21,10 @@ export { Buffer, Parameter } from "./parameter"; export { RotaryEmbedding } from "./rope"; export { SinusoidalEmbedding } from "./sinusoidal"; export { - ModuleScopeItem, nameFromScope, - OptimizerParamGroupScopeItem, - OptimizerParamUpdateScopeItem, - OptimizerScopeItem, - ScopeItem, + type OptimizerScopeItem, + type ScopeItem, tensorName, - tensorScopeStack, track, - TrackedTensor, - trackOptimizerStep, - withScope, + type TrackedTensor, } from "./tracking"; diff --git a/packages/web/src/nn/tracking.ts b/packages/web/src/nn/tracking.ts index b66a9a03..9331aee6 100644 --- a/packages/web/src/nn/tracking.ts +++ b/packages/web/src/nn/tracking.ts @@ -1,10 +1,18 @@ -import { Module } from "@/nn/module"; -import { Optimizer, ParamGroup } from "@/optim"; -import { Parameter } from "@/parameter"; +import { PistonFunctionMode } from "@/function"; +import { + Module, + registerModuleBufferRegistrationHook, + registerModuleModuleRegistrationHook, + registerModuleParameterRegistrationHook, +} from "@/nn/module"; +import { Buffer, Parameter } from "@/nn/parameter"; +import { Optimizer } from "@/optim"; import { OpDescription, Tensor } from "@/tensor"; +import { RemovableHandle } from "@/utils"; +import { Tensor_wasm } from "@/wasm"; export interface BaseScopeItem { - type: "module" | "optimizer" | "optimizer-param-group" | "optimizer-param-update"; + type: "module" | "optimizer"; name: string | undefined; } @@ -18,32 +26,7 @@ export interface OptimizerScopeItem extends BaseScopeItem { optimizer: Optimizer; } -export interface OptimizerParamGroupScopeItem extends BaseScopeItem { - type: "optimizer-param-group"; - optimizer: Optimizer; - paramGroup: ParamGroup; -} - -export interface OptimizerParamUpdateScopeItem extends BaseScopeItem { - type: "optimizer-param-update"; - optimizer: Optimizer; - parameter: Parameter; -} - -export type ScopeItem = - | ModuleScopeItem - | OptimizerScopeItem - | OptimizerParamGroupScopeItem - | OptimizerParamUpdateScopeItem; - -export const tensorScopeStack: ScopeItem[] = []; - -interface TrackState { - stack: TrackedTensor[]; - alreadyTracked: Set; - index: number; -} - +export type ScopeItem = ModuleScopeItem | OptimizerScopeItem; export interface TrackedTensor { id: number; @@ -62,96 +45,292 @@ export interface TensorTrackOptions { dependency?: boolean; } -function trackTensor(tensor: Tensor, options: TensorTrackOptions, trackStack: TrackState) { - const { dependency = false } = options ?? {}; - - const id = tensor.id(); - const trackedTensor: Omit = { - id, - op: tensor.op(), - shape: tensor.shape, - srcIds: tensor.srcIds(), - nameOnParent: tensor.nameOnParent, - scope: tensor.scope, - // This presumably breaks any non-explicit inplacing, which makes - // observing inplacing storage patterns difficult. - tensor: tensor, - // tensor: tensor.requiresGrad() ? tensor : undefined, - debugTensor: tensor.debugTensor(), - dependency, - }; - - if (trackStack.alreadyTracked.has(id)) { +/** + * Tracking dispatch mode that intercepts tensor operations and builds comprehensive tracking data. + * This replaces the old tensor hook system with a dispatch-based approach. + */ +export class TrackingFunctionMode extends PistonFunctionMode { + private trackStack: TrackedTensor[] = []; + private alreadyTracked = new Set(); + private index = 0; + + // Current scope tracking + private currentScope: ScopeItem[] = []; + private currentTensorName: string | undefined; + + // Module path tracking for scope building + private modulePathMap = new WeakMap(); + private parameterNameMap = new WeakMap(); + private bufferNameMap = new WeakMap(); + + // Module scope stack for proper nesting + private moduleStack: Module[] = []; + + // Hook handles for cleanup + private hookHandles: RemovableHandle[] = []; + + // Track modules that already have forward hooks registered + private hookedModules: WeakSet> = new WeakSet(); + + constructor() { + super(); + this.setupHooks(); + } + + private setupHooks(): void { + // Register global hooks to track module structure + this.hookHandles.push( + registerModuleModuleRegistrationHook((parentModule, name, submodule) => { + if (submodule) { + const parentPath = this.modulePathMap.get(parentModule) || ""; + const fullPath = parentPath ? `${parentPath}.${name}` : name; + this.modulePathMap.set(submodule, fullPath); + + // Register forward hooks on the submodule for scope tracking + this.setupModuleForwardHooks(submodule, fullPath, true); + } + }), + ); + + this.hookHandles.push( + registerModuleParameterRegistrationHook((_module, name, param) => { + if (param) { + this.parameterNameMap.set(param, name); + } + }), + ); + + this.hookHandles.push( + registerModuleBufferRegistrationHook((_module, name, buffer) => { + if (buffer) { + this.bufferNameMap.set(buffer, name); + } + }), + ); + } + + setupModuleForwardHooks( + module: Module, + path?: string, + recurse: boolean = false, + ): void { + if (path) { + this.modulePathMap.set(module, path); + } + + if (this.hookedModules.has(module)) { + return; + } + + // Pre-hook: push module to scope before forward + this.hookHandles.push( + module.registerForwardPreHook((mod, _inputs) => { + this.moduleStack.push(mod); + this.updateCurrentScope(); + return undefined; // Don't modify inputs + }), + ); + + // Post-hook: pop module from scope after forward + this.hookHandles.push( + module.registerForwardHook((_mod, _inputs, _output) => { + this.moduleStack.pop(); + this.updateCurrentScope(); + return undefined; // Don't modify output + }), + ); + + this.hookedModules.add(module); + + if (recurse) { + const basePath = path || this.modulePathMap.get(module) || module.constructor.name || ""; + for (const [name, child] of module.namedChildren()) { + if (child) { + const childPath = basePath ? `${basePath}.${name}` : name; + this.setupModuleForwardHooks(child as Module, childPath, true); + } + } + } + } + + private updateCurrentScope(): void { + this.currentScope = this.moduleStack.map((module) => ({ + type: "module" as const, + name: this.modulePathMap.get(module) || module.constructor.name, + module, + })); + } + + initializeRootModule(module: Module): void { + // Set the root module path + this.modulePathMap.set(module, module.constructor.name); + } + + _pistonFunction( + func: (...args: unknown[]) => FT | Promise, + _types: unknown[], + args: unknown[], + kwargs: Record, + ): Promise | T { + const after = (result: T) => { + // Track the resulting tensor + if (result instanceof Tensor_wasm) { + this.trackTensor(result, { dependency: false }); + } + return result; + }; + + // Call the next dispatch mode or default implementation + const result = func(...args, kwargs); + + if (result instanceof Promise) { + return result.then(after); + } + + return after(result as T); + } + + private trackTensor(tensor: Tensor, options: TensorTrackOptions): Tensor { + const { dependency = false } = options; + const id = tensor.id; + + if (this.alreadyTracked.has(id)) { + return tensor; + } + + // Try to determine tensor name from parameter/buffer maps + let tensorName = this.currentTensorName; + if (!tensorName) { + tensorName = this.findTensorName(tensor); + } + + const trackedTensor: TrackedTensor = { + id, + op: tensor.op(), + shape: tensor.shape, + srcIds: tensor.srcIds(), + nameOnParent: tensorName, + scope: [...this.currentScope], // Copy current scope + tensor: tensor, + debugTensor: tensor.debugTensor, + dependency, + index: this.index++, + }; + + this.trackStack.push(trackedTensor); + this.alreadyTracked.add(id); + + // Reset tensor name after tracking + this.currentTensorName = undefined; + return tensor; } - trackStack.stack.push({ - ...trackedTensor, - index: trackStack.index, - }); - trackStack.index++; - trackStack.alreadyTracked.add(id); - - return tensor; -} -export interface WithScopeOptions { - replace?: boolean; -} + private findTensorName(tensor: Tensor): string | undefined { + // Check if this tensor is a parameter or buffer in the current module + if (this.moduleStack.length > 0) { + const currentModule = this.moduleStack[this.moduleStack.length - 1]; -export function withScope( - scope: ScopeItem[] | ((scope: ScopeItem[]) => ScopeItem[]), - f: () => T, - options: WithScopeOptions = {}, -): T { - const { replace = false } = options; - const originalScope = [...tensorScopeStack]; - const newScope = typeof scope === "function" ? scope(tensorScopeStack) : scope; - if (replace) { - tensorScopeStack.length = 0; + // Check parameters using the public API + for (const [name, param] of currentModule.namedParametersIter("", false)) { + if (param && param === tensor) { + return name; + } + } + + // Check buffers using the public API + for (const [name, buffer] of currentModule.namedBuffersIter("", false)) { + if (buffer && buffer === tensor) { + return name; + } + } + } + + return undefined; + } + + /** + * Set the current scope for subsequent tensor operations + */ + withScope(scope: ScopeItem[], f: () => T): T { + const originalScope = [...this.currentScope]; + this.currentScope = scope; + try { + return f(); + } finally { + this.currentScope = originalScope; + } + } + + /** + * Set the current tensor name for the next tensor operation + */ + withTensorName(name: string, f: () => T): T { + const originalName = this.currentTensorName; + this.currentTensorName = name; + try { + return f(); + } finally { + this.currentTensorName = originalName; + } + } + + /** + * Get all tracked tensors + */ + getTrackedTensors(): TrackedTensor[] { + return [...this.trackStack]; } - tensorScopeStack.push(...newScope); - try { - return f(); - } finally { - tensorScopeStack.length = 0; - tensorScopeStack.push(...originalScope); + + /** + * Clear tracking state + */ + clear(): void { + this.trackStack = []; + this.alreadyTracked.clear(); + this.index = 0; + this.currentScope = []; + this.currentTensorName = undefined; + this.moduleStack = []; + } + + /** + * Clean up hooks when disposing + */ + [Symbol.dispose](): void { + super[Symbol.dispose](); + this.hookHandles.forEach((handle) => handle.remove()); + this.hookHandles = []; } } -export function nameFromScope(scope: ScopeItem[]) { +export function nameFromScope(scope: ScopeItem[]): string { return ( - scope.map((item) => (item.name?.includes(".") ? `[${item.name}]` : item.name)).join(".") ?? "." + scope.map((item) => (item.name?.includes(".") ? `[${item.name}]` : item.name)).join(".") || "." ); } -export function tensorName(tensor: Tensor) { +export function tensorName(tensor: TrackedTensor): string { return nameFromScope(tensor.scope ?? []) + (tensor.nameOnParent ? `.${tensor.nameOnParent}` : ""); } - +/** + * Track tensor operations during module forward pass + */ export function track( module: Module, ...args: Parameters ): [Output, TrackedTensor[]] { - const trackStack = { - stack: [], - alreadyTracked: new Set(), - index: 0, - }; - module.modulesIter().forEach((m) => { - m.registerTensorHook((tensor, options) => trackTensor(tensor, options, trackStack)); - }); + using trackingMode = new TrackingFunctionMode(); + + // Initialize the root module in the path map + const rootModule = module as Module; + trackingMode.initializeRootModule(rootModule); + + // Set up forward hooks for the root module and recurse into children + trackingMode.setupModuleForwardHooks(rootModule, rootModule.constructor.name, true); + const result = module.forward(...args); - return [result, trackStack.stack]; -} -export async function trackOptimizerStep(optimizer: Optimizer) { - const trackStack = { - stack: [], - alreadyTracked: new Set(), - index: 0, - }; - optimizer.registerTensorHook((tensor, options) => trackTensor(tensor, options, trackStack)); - await optimizer.step(); - return trackStack.stack; + return [result, trackingMode.getTrackedTensors()]; } From 9fd8a47fd8d49efba2b0355b815cbe1d947c52ed Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 25 Oct 2025 13:43:04 -0600 Subject: [PATCH 497/590] Add grad tracking, clipping utils --- packages/web/src/nn/index.ts | 1 + packages/web/src/nn/utils.ts | 82 ++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 packages/web/src/nn/utils.ts diff --git a/packages/web/src/nn/index.ts b/packages/web/src/nn/index.ts index b8ad157c..64e13040 100644 --- a/packages/web/src/nn/index.ts +++ b/packages/web/src/nn/index.ts @@ -28,3 +28,4 @@ export { track, type TrackedTensor, } from "./tracking"; +export * from "./utils"; diff --git a/packages/web/src/nn/utils.ts b/packages/web/src/nn/utils.ts new file mode 100644 index 00000000..2c5793c8 --- /dev/null +++ b/packages/web/src/nn/utils.ts @@ -0,0 +1,82 @@ +import { stack, zeros } from "@/globals"; +import { Tensor } from "@/tensor"; + +export interface TotalNormOptions { + normType: "fro" | "inf" | "-inf" | "0" | "1" | "-1" | "2" | number | null | undefined; + errorIfNonfinite?: boolean; +} + +export function getTotalNorm(tensors: Tensor | Tensor[], options?: TotalNormOptions): Tensor { + const { normType, errorIfNonfinite } = options ?? {}; + + if (errorIfNonfinite) { + throw new Error("errorIfNonfinite is not yet implemented (no isnan/isinf support yet)"); + } + + if (!Array.isArray(tensors)) { + tensors = [tensors]; + } + + if (tensors.length === 0) { + return zeros([1], { device: tensors[0].device }); + } + + // TODO: We assume the tensors are all on the same device (single GPU) for now + const norms = tensors.map((tensor) => tensor.norm({ ord: normType })); + const totalNorm = stack(norms).norm({ ord: normType }); + + // TODO: This is where we'd implement nonfinite handling, but lazy evaluation makes it unlikely + // that we'd know this here. A drawback of lazy evaluation, but we can check this manually later + // when we pull to the CPU later. + + return totalNorm; +} + +export function getTotalGradNorm(parameters: Tensor[], options?: TotalNormOptions): Tensor { + const grads = parameters.map((parameter) => parameter.grad).filter((grad) => grad !== undefined); + return getTotalNorm(grads, options); +} + +export function clipGradsWithNorm_(parameters: Tensor[], maxNorm: number, totalNorm: Tensor) { + if (!Array.isArray(parameters)) { + parameters = [parameters]; + } + + const grads = parameters.map((parameter) => parameter.grad).filter((grad) => grad !== undefined); + + if (grads.length === 0) { + return; + } + + const clipCoef = totalNorm.add(1e-6).recip().mul(maxNorm); + const clipCoefClamped = clipCoef.clamp({ max: 1 }); + grads.forEach((grad) => grad.mul_(clipCoefClamped)); +} + +export function clipGradNorm_( + parameters: Tensor | Tensor[], + maxNorm: number, + options?: TotalNormOptions, +): Tensor | undefined { + if (!Array.isArray(parameters)) { + parameters = [parameters]; + } + + if (parameters.length === 0) { + return; + } + + const { normType, errorIfNonfinite } = options ?? {}; + + const totalNorm = getTotalGradNorm(parameters, { normType, errorIfNonfinite }); + clipGradsWithNorm_(parameters, maxNorm, totalNorm); + return totalNorm; +} + +export function calculateFanInAndFanOut(tensor: Tensor) { + const dims = tensor.size(); + const receptive_field_size = dims.slice(2).reduce((acc, dim) => acc * dim, 1); + const fan_in = dims.length < 2 ? 1 : dims[1] * receptive_field_size; + const fan_out = dims.length === 0 ? 1 : dims[0] * receptive_field_size; + return [fan_in, fan_out]; +} From 21dbeedbf0872a560cd8b6cef1f45d8b8ad6266e Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 25 Oct 2025 13:48:29 -0600 Subject: [PATCH 498/590] Move forEachTensorDeep to utils index --- packages/web/src/utils/index.ts | 52 +++++++++++++++++++++++++++++++++ packages/web/src/utils/weak.ts | 50 +------------------------------ 2 files changed, 53 insertions(+), 49 deletions(-) diff --git a/packages/web/src/utils/index.ts b/packages/web/src/utils/index.ts index 501f108a..8e8b1a26 100644 --- a/packages/web/src/utils/index.ts +++ b/packages/web/src/utils/index.ts @@ -1,3 +1,5 @@ +import { Tensor } from "@/tensor"; + /** * A handle which provides the capability to remove a hook. * @@ -43,5 +45,55 @@ export class RemovableHandle { } } +export function forEachTensorDeep( + value: unknown, + visit: (t: Tensor) => void, + seen: WeakSet = new WeakSet(), +): void { + if (value instanceof Tensor) { + visit(value); + return; + } + if (value === null) return; + + const t = typeof value; + if (t !== "object" && t !== "function") return; + + const obj = value as object; + if (seen.has(obj)) return; + seen.add(obj); + + if (Array.isArray(obj)) { + for (const v of obj) forEachTensorDeep(v, visit, seen); + return; + } + if (obj instanceof Map) { + for (const [k, v] of obj) { + forEachTensorDeep(k, visit, seen); + forEachTensorDeep(v, visit, seen); + } + return; + } + if (obj instanceof Set) { + for (const v of obj) forEachTensorDeep(v, visit, seen); + return; + } + if ( + ArrayBuffer.isView(obj) || + obj instanceof ArrayBuffer || + obj instanceof Date || + obj instanceof RegExp + ) { + return; + } + + // Own props only; avoid calling getters + for (const key of Reflect.ownKeys(obj)) { + const desc = Object.getOwnPropertyDescriptor(obj, key); + if (!desc || !("value" in desc)) continue; + forEachTensorDeep(obj[key as keyof typeof obj], visit, seen); + } +} + export * from "./data"; export * from "./weak"; diff --git a/packages/web/src/utils/weak.ts b/packages/web/src/utils/weak.ts index e5cbc924..916968db 100644 --- a/packages/web/src/utils/weak.ts +++ b/packages/web/src/utils/weak.ts @@ -2,55 +2,7 @@ import { FunctionModeGuard, PistonFunctionMode } from "@/function"; import { Tensor } from "@/tensor"; import { __pistonActiveTensors, Tensor_wasm } from "@/wasm"; -function forEachTensorDeep( - value: unknown, - visit: (t: Tensor) => void, - seen: WeakSet = new WeakSet(), -): void { - if (value instanceof Tensor) { - visit(value); - return; - } - if (value === null) return; - - const t = typeof value; - if (t !== "object" && t !== "function") return; - - const obj = value as object; - if (seen.has(obj)) return; - seen.add(obj); - - if (Array.isArray(obj)) { - for (const v of obj) forEachTensorDeep(v, visit, seen); - return; - } - if (obj instanceof Map) { - for (const [k, v] of obj) { - forEachTensorDeep(k, visit, seen); - forEachTensorDeep(v, visit, seen); - } - return; - } - if (obj instanceof Set) { - for (const v of obj) forEachTensorDeep(v, visit, seen); - return; - } - if ( - ArrayBuffer.isView(obj) || - obj instanceof ArrayBuffer || - obj instanceof Date || - obj instanceof RegExp - ) { - return; - } - - // Own props only; avoid calling getters - for (const key of Reflect.ownKeys(obj)) { - const desc = Object.getOwnPropertyDescriptor(obj, key); - if (!desc || !("value" in desc)) continue; - forEachTensorDeep(obj[key as keyof typeof obj], visit, seen); - } -} +import { forEachTensorDeep } from "."; interface WeakTensorFunctionModeOptions { label?: string; From 3c8fbb8fb788ee06dea905b01ea2a361c418643d Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 25 Oct 2025 13:53:14 -0600 Subject: [PATCH 499/590] Make pin accept arbitrary object --- packages/web/src/utils/weak.ts | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/packages/web/src/utils/weak.ts b/packages/web/src/utils/weak.ts index 916968db..bd6e4f70 100644 --- a/packages/web/src/utils/weak.ts +++ b/packages/web/src/utils/weak.ts @@ -95,9 +95,14 @@ class WeakTensorFunctionMode extends PistonFunctionMode { } } - pinTensor(tensor: Tensor) { - this.weakTensors.delete((tensor as unknown as { __wbg_ptr: number }).__wbg_ptr); - this.pinnedPtrs.add(tensor.__pistonStrongId); + pin(input: T): T { + forEachTensorDeep(input, (tensor) => { + this.weakTensors.delete((tensor as unknown as { __wbg_ptr: number }).__wbg_ptr); + this.pinnedPtrs.add(tensor.__pistonStrongId); + }); + return input; + } + } [Symbol.dispose]() { @@ -107,14 +112,14 @@ class WeakTensorFunctionMode extends PistonFunctionMode { } } -export function pin(tensor: Tensor) { +export function pin(input: T): T { using guard = new FunctionModeGuard(); const mode = guard.mode; if (mode instanceof WeakTensorFunctionMode) { - mode.pinTensor(tensor); + return mode.pin(input); } // Fine to silently ignore; default "strong" mode will pin anyway - return tensor; + return input; } export async function weak( @@ -132,11 +137,11 @@ export function weak( const after = (result: T) => { try { if (result instanceof Tensor) { - weakMode.pinTensor(result); + weakMode.pin(result); return result; } - forEachTensorDeep(result, (tensor) => weakMode.pinTensor(tensor)); + forEachTensorDeep(result, (tensor) => weakMode.pin(tensor)); return result; } finally { From b9dea70db828bd92885a22594c00596907734954 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 25 Oct 2025 13:53:39 -0600 Subject: [PATCH 500/590] Make function mode, options public --- packages/web/src/utils/weak.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/web/src/utils/weak.ts b/packages/web/src/utils/weak.ts index bd6e4f70..396c5e9c 100644 --- a/packages/web/src/utils/weak.ts +++ b/packages/web/src/utils/weak.ts @@ -4,12 +4,12 @@ import { __pistonActiveTensors, Tensor_wasm } from "@/wasm"; import { forEachTensorDeep } from "."; -interface WeakTensorFunctionModeOptions { +export interface WeakTensorFunctionModeOptions { label?: string; trackLeaks?: boolean; } -class WeakTensorFunctionMode extends PistonFunctionMode { +export class WeakTensorFunctionMode extends PistonFunctionMode { private weakTensors: Map; public readonly label?: string; private readonly trackLeaks: boolean; From 2e812dc399b88e8e959666674e2c6f19f56fcfea Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 25 Oct 2025 13:54:27 -0600 Subject: [PATCH 501/590] Add markWeak for previously-pinned --- packages/web/src/utils/weak.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/web/src/utils/weak.ts b/packages/web/src/utils/weak.ts index 396c5e9c..c69bee3d 100644 --- a/packages/web/src/utils/weak.ts +++ b/packages/web/src/utils/weak.ts @@ -103,6 +103,11 @@ export class WeakTensorFunctionMode extends PistonFunctionMode { return input; } + markWeak(input: T) { + forEachTensorDeep(input, (tensor) => { + this.weakTensors.set((tensor as unknown as { __wbg_ptr: number }).__wbg_ptr, tensor); + }); + return input; } [Symbol.dispose]() { @@ -122,6 +127,14 @@ export function pin(input: T): T { return input; } +export function markWeak(input: T) { + using guard = new FunctionModeGuard(); + const mode = guard.mode; + if (mode instanceof WeakTensorFunctionMode) { + mode.markWeak(input); + } +} + export async function weak( input: () => Promise, options?: WeakTensorFunctionModeOptions, From c8030229927b5935ed11726d735dd26877b50148 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 25 Oct 2025 13:54:42 -0600 Subject: [PATCH 502/590] Allow passing a mode into weak fn --- packages/web/src/utils/weak.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/web/src/utils/weak.ts b/packages/web/src/utils/weak.ts index c69bee3d..7f0c4f44 100644 --- a/packages/web/src/utils/weak.ts +++ b/packages/web/src/utils/weak.ts @@ -136,13 +136,16 @@ export function markWeak(input: T) { } export async function weak( - input: () => Promise, + input: (mode: WeakTensorFunctionMode) => Promise, options?: WeakTensorFunctionModeOptions, ): Promise; -export function weak(input: () => T, options?: WeakTensorFunctionModeOptions): T; +export function weak( + input: (mode: WeakTensorFunctionMode) => T, + options?: WeakTensorFunctionModeOptions, +): T; export function weak(input: T, options?: WeakTensorFunctionModeOptions): T; export function weak( - input: (() => T | Promise) | T, + input: ((mode: WeakTensorFunctionMode) => T | Promise) | T, options?: WeakTensorFunctionModeOptions, ): T | Promise { const weakMode = new WeakTensorFunctionMode(options); @@ -163,9 +166,9 @@ export function weak( }; if (typeof input === "function") { - const fn = input as () => T | Promise; + const fn = input as (mode: WeakTensorFunctionMode) => T | Promise; try { - const fnOutput = fn(); + const fnOutput = fn(weakMode); if (fnOutput instanceof Promise) { return fnOutput.then(after); } From 5509237a0dca40b54844ba0c4454c400009757df Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 25 Oct 2025 13:56:28 -0600 Subject: [PATCH 503/590] Add mark-step mode to clean up weak mode --- packages/web/src/utils/weak.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/web/src/utils/weak.ts b/packages/web/src/utils/weak.ts index 7f0c4f44..41810826 100644 --- a/packages/web/src/utils/weak.ts +++ b/packages/web/src/utils/weak.ts @@ -1,4 +1,4 @@ -import { FunctionModeGuard, PistonFunctionMode } from "@/function"; +import { FunctionModeGuard, PistonFunctionMode, PistonMarkStepMode } from "@/function"; import { Tensor } from "@/tensor"; import { __pistonActiveTensors, Tensor_wasm } from "@/wasm"; @@ -182,3 +182,14 @@ export function weak( return after(input as T); } + +export class WeakMarkStepMode extends PistonMarkStepMode { + _pistonMarkStep(original: () => Promise): void | Promise { + using guard = new FunctionModeGuard(); + const mode = guard.mode; + if (mode instanceof WeakTensorFunctionMode) { + return mode[Symbol.dispose](); + } + return original(); + } +} From ab0157f54bc6e0549ceb673a4f462cd8922787ff Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 25 Oct 2025 13:58:30 -0600 Subject: [PATCH 504/590] Add index for data I never added --- packages/web/src/utils/data/index.ts | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 packages/web/src/utils/data/index.ts diff --git a/packages/web/src/utils/data/index.ts b/packages/web/src/utils/data/index.ts new file mode 100644 index 00000000..c6846b43 --- /dev/null +++ b/packages/web/src/utils/data/index.ts @@ -0,0 +1,3 @@ +export * from "./dataloader"; +export * from "./dataset"; +export * from "./sampler"; From 5887ed1f567182502a9eb3c1f21e9ab908c50f10 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 25 Oct 2025 14:00:50 -0600 Subject: [PATCH 505/590] TS support for explicit resource management --- packages/web/tsconfig.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/web/tsconfig.json b/packages/web/tsconfig.json index 8ed199c2..33022490 100644 --- a/packages/web/tsconfig.json +++ b/packages/web/tsconfig.json @@ -1,8 +1,8 @@ { "compilerOptions": { - "target": "es2023", + "target": "es2022", "module": "esnext", - "lib": ["dom", "esnext"], + "lib": ["dom", "esnext", "esnext.disposable"], "importHelpers": true, "declaration": true, "rootDir": "./src", From ca157628568ea6128fb3e96c97bf8b1367e3ab8b Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 26 Oct 2025 13:45:28 -0600 Subject: [PATCH 506/590] Fix loss gathering with -100 --- packages/web/src/nn/loss.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/web/src/nn/loss.ts b/packages/web/src/nn/loss.ts index d9e58c84..f0818caa 100644 --- a/packages/web/src/nn/loss.ts +++ b/packages/web/src/nn/loss.ts @@ -78,8 +78,12 @@ export class CrossEntropyLoss extends Module { ) .cast(float32); + // Replace ignoreIndex positions with a safe index (0) before gather. + // This prevents out-of-range gathers on -100 while having no effect after masking. + const safeTargets = target.where(mask, 0).cast(int32); + // Gather the negative log-prob for the correct classes - const nllGathered = logProbs.gather(1, target.unsqueeze(1)).mul(-1); + const nllGathered = logProbs.gather(1, safeTargets.unsqueeze(1)).mul(-1); // Mask out ignored tokens const nllMasked = nllGathered.mul(mask.unsqueeze(1)); From b5ae04df07c32128b7999659605d5f46c45ea91b Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 26 Oct 2025 14:27:08 -0600 Subject: [PATCH 507/590] Kill piston-web/model.rs and unused deps --- crates/piston-web/Cargo.toml | 22 +- crates/piston-web/src/lib.rs | 1 - crates/piston-web/src/model.rs | 690 --------------------------------- packages/web/src/wasm.ts | 1 - 4 files changed, 5 insertions(+), 709 deletions(-) delete mode 100644 crates/piston-web/src/model.rs diff --git a/crates/piston-web/Cargo.toml b/crates/piston-web/Cargo.toml index 64620c32..0913ef57 100644 --- a/crates/piston-web/Cargo.toml +++ b/crates/piston-web/Cargo.toml @@ -6,9 +6,6 @@ edition = "2024" license = "MIT" repository = "https://github.com/vinhowe/piston" -[features] -debug = ["piston-nn/debug"] - [lib] crate-type = ["cdylib", "rlib"] @@ -23,9 +20,6 @@ strip = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -piston-models = { path = "../piston-models" } -piston-nn = { path = "../piston-nn" } -piston-datasets = { path = "../piston-datasets" } piston-macros = { path = "../piston-macros" } piston = { path = "../piston-core" } half = { workspace = true } @@ -34,31 +28,25 @@ wasm-bindgen = { workspace = true } safetensors = { workspace = true } wasm-bindgen-futures = { workspace = true } js-sys = { workspace = true } -thiserror.workspace = true anyhow.workspace = true serde = { workspace = true } -serde_json = { workspace = true } serde-wasm-bindgen = { workspace = true } console_error_panic_hook = { workspace = true } console_log = { workspace = true } parking_lot = { workspace = true } log.workspace = true -chrono = { workspace = true } fern = { workspace = true } -tokenizers = { version = "0.19.1", default-features = false, features = [ - "unstable_wasm", -] } futures = "0.3.30" async-trait = { workspace = true } -paste = { workspace = true } tsify = { workspace = true } -smallvec = { workspace = true } + [dependencies.web-sys] features = ['console', 'Window', 'Navigator'] workspace = true - [dev-dependencies] wasm-bindgen-test.workspace = true -serde_json = { workspace = true } -ndarray = { workspace = true } +chrono = { workspace = true } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +getrandom = { workspace = true, features = ["js"] } diff --git a/crates/piston-web/src/lib.rs b/crates/piston-web/src/lib.rs index 2fa37c3d..8f195492 100644 --- a/crates/piston-web/src/lib.rs +++ b/crates/piston-web/src/lib.rs @@ -4,7 +4,6 @@ mod dtype; mod error; mod function; mod js_util; -mod model; mod serialization; mod shape; mod tensor; diff --git a/crates/piston-web/src/model.rs b/crates/piston-web/src/model.rs deleted file mode 100644 index ae7d31a6..00000000 --- a/crates/piston-web/src/model.rs +++ /dev/null @@ -1,690 +0,0 @@ -use crate::error::IntoJsError; -use anyhow::Result; -use async_trait::async_trait; -use js_sys::Function; -use piston::{ - AllDims, DType, Device, DeviceRequest, StepLog, Tensor, TensorOptions, reset_scope_context, -}; -use piston_models::gpt2::generate; -use piston_models::gpt2::{Config, GPT2Input, PositionalEncoding}; -use piston_models::gpt2::{GPT2, LayerNormPosition}; -use piston_nn::{ - Activation, AdamW, ConstantLR, CosineAnnealingLR, LRScheduler, LRSchedulerCore, LinearLR, - Module, Optimizer, ParamsAdamW, SGD, VarBuilder, VarMap, label_smoothed_nll, log_softmax, - nll_masked, -}; -use piston_nn::{ModuleMode, ModuleModeGuard}; -use serde::{Deserialize, Serialize}; -use std::cell::RefCell; -use std::iter::Iterator; -use wasm_bindgen::prelude::*; - -#[derive(Serialize, Deserialize)] -pub struct OptimizerConfig { - pub optimizer_type: String, // "adamw" or "sgd" - pub lr: f64, - #[serde(default = "default_beta1")] - pub beta1: f64, - #[serde(default = "default_beta2")] - pub beta2: f64, - #[serde(default = "default_eps")] - pub eps: f64, - #[serde(default = "default_weight_decay")] - pub weight_decay: f64, - #[serde(default = "default_momentum")] - pub momentum: f64, // For SGD - #[serde(default = "default_scheduler_type")] - pub scheduler_type: String, // "none", "constant", "linear", "cosine" - #[serde(default = "default_scheduler_factor")] - pub scheduler_factor: f64, // For constant/linear scheduler - #[serde(default = "default_scheduler_steps")] - pub scheduler_steps: usize, // For all schedulers - #[serde(default = "default_scheduler_eta_min")] - pub scheduler_eta_min: f64, // For cosine scheduler -} - -fn default_beta1() -> f64 { - 0.9 -} -fn default_beta2() -> f64 { - 0.999 -} -fn default_eps() -> f64 { - 1e-8 -} -fn default_weight_decay() -> f64 { - 0.0 -} -fn default_momentum() -> f64 { - 0.0 -} -fn default_scheduler_type() -> String { - "none".to_string() -} -fn default_scheduler_factor() -> f64 { - 1.0 -} -fn default_scheduler_steps() -> usize { - 1000 -} -fn default_scheduler_eta_min() -> f64 { - 0.0 -} - -#[derive(Serialize, Deserialize)] -#[serde(untagged)] -pub enum JsDebugSelection { - String(String), - Array(Vec), -} - -#[derive(Serialize, Deserialize)] -pub struct JsStepLogConfig { - #[serde(default = "default_profiling")] - pub profiling: bool, - #[serde(default = "default_debug_selection")] - pub debug_selection: Option, -} - -fn default_profiling() -> bool { - false -} - -fn default_debug_selection() -> Option { - None -} - -#[derive(Serialize, Deserialize)] -pub struct TrainerConfig { - pub vocab_size: usize, - pub n_embd: usize, - pub n_layer: usize, - pub n_head: usize, - pub block_size: usize, - pub batch_size: usize, - #[serde(default = "default_caching_enabled")] - pub caching_enabled: bool, - #[serde(default = "default_inplace_support")] - pub inplace_support: bool, - #[serde(default = "default_optimizer_config")] - pub optimizer: OptimizerConfig, - #[serde(default = "default_activation")] - pub activation: String, - #[serde(default = "default_attention_only")] - pub attention_only: bool, - #[serde(default = "default_positional_encoding")] - pub positional_encoding: String, - #[serde(default = "default_layernorm_position")] - pub layernorm_position: String, - #[serde(default = "default_seed")] - pub seed: Option, - #[serde(default = "default_label_smoothing")] - pub label_smoothing: f32, - #[serde(default = "default_embd_pdrop")] - pub embd_pdrop: f32, - #[serde(default = "default_attn_pdrop")] - pub attn_pdrop: f32, - #[serde(default = "default_resid_pdrop")] - pub resid_pdrop: f32, -} - -fn default_caching_enabled() -> bool { - true -} - -fn default_inplace_support() -> bool { - true -} - -fn default_optimizer_config() -> OptimizerConfig { - OptimizerConfig { - optimizer_type: "adamw".to_string(), - lr: 1e-3, - beta1: default_beta1(), - beta2: default_beta2(), - eps: default_eps(), - weight_decay: default_weight_decay(), - momentum: default_momentum(), - scheduler_type: default_scheduler_type(), - scheduler_factor: default_scheduler_factor(), - scheduler_steps: default_scheduler_steps(), - scheduler_eta_min: default_scheduler_eta_min(), - } -} - -fn default_activation() -> String { - "gelu".to_string() -} - -fn default_attention_only() -> bool { - false -} - -fn default_positional_encoding() -> String { - "learned".to_string() -} - -fn default_layernorm_position() -> String { - "pre".to_string() -} - -fn default_embd_pdrop() -> f32 { - 0.1 -} - -fn default_attn_pdrop() -> f32 { - 0.1 -} - -fn default_resid_pdrop() -> f32 { - 0.1 -} - -fn default_seed() -> Option { - None -} - -fn default_label_smoothing() -> f32 { - 0.0 -} - -fn string_to_activation(s: &str) -> Activation { - match s.to_lowercase().as_str() { - "gelu" => Activation::Gelu, - "relu" => Activation::Relu, - "relu2" => Activation::Relu2, - "silu" => Activation::Silu, - "sigmoid" => Activation::Sigmoid, - "swiglu" => Activation::Swiglu, - _ => Activation::Gelu, - } -} - -#[derive(Debug, Clone)] -pub enum OptimizerConfigEnum { - AdamW(ParamsAdamW), - SGD(f64), -} - -pub enum OptimizerEnum { - AdamW(AdamW), - SGD(SGD), -} - -#[async_trait] -impl Optimizer for OptimizerEnum { - type Config = OptimizerConfigEnum; - - fn new(vars: Vec, config: Self::Config) -> anyhow::Result { - match config { - OptimizerConfigEnum::AdamW(params) => Ok(Self::AdamW(AdamW::new(vars, params)?)), - OptimizerConfigEnum::SGD(params) => Ok(Self::SGD(SGD::new(vars, params)?)), - } - } - - async fn step(&mut self, device: &Device) -> anyhow::Result<()> { - match self { - OptimizerEnum::AdamW(opt) => opt.step(device).await, - OptimizerEnum::SGD(opt) => opt.step(device).await, - } - } - - fn learning_rate(&self) -> f64 { - match self { - OptimizerEnum::AdamW(opt) => opt.learning_rate(), - OptimizerEnum::SGD(opt) => opt.learning_rate(), - } - } - - fn set_learning_rate(&mut self, lr: f64) { - match self { - OptimizerEnum::AdamW(opt) => opt.set_learning_rate(lr), - OptimizerEnum::SGD(opt) => opt.set_learning_rate(lr), - } - } - - fn parameters(&self) -> Vec<&Tensor> { - match self { - OptimizerEnum::AdamW(opt) => opt.parameters(), - OptimizerEnum::SGD(opt) => opt.parameters(), - } - } -} - -#[wasm_bindgen] -pub struct Trainer { - model: GPT2, - varmap: VarMap, - optimizer: Box + Send + Sync>, - device: Device, - config: TrainerConfig, -} - -#[wasm_bindgen] -impl Trainer { - #[wasm_bindgen(constructor)] - pub async fn new(config: JsValue) -> Result { - let cfg: TrainerConfig = - serde_wasm_bindgen::from_value(config).map_err(|e| JsValue::from(e.to_string()))?; - - let device = Device::request_device(DeviceRequest::GPU) - .await - .map_err(|e| e.to_string())?; - - // Set seed on device if provided - if let Some(seed) = cfg.seed { - device.set_seed(seed); - } - - let varmap = VarMap::new(); - let vb = VarBuilder::from_varmap(&varmap, DType::F32, &device); - - let gpt2_config = Config { - vocab_size: cfg.vocab_size, - hidden_act: string_to_activation(&cfg.activation), - n_embd: cfg.n_embd, - n_layer: cfg.n_layer, - n_head: cfg.n_head, - block_size: cfg.block_size, - attention_only: cfg.attention_only, - positional_encoding: match cfg.positional_encoding.to_lowercase().as_str() { - "rope" => PositionalEncoding::RoPE, - "alibi" => PositionalEncoding::ALiBi, - "sinusoidal" => PositionalEncoding::Sinusoidal, - _ => PositionalEncoding::Learned, - }, - layernorm_position: match cfg.layernorm_position.to_lowercase().as_str() { - "pre" => LayerNormPosition::Pre, - "post" => LayerNormPosition::Post, - "none" => LayerNormPosition::None, - _ => LayerNormPosition::Pre, - }, - embd_pdrop: cfg.embd_pdrop, - attn_pdrop: cfg.attn_pdrop, - resid_pdrop: cfg.resid_pdrop, - }; - - device - .try_gpu() - .unwrap() - .set_caching_enabled(cfg.caching_enabled); - - device - .try_gpu() - .unwrap() - .set_inplace_support(cfg.inplace_support); - - let model = GPT2::new(&gpt2_config, vb, false) - .await - .map_err(|e| e.to_string())?; - - let vars = varmap - .all_vars() - .iter() - .map(|var| var.to_owned()) - .collect::>(); - - let optimizer_config = match cfg.optimizer.optimizer_type.to_lowercase().as_str() { - "sgd" => OptimizerConfigEnum::SGD(cfg.optimizer.lr), - _ => OptimizerConfigEnum::AdamW(ParamsAdamW { - lr: cfg.optimizer.lr, - beta1: cfg.optimizer.beta1, - beta2: cfg.optimizer.beta2, - eps: cfg.optimizer.eps, - weight_decay: cfg.optimizer.weight_decay, - }), - }; - - let base_optimizer = - OptimizerEnum::new(vars, optimizer_config).map_err(|e| e.to_string())?; - - // Wrap the optimizer in the selected scheduler - let optimizer: Box + Send + Sync> = - match cfg.optimizer.scheduler_type.to_lowercase().as_str() { - "constant" => Box::new(ConstantLR::new( - base_optimizer, - cfg.optimizer.scheduler_factor, - cfg.optimizer.scheduler_steps, - )), - "linear" => Box::new(LinearLR::new( - base_optimizer, - 1.0, - cfg.optimizer.scheduler_factor, - cfg.optimizer.scheduler_steps, - )), - "cosine" => Box::new(CosineAnnealingLR::new( - base_optimizer, - cfg.optimizer.scheduler_steps, - cfg.optimizer.scheduler_eta_min, - )), - _ => Box::new(NoopScheduler::new(base_optimizer)), - }; - - Ok(Trainer { - model, - varmap, - optimizer, - device, - config: cfg, - }) - } - - async fn forward(&mut self, input: Vec>) -> anyhow::Result<(Tensor, Tensor)> { - let batch_size = input.len(); - let seq_len = input[0].len(); - - let input_flat: Vec = input.into_iter().flatten().collect(); - let input_tensor = Tensor::from_data( - input_flat, - (batch_size, seq_len), - TensorOptions::new().device(self.device.clone()), - )?; - - let (logits, attn_masks) = self.model.schedule(GPT2Input { - x: input_tensor, - index_pos: 0, - })?; - Ok((logits, attn_masks)) - } - - #[wasm_bindgen(js_name = "forward")] - pub async fn forward_js(&mut self, input: JsValue) -> Result { - reset_scope_context(); - let input: Vec> = - serde_wasm_bindgen::from_value(input).map_err(|e| JsValue::from(e.to_string()))?; - let (logits, _) = self.forward(input).await.map_err(|e| e.to_string())?; - - let logits_cpu = logits.to(&Device::CPU).await.map_err(|e| e.to_string())?; - let logits_shape = logits_cpu.shape().to_vec(); - let logits_data = logits_cpu - .to_vec::() - .await - .map_err(|e| e.to_string())?; - - Ok( - serde_wasm_bindgen::to_value(&(logits_data, logits_shape)) - .map_err(|e| e.to_string())?, - ) - } - - /// Train on a single batch of data provided directly from JavaScript - #[wasm_bindgen] - pub async fn train_on_batch( - &mut self, - input: JsValue, - target: JsValue, - ) -> Result { - reset_scope_context(); - let _train_mode_guard = ModuleModeGuard::new(ModuleMode::Train); - // Convert input and target from JS arrays to Vec> - let input: Vec> = - serde_wasm_bindgen::from_value(input).map_err(|e| JsValue::from(e.to_string()))?; - let target: Vec> = - serde_wasm_bindgen::from_value(target).map_err(|e| JsValue::from(e.to_string()))?; - - if input.is_empty() || target.is_empty() { - return Err("Empty batch provided".into()); - }; - - let batch_size = input.len(); - let seq_len = input[0].len(); - if batch_size != target.len() - || input.iter().any(|x| x.len() != seq_len) - || target.iter().any(|x| x.len() != seq_len) - { - return Err("Inconsistent batch dimensions".into()); - } - - let (logits, attn_masks) = self.forward(input).await.map_err(|e| e.to_string())?; - - let target_flat: Vec = target.into_iter().flatten().collect(); - let target_tensor = Tensor::from_data( - target_flat, - (batch_size, seq_len), - TensorOptions::new().device(self.device.clone()), - ) - .map_err(|e| e.into_js_error())?; - - // Flatten the logits and targets, compute the cross-entropy loss with label smoothing - let logits_flat = logits.clone().flatten(0, 1).map_err(|e| e.to_string())?; - let target_flat = target_tensor.flatten(0, 1).map_err(|e| e.to_string())?; - let inp = log_softmax(logits_flat, 1).map_err(|e| e.to_string())?; - - let alpha = self.config.label_smoothing; - let loss = if alpha > 0.0 { - label_smoothed_nll(inp, target_flat, alpha).map_err(|e| e.to_string())? - } else { - nll_masked(inp, target_flat).map_err(|e| e.to_string())? - }; - - // We sum the loss outside of x entropy because we want to see per-token loss - let loss_summed = loss - .clone() - .sum(AllDims, false) - .map_err(|e| e.to_string())?; - - loss_summed.backward().map_err(|e| e.to_string())?; - - // Run an optimizer step (updating model parameters). - self.optimizer - .step(&self.device) - .await - .map_err(|e| e.to_string())?; - - // Transfer tensors to CPU - let loss_cpu = loss.to(&Device::CPU).await.map_err(|e| e.to_string())?; - let loss_vec = loss_cpu.to_vec::().await.map_err(|e| e.to_string())?; - - // Transfer attention masks to CPU and get their shape and flattened data - let attn_masks_cpu = attn_masks - .to(&Device::CPU) - .await - .map_err(|e| e.to_string())?; - let attn_masks_shape = attn_masks_cpu.shape().to_vec(); - let attn_masks_data = attn_masks_cpu - .to_vec::() - .await - .map_err(|e| e.to_string())?; - - // Add logits to the result: - let logits_cpu = logits.to(&Device::CPU).await.map_err(|e| e.to_string())?; - let logits_shape = logits_cpu.shape().to_vec(); - let logits_data = logits_cpu - .to_vec::() - .await - .map_err(|e| e.to_string())?; - - let result = serde_json::json!({ - "loss": { - "tokens": loss_vec, - "total": loss_vec.iter().sum::(), - }, - "learning_rate": self.optimizer.get_lr(), - "attn_masks": { - "data": attn_masks_data, - "shape": attn_masks_shape, - }, - "logits": { - "data": logits_data, - "shape": logits_shape, - } - }); - - serde_wasm_bindgen::to_value(&result).map_err(|e| e.to_string().into()) - } - - #[wasm_bindgen] - pub fn usage_bytes(&self) -> u64 { - self.device.try_gpu().unwrap().usage_bytes() - } - - #[wasm_bindgen] - pub async fn save_checkpoint(&self) -> Result { - self.varmap.download_url().await - } - - #[wasm_bindgen] - pub fn set_step_log_config(&mut self, config: JsValue) -> Result<(), JsValue> { - let config: JsStepLogConfig = - serde_wasm_bindgen::from_value(config).map_err(|e| JsValue::from(e.to_string()))?; - self.device - .try_gpu() - .unwrap() - .set_step_log_config(piston::StepLogConfig { - profiling: config.profiling, - debug_selection: match config.debug_selection { - Some(JsDebugSelection::String(s)) => match s.as_str() { - "all" => Some(piston::DebugSelection::All), - _ => None, - }, - Some(JsDebugSelection::Array(a)) => Some(piston::DebugSelection::Some(a)), - None => None, - }, - }); - Ok(()) - } - - #[wasm_bindgen] - pub fn take_step_log(&mut self) -> Option { - self.device.try_gpu().unwrap().take_step_log() - } - - #[wasm_bindgen] - pub async fn generate( - &mut self, - prompt: JsValue, - max_tokens: usize, - callback: Option, - ) -> Result { - // Convert prompt from JsValue -> Vec - let prompt_vec: Vec = - serde_wasm_bindgen::from_value(prompt).map_err(|e| JsValue::from(e.to_string()))?; - - // If a callback is provided, we do the streaming approach. - if let Some(callback_fn) = callback { - // Call existing GPT-2 generate with a streaming closure: - generate( - &mut self.model, - prompt_vec, - |tokens, logits_nd, attn_probs_data| { - // Convert the tokens to JS - let tokens_js = serde_wasm_bindgen::to_value(&tokens).unwrap_or(JsValue::NULL); - - // Convert the logits to a {shape, data} object - let shape = vec![ - logits_nd.shape()[0], - logits_nd.shape()[1], - logits_nd.shape()[2], - ]; - let data: Vec = logits_nd.into_iter().collect(); - - let logits_obj = js_sys::Object::new(); - let _ = js_sys::Reflect::set( - &logits_obj, - &JsValue::from_str("shape"), - &serde_wasm_bindgen::to_value(&shape).unwrap_or(JsValue::NULL), - ); - let _ = js_sys::Reflect::set( - &logits_obj, - &JsValue::from_str("data"), - &serde_wasm_bindgen::to_value(&data).unwrap_or(JsValue::NULL), - ); - - let attn_probs_obj = js_sys::Object::new(); - let _ = js_sys::Reflect::set( - &attn_probs_obj, - &JsValue::from_str("data"), - &serde_wasm_bindgen::to_value(&attn_probs_data).unwrap_or(JsValue::NULL), - ); - // Finally, call the JS callback with (tokens, logitsObj) - let _ = - callback_fn.call3(&JsValue::NULL, &tokens_js, &logits_obj, &attn_probs_obj); - }, - max_tokens, - ) - .await - .map_err(|e| JsValue::from(e.to_string()))?; - - // Return undefined if we're streaming via callback - Ok(JsValue::UNDEFINED) - } else { - // No callback was provided, so we accumulate only the final tokens/logits and return them. - let final_tokens: RefCell> = RefCell::new(Vec::new()); - let final_logits_data: RefCell> = RefCell::new(Vec::new()); - let final_logits_shape: RefCell> = RefCell::new(Vec::new()); - - let prompt_len = prompt_vec.len(); - - // We capture these as mut so we can overwrite them at each step with the latest pass - generate( - &mut self.model, - prompt_vec, - |tokens, logits_nd, _attn_probs_data| { - // Update tokens - final_tokens.borrow_mut().extend_from_slice(&tokens); - - // Convert the logits into shape/data only for the final pass - final_logits_shape.borrow_mut().extend_from_slice(&[ - logits_nd.shape()[0], - logits_nd.shape()[1], - logits_nd.shape()[2], - ]); - - final_logits_data.borrow_mut().extend(logits_nd.into_iter()); - - // Note that we don't bother passing along the attn_probs_data here. - // This is inconsistent but we'll deal with it if it becomes an issue. - }, - max_tokens, - ) - .await - .map_err(|e| JsValue::from(e.to_string()))?; - - let result = serde_json::json!({ - "tokens": final_tokens.borrow()[prompt_len..].to_vec(), - "logits": { - "shape": final_logits_shape.borrow().to_vec(), - "data": final_logits_data.borrow().to_vec(), - }, - }); - - Ok(serde_wasm_bindgen::to_value(&result).map_err(|e| e.to_string())?) - } - } -} - -/// A scheduler that does nothing, just passes through to the underlying optimizer. -/// This is used when no scheduler is selected. -pub struct NoopScheduler { - core: LRSchedulerCore, -} - -impl NoopScheduler { - pub fn new(optimizer: O) -> Self { - Self { - core: LRSchedulerCore::new(optimizer), - } - } -} - -impl LRScheduler for NoopScheduler { - fn optimizer_mut(&mut self) -> &mut O { - &mut self.core.optimizer - } - - fn base_lr(&self) -> f64 { - self.core.base_lr - } - - fn step_count(&self) -> usize { - self.core.step_count - } - - fn set_step_count(&mut self, count: usize) { - self.core.step_count = count; - } - - fn compute_lr(&self) -> f64 { - self.core.base_lr - } -} diff --git a/packages/web/src/wasm.ts b/packages/web/src/wasm.ts index ae094d85..7fe442af 100644 --- a/packages/web/src/wasm.ts +++ b/packages/web/src/wasm.ts @@ -85,7 +85,6 @@ export { tanh, Tensor as Tensor_wasm, topk, - Trainer as Trainer_wasm, uint32 as uint32_wasm, default as wasmInit, zeros, From 4f8e9fd969b88a667c0838df2d40125061f5b571 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 26 Oct 2025 14:50:11 -0600 Subject: [PATCH 508/590] Update custom wasm-bindgen --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 16cc688f..fe3fad39 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,7 +45,7 @@ anyhow = "1.0.79" tokenizers = "0.19.1" js-sys = "0.3.64" -wasm-bindgen = "0.2.100" +wasm-bindgen = "0.2.104" wasm-bindgen-test = "0.3.34" cfg-if = "1.0.0" chrono = "0.4.35" @@ -108,4 +108,4 @@ paste = { version = "1.0.15" } tsify = { version = "0.5.5", features = ["js"] } [patch.crates-io] -wasm-bindgen = { git = "https://github.com/vinhowe/wasm-bindgen", branch = "unchecked-prelude" } \ No newline at end of file +wasm-bindgen = { git = "https://github.com/vinhowe/wasm-bindgen", rev = "4b4f9cd9731cf35725727bcac92940d51a559a50" } From 0f65a310b42130868f9681941be2e07abe65c136 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 26 Oct 2025 14:50:43 -0600 Subject: [PATCH 509/590] Remove a bunch of unused packages --- crates/piston-core/Cargo.toml | 7 +++---- crates/piston-datasets/Cargo.toml | 4 ---- crates/piston-nn/Cargo.toml | 3 +-- crates/piston-web/Cargo.toml | 5 +++-- 4 files changed, 7 insertions(+), 12 deletions(-) diff --git a/crates/piston-core/Cargo.toml b/crates/piston-core/Cargo.toml index e1b1ad6b..230797bb 100644 --- a/crates/piston-core/Cargo.toml +++ b/crates/piston-core/Cargo.toml @@ -55,10 +55,6 @@ regex = { workspace = true, optional = true } numpy = { workspace = true, optional = true, features = ["half"] } gemm = { version = "0.18.0", features = ["nightly", "wasm-simd128-enable"] } bitvec = { workspace = true } -paste = { workspace = true } -bitflags = "2.9.1" -inventory = "0.3.20" -once_cell = "1.21.3" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] maybe-async = { workspace = true, features = ["is_sync"] } @@ -82,3 +78,6 @@ rand = { workspace = true } test-strategy = { workspace = true } ndarray = { workspace = true } proptest = { workspace = true } + +[package.metadata.cargo-machete] +ignored = ["strum"] \ No newline at end of file diff --git a/crates/piston-datasets/Cargo.toml b/crates/piston-datasets/Cargo.toml index cf7a0c18..3952f1b2 100644 --- a/crates/piston-datasets/Cargo.toml +++ b/crates/piston-datasets/Cargo.toml @@ -26,10 +26,6 @@ wasm-opt = ['-O3', '--enable-simd'] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] piston = { path = "../piston-core" } -log.workspace = true -tokenizers = { version = "0.19.1", default-features = false, features = [ - "unstable_wasm", -] } anyhow.workspace = true memmap2.workspace = true rand.workspace = true diff --git a/crates/piston-nn/Cargo.toml b/crates/piston-nn/Cargo.toml index 526f3e70..350bc87b 100644 --- a/crates/piston-nn/Cargo.toml +++ b/crates/piston-nn/Cargo.toml @@ -14,12 +14,10 @@ debug = ["piston/debug"] anyhow.workspace = true log.workspace = true derive-new = { workspace = true } -half = { workspace = true } piston = { path = "../piston-core" } piston-macros = { path = "../piston-macros" } wasm-bindgen = { workspace = true } wasm-bindgen-futures = { workspace = true } -wasm-bindgen-test = { workspace = true } safetensors = { workspace = true } thiserror = { workspace = true } ndarray = { workspace = true } @@ -40,6 +38,7 @@ test-strategy = { workspace = true } tokenizers = { version = "0.19.1", default-features = false, features = [ "unstable_wasm", ] } +wasm-bindgen-test = { workspace = true } [target.'cfg(target_arch = "wasm32")'.dependencies.web-sys] features = ["Blob", "Url"] diff --git a/crates/piston-web/Cargo.toml b/crates/piston-web/Cargo.toml index 0913ef57..b2603092 100644 --- a/crates/piston-web/Cargo.toml +++ b/crates/piston-web/Cargo.toml @@ -23,7 +23,6 @@ strip = true piston-macros = { path = "../piston-macros" } piston = { path = "../piston-core" } half = { workspace = true } -wgpu = { workspace = true } wasm-bindgen = { workspace = true } safetensors = { workspace = true } wasm-bindgen-futures = { workspace = true } @@ -37,7 +36,6 @@ parking_lot = { workspace = true } log.workspace = true fern = { workspace = true } futures = "0.3.30" -async-trait = { workspace = true } tsify = { workspace = true } [dependencies.web-sys] @@ -50,3 +48,6 @@ chrono = { workspace = true } [target.'cfg(target_arch = "wasm32")'.dependencies] getrandom = { workspace = true, features = ["js"] } + +[package.metadata.cargo-machete] +ignored = ["tsify"] From 6f2b34f8b42b0f5bbf3d6e40ceb339b891f58108 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 26 Oct 2025 15:01:53 -0600 Subject: [PATCH 510/590] Update pnpm --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 31ea4f6d..beea8138 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "piston-repo", "version": "0.0.0", - "packageManager": "pnpm@10.13.1", + "packageManager": "pnpm@10.15.0", "private": true, "devDependencies": { "pkg-pr-new": "0.0.15", From a2661a339d2a1f5e1a21555e59412abae769ec42 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 26 Oct 2025 15:02:43 -0600 Subject: [PATCH 511/590] Delete old example source --- .../lib/components/ActivationPicker.svelte | 79 - .../src/lib/components/BaseSlider.svelte | 96 - .../src/lib/components/LogSlider.svelte | 127 -- .../src/lib/components/Slider.svelte | 25 - .../src/lib/components/TickSlider.svelte | 105 -- examples/piston-train-toy/src/lib/index.ts | 0 examples/piston-train-toy/src/lib/tasks.ts | 792 --------- .../piston-train-toy/src/lib/trainWorker.ts | 259 --- .../src/routes/old-trainer/+layout.svelte | 6 - .../src/routes/old-trainer/+layout.ts | 1 - .../src/routes/old-trainer/+page.svelte | 1572 ----------------- 11 files changed, 3062 deletions(-) delete mode 100644 examples/piston-train-toy/src/lib/components/ActivationPicker.svelte delete mode 100644 examples/piston-train-toy/src/lib/components/BaseSlider.svelte delete mode 100644 examples/piston-train-toy/src/lib/components/LogSlider.svelte delete mode 100644 examples/piston-train-toy/src/lib/components/Slider.svelte delete mode 100644 examples/piston-train-toy/src/lib/components/TickSlider.svelte delete mode 100644 examples/piston-train-toy/src/lib/index.ts delete mode 100644 examples/piston-train-toy/src/lib/tasks.ts delete mode 100644 examples/piston-train-toy/src/lib/trainWorker.ts delete mode 100644 examples/piston-train-toy/src/routes/old-trainer/+layout.svelte delete mode 100644 examples/piston-train-toy/src/routes/old-trainer/+layout.ts delete mode 100644 examples/piston-train-toy/src/routes/old-trainer/+page.svelte diff --git a/examples/piston-train-toy/src/lib/components/ActivationPicker.svelte b/examples/piston-train-toy/src/lib/components/ActivationPicker.svelte deleted file mode 100644 index 5ba7fe76..00000000 --- a/examples/piston-train-toy/src/lib/components/ActivationPicker.svelte +++ /dev/null @@ -1,79 +0,0 @@ - - -
    -
    - -
    - -
    -
    -
    - - - - - - - - -
    -
    \ No newline at end of file diff --git a/examples/piston-train-toy/src/lib/components/BaseSlider.svelte b/examples/piston-train-toy/src/lib/components/BaseSlider.svelte deleted file mode 100644 index eacc08b5..00000000 --- a/examples/piston-train-toy/src/lib/components/BaseSlider.svelte +++ /dev/null @@ -1,96 +0,0 @@ - - -
    - -
    - -
    -
    - - -
    -
    -
    - - \ No newline at end of file diff --git a/examples/piston-train-toy/src/lib/components/LogSlider.svelte b/examples/piston-train-toy/src/lib/components/LogSlider.svelte deleted file mode 100644 index 63372085..00000000 --- a/examples/piston-train-toy/src/lib/components/LogSlider.svelte +++ /dev/null @@ -1,127 +0,0 @@ - - - -
    - {formatNumber(logValue)} -
    - -
    - {#each ticks as tick} - {@const position = `${((tick.value - minExp) / (maxExp - minExp)) * 100}%`} - {@const isSnapPoint = snapPoints.includes(tick.value)} -
    -
    - {#if tick.type === 'major'} -
    - {@html tick.label} -
    - {/if} -
    - {/each} -
    - - - \ No newline at end of file diff --git a/examples/piston-train-toy/src/lib/components/Slider.svelte b/examples/piston-train-toy/src/lib/components/Slider.svelte deleted file mode 100644 index 1ba7c119..00000000 --- a/examples/piston-train-toy/src/lib/components/Slider.svelte +++ /dev/null @@ -1,25 +0,0 @@ - - - -
    - {formatter(value)} -
    - -
    \ No newline at end of file diff --git a/examples/piston-train-toy/src/lib/components/TickSlider.svelte b/examples/piston-train-toy/src/lib/components/TickSlider.svelte deleted file mode 100644 index 32bd4f51..00000000 --- a/examples/piston-train-toy/src/lib/components/TickSlider.svelte +++ /dev/null @@ -1,105 +0,0 @@ - - - -
    - {formatter(value)} -
    - -
    - {#each tickPositions as tick} -
    -
    -
    - {/each} -
    - - -
    - - diff --git a/examples/piston-train-toy/src/lib/index.ts b/examples/piston-train-toy/src/lib/index.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/piston-train-toy/src/lib/tasks.ts b/examples/piston-train-toy/src/lib/tasks.ts deleted file mode 100644 index d43d8fe2..00000000 --- a/examples/piston-train-toy/src/lib/tasks.ts +++ /dev/null @@ -1,792 +0,0 @@ -// Task-specific configuration types -export interface TrainBatchConfig { - batchSize: number; -} - -export interface NumberSequenceConfig { - seqLen: number; - maxNum: number; - maskOutPrefix: boolean; - includeCommas: boolean; - includeColon: boolean; -} - -export interface AdditionConfig { - maxNum: number; - includeExpressionTokens: boolean; - maskOutPrefix: boolean; -} - -export interface ModAdditionConfig { - maxNum: number; - includeExpressionTokens: boolean; - maskOutPrefix: boolean; -} - -export interface FixedLengthConfig { - seqLen: number; -} - -// Task metadata for UI configuration -export interface TaskParameter { - name: string; - description?: string; - type?: 'number' | 'boolean'; // Default to 'number' if not specified - min?: number; // Only for number type - max?: number; // Only for number type - default: number | boolean; -} - -export interface TaskMetadata { - name: string; - description: string; - parameters: Record; - // For tasks using typical tokenization (zeros, count, slapjack) - vocab?: string; -} - -export const taskMetadata: Record = { - sort: { - name: 'Sorting', - description: 'Learn to sort a sequence of characters', - parameters: { - seqLen: { - name: 'Sequence Length', - description: 'Number of items to sort', - type: 'number', - min: 2, - max: 10, - default: 2 - }, - maxNum: { - name: 'Max Number', - description: 'Maximum value in the sequence', - type: 'number', - min: 10, - max: 26, - default: 26 - }, - maskOutPrefix: { - name: 'Mask Out Prefix', - type: 'boolean', - default: true - }, - includeCommas: { - name: 'Include Commas', - type: 'boolean', - default: false - }, - includeColon: { - name: 'Include Colon', - type: 'boolean', - default: true - } - } - }, - repeat: { - name: 'Repeat', - description: 'Learn to repeat a sequence of characters', - parameters: { - seqLen: { - name: 'Sequence Length', - description: 'Number of items to repeat', - type: 'number', - min: 1, - max: 10, - default: 3, - }, - maxNum: { - name: 'Max Number', - description: 'Maximum value in the sequence', - type: 'number', - min: 5, - max: 26, - default: 5 - }, - maskOutPrefix: { - name: 'Mask Out Prefix', - type: 'boolean', - default: true, - }, - includeCommas: { - name: 'Include Commas', - type: 'boolean', - default: false - }, - includeColon: { - name: 'Include Colon', - type: 'boolean', - default: true - } - } - }, - add: { - name: 'Addition', - description: 'Learn to add two numbers', - parameters: { - maxNum: { - name: 'Max Number', - description: 'Maximum value for each addend', - type: 'number', - min: 10, - max: 100, - default: 100 - }, - maskOutPrefix: { - name: 'Mask Out Prefix', - type: 'boolean', - default: false - }, - includeExpressionTokens: { - name: 'Include Expression Tokens (+, =)', - type: 'boolean', - default: false - } - } - }, - mod_add: { - name: 'Modular Addition', - description: 'Learn to add two numbers with modulo', - parameters: { - maxNum: { - name: 'Max Number', - description: 'The modulo to use', - type: 'number', - min: 10, - max: 113, - default: 113 - }, - maskOutPrefix: { - name: 'Mask Out Prefix', - type: 'boolean', - default: false - }, - includeExpressionTokens: { - name: 'Include Expression Tokens (+, =)', - type: 'boolean', - default: false - } - } - }, - zeros: { - name: 'Zeros', - description: 'Learn to output zeros (baseline/debug task)', - parameters: { - seqLen: { - name: 'Sequence Length', - description: 'Length of the sequence', - type: 'number', - min: 2, - max: 10, - default: 5 - } - }, - vocab: '0123456789' - }, - two_sum: { - name: 'Two Sum', - description: 'Find two numbers in a sequence that sum to a target', - parameters: { - seqLen: { - name: 'Sequence Length', - description: 'Length of the sequence', - type: 'number', - min: 2, - max: 10, - default: 3 - }, - maxNum: { - name: 'Max Number', - description: 'Maximum value in the sequence', - type: 'number', - min: 10, - max: 26, - default: 26 - }, - maskOutPrefix: { - name: 'Mask Out Prefix', - type: 'boolean', - default: false - }, - includeCommas: { - name: 'Include Commas', - type: 'boolean', - default: false - } - } - } - /* - // (Commented: these tasks use the typical tokenization.) - count: { - name: 'Counting', - description: 'Learn to count the occurrences of a character in a sequence', - parameters: { - seqLen: { - name: 'Sequence Length', - description: 'Length of the counting sequence', - min: 2, - max: 10, - default: 5 - }, - maxNum: { - name: 'Max Number', - description: 'Maximum starting number', - min: 10, - max: 100, - default: 100 - } - } - }, - slapjack: { - name: 'Slapjack', - description: 'Learn to find a specific number in a sequence', - parameters: { - seqLen: { - name: 'Sequence Length', - description: 'Length of the sequence', - min: 2, - max: 10, - default: 5 - } - } - } - */ -}; - -export type SimpleTokenizer = { - vocab: Record; - ids: Record; - lastToken: number; -}; - -/** - * Given a prompt and a completion (both as token arrays), concatenate them to form a full sequence. - * Then, create an autoregressive pair where the input is fullSequence[:-1] and the target is fullSequence[1:], - * with all tokens corresponding to the prompt (except for its final token) masked to -100. - */ -export function createAutoregressivePair( - prompt: T[], - completion: T[], - maskOutPrefix: boolean -): [T[], (T | number)[]] { - const fullSequence = prompt.concat(completion); - const input = fullSequence.slice(0, fullSequence.length - 1); - const target = fullSequence.slice(1); - // Mask the positions corresponding to the prompt (all but the last token of the prompt) - if (maskOutPrefix) { - const maskedTarget = target.map((tok, i) => (i < prompt.length - 1 ? -100 : tok)); - return [input, maskedTarget]; - } else { - return [input, target]; - } -} - -/** - * For tasks that use number blocks we define custom tokenizers. - * - * For Add (and Mod-Add): - * token 0 → "+" - * token 1 → "=" - * tokens 2 … maxNum+2 represent the numbers 0 … maxNum - * (When converting to a string, token id i ≥ 2 becomes `<${i-2}>`.) - */ -export function createAddTokenizer( - maxNum: number, - includeExpressionTokens: boolean -): SimpleTokenizer { - const vocab: Record = {}; - const ids: Record = {}; - let offset = 0; - if (includeExpressionTokens) { - vocab['+'] = 0; - ids[0] = '+'; - vocab['='] = 1; - ids[1] = '='; - offset = 2; - } - const width = maxNum.toString().length; - for (let n = 0; n < maxNum; n++) { - const token = `<${n.toString().padStart(width, '0')}>`; - const id = n + offset; - vocab[token] = id; - ids[id] = token; - } - return { vocab, ids, lastToken: maxNum + offset }; -} - -/** - * For Sort: - * token 0 → ":" - * token 1 → "," - * tokens 2 … maxNum+2 represent the numbers 0 … maxNum. - */ -export function createSortTokenizer( - maxNum: number, - includeColon: boolean, - includeCommas: boolean -): SimpleTokenizer { - const vocab: Record = {}; - const ids: Record = {}; - let offset = 0; - if (includeColon) { - vocab[':'] = 0; - ids[0] = ':'; - offset++; - } - if (includeCommas) { - vocab[','] = 1; - ids[1] = ','; - offset++; - } - const aCharCode = 'A'.charCodeAt(0); - for (let n = 0; n < maxNum; n++) { - const token = String.fromCharCode(aCharCode + n); - const id = n + offset; - vocab[token] = id; - ids[id] = token; - } - return { vocab, ids, lastToken: maxNum + offset }; -} - -/** - * For Two-Sum: - * token 0 → ":" - * token 1 → "=" - * token 2 → "," - * tokens 3 … maxNum+3 represent the numbers 0 … maxNum. - */ -export function createTwoSumTokenizer(maxNum: number, includeCommas: boolean): SimpleTokenizer { - const vocab: Record = {}; - const ids: Record = {}; - let offset = 2; - vocab[':'] = 0; - ids[0] = ':'; - vocab['='] = 1; - ids[1] = '='; - if (includeCommas) { - vocab[','] = 2; - ids[2] = ','; - offset++; - } - const aCharCode = 'A'.charCodeAt(0); - for (let n = 0; n < maxNum; n++) { - const token = String.fromCharCode(aCharCode + n); - const id = n + 3; - vocab[token] = id; - ids[id] = token; - } - return { vocab, ids, lastToken: maxNum + offset }; -} - -/** - * Typical tokenizer (used by zeros, count, slapjack). - */ -export function vocabToSimpleTokenizer(vocab: string): SimpleTokenizer { - const tokens = vocab.split(''); - const vocabMap = tokens.reduce( - (acc, c, i) => { - acc[c] = i; - return acc; - }, - {} as Record - ); - const ids = tokens.reduce( - (acc, c, i) => { - acc[i] = c; - return acc; - }, - {} as Record - ); - return { vocab: vocabMap, ids, lastToken: tokens.length }; -} - -// A helper for typical (character-based) tokenization. -export function sequenceToTokens(sequence: string, tokenizer: SimpleTokenizer): number[] { - return sequence.split('').map((c) => tokenizer.vocab[c]); -} - -// -------------------------- -// Task-Specific Sequence Generators -// (These return token ID arrays directly for the number‐block tasks.) -// -------------------------- - -/** - * Addition: generate two addends (in [0, maxNum]) chosen so that their sum ≤ maxNum. - * Mapping: number token = number + 2, plus = 0, equals = 1. - */ -export function addSequenceTokenized(config: AdditionConfig): [number[], number[]] { - const { maxNum, includeExpressionTokens } = config; - const num1 = Math.floor(Math.random() * (maxNum + 1)); - const num2 = Math.floor(Math.random() * (maxNum - num1 + 1)); - const sum = num1 + num2; - let prompt; - if (includeExpressionTokens) { - prompt = [num1 + 2, 0, num2 + 2, 1]; - } else { - prompt = [num1 + 2, num2 + 2]; - } - const target = [sum + 2]; - return [prompt, target]; -} - -/** - * Modular Addition: generate two numbers (in [0, maxNum)) and compute (num1+num2)%maxNum. - * Mapping is identical to Addition. - */ -export function modAddSequenceTokenized(config: ModAdditionConfig): [number[], number[]] { - const { maxNum, includeExpressionTokens } = config; - const num1 = Math.floor(Math.random() * maxNum); - const num2 = Math.floor(Math.random() * maxNum); - const sum = (num1 + num2) % maxNum; - let prompt; - if (includeExpressionTokens) { - prompt = [num1 + 2, 0, num2 + 2, 1]; - } else { - prompt = [num1 + 2, num2 + 2]; - } - const target = [sum + 2]; - return [prompt, target]; -} - -/** - * Sorting: generate a sequence of numbers (each in [0, maxNum]) and return: - * - prompt: the unsorted sequence with commas (token 1) between numbers and a colon (token 0) at the end. - * - target: the sorted sequence with commas between numbers. - * Mapping: number token = number + 2. - */ -export function sortSequenceTokenized( - config: NumberSequenceConfig, - tokenizer: SimpleTokenizer -): [number[], number[]] { - const { seqLen, maxNum, includeCommas, includeColon } = config; - const nums = Array.from({ length: seqLen }, () => Math.floor(Math.random() * maxNum)); - const sorted = [...nums].sort((a, b) => a - b); - const prompt: number[] = []; - for (let i = 0; i < nums.length; i++) { - prompt.push(tokenizer.vocab[String.fromCharCode('A'.charCodeAt(0) + nums[i])]); - if (includeCommas && i < nums.length - 1) { - prompt.push(tokenizer.vocab[',']); // comma - } - } - - const target: number[] = []; - if (includeColon) { - target.push(tokenizer.vocab[':']); // colon - } - for (let i = 0; i < sorted.length; i++) { - target.push(tokenizer.vocab[String.fromCharCode('A'.charCodeAt(0) + sorted[i])]); - if (includeCommas && i < sorted.length - 1) { - target.push(tokenizer.vocab[',']); // comma - } - } - return [prompt, target]; -} - -/** - * Repeat: generate a sequence of numbers (each in [0, maxNum]) and return: - * - prompt: the sequence with commas (token 1) between numbers and a colon (token 0) at the end. - * - target: the same sequence with commas between numbers. - * Mapping: number token = number + 2. - */ -export function repeatSequenceTokenized( - config: NumberSequenceConfig, - tokenizer: SimpleTokenizer -): [number[], number[]] { - const { seqLen, maxNum, includeCommas, includeColon } = config; - const nums = Array.from({ length: seqLen }, () => Math.floor(Math.random() * maxNum)); - const prompt: number[] = []; - for (let i = 0; i < nums.length; i++) { - prompt.push(tokenizer.vocab[String.fromCharCode('A'.charCodeAt(0) + nums[i])]); - if (includeCommas && i < nums.length - 1) { - prompt.push(tokenizer.vocab[',']); // comma - } - } - - const target: number[] = []; - if (includeColon) { - target.push(tokenizer.vocab[':']); // colon - } - for (let i = 0; i < nums.length; i++) { - target.push(tokenizer.vocab[String.fromCharCode('A'.charCodeAt(0) + nums[i])]); - if (includeCommas && i < nums.length - 1) { - target.push(tokenizer.vocab[',']); // comma - } - } - return [prompt, target]; -} - -/** - * Two-Sum: generate a sequence of numbers (each in [0, maxNum]) and two random indices. - * The prompt consists of: - * - the unsorted sequence (with commas, token 2) - * - a colon (token 0), the sum (token = sum+3) and an equals sign (token 1). - * The target is the two numbers (from the chosen indices) separated by a comma. - * Mapping: number token = number + 3. - */ -export function twoSumSequenceTokenized(config: NumberSequenceConfig): [number[], number[]] { - const { seqLen, maxNum } = config; - const nums = Array.from({ length: seqLen }, () => Math.floor(Math.random() * (maxNum / 2))); - const i = Math.floor(Math.random() * seqLen); - const j = Math.floor(Math.random() * seqLen); - const sum = nums[i] + nums[j]; - const prompt: number[] = []; - for (let k = 0; k < nums.length; k++) { - prompt.push(nums[k] + 3); - if (k < nums.length - 1) { - prompt.push(2); // comma - } - } - prompt.push(0); // colon - prompt.push(sum + 3); - prompt.push(1); // equals - const target: number[] = [nums[i] + 3, 2, nums[j] + 3]; - return [prompt, target]; -} - -/** - * Zeros: (Baseline) returns string outputs and uses typical tokenization. - */ -export function zerosSequence(config: FixedLengthConfig): [string, string] { - const { seqLen } = config; - const sequence = Array(seqLen - 1) - .fill('0') - .join(''); - return ['0', sequence]; -} - -// (The counting and slapjack tasks are left commented out. Note that slapjack will continue to use the typical tokenization.) - -export interface TaskSpec { - metadata: TaskMetadata; - /** - * Build a tokenizer (which may depend on the config). - */ - createTokenizer: (config: Config) => SimpleTokenizer; - /** - * Generate a training batch given the combined task config. - */ - trainBatch: (config: TrainBatchConfig & Config) => [number[][], number[][]]; - /** - * Return the evaluation configuration, including a generator and a metric. - */ - eval: (config: Config) => { - example: () => [number[], number[]]; - metric: (completion: number[], target: number[], sequence?: number[]) => Array; - }; -} - -export type TaskConfigMap = { - two_sum: NumberSequenceConfig; - sort: NumberSequenceConfig; - repeat: NumberSequenceConfig; - add: AdditionConfig; - mod_add: ModAdditionConfig; - zeros: FixedLengthConfig; - // count: NumberSequenceConfig; - // slapjack: FixedLengthConfig; -}; - -export const tasks: { [K in keyof TaskConfigMap]: TaskSpec } = { - add: (() => { - const createTokenizer = (config: AdditionConfig) => - createAddTokenizer(config.maxNum, config.includeExpressionTokens); - return { - metadata: taskMetadata.add, - createTokenizer, - trainBatch: (config: TrainBatchConfig & AdditionConfig) => { - const inputs: number[][] = []; - const targets: (number | -100)[][] = []; - for (let i = 0; i < config.batchSize; i++) { - const [prompt, completion] = addSequenceTokenized(config); - const [inputSeq, targetSeq] = createAutoregressivePair( - prompt, - completion, - config.maskOutPrefix - ); - inputs.push(inputSeq); - targets.push(targetSeq); - } - return [inputs, targets]; - }, - eval: (config: AdditionConfig) => { - return { - example: () => addSequenceTokenized(config), - metric: (completion, target) => { - if (completion.length !== target.length) return completion.map(() => false); - return completion.map((t, i) => t === target[i]); - } - }; - } - }; - })(), - mod_add: (() => { - const createTokenizer = (config: ModAdditionConfig) => - createAddTokenizer(config.maxNum, config.includeExpressionTokens); - return { - metadata: taskMetadata.mod_add, - createTokenizer, - trainBatch: (config: TrainBatchConfig & ModAdditionConfig) => { - const inputs: number[][] = []; - const targets: (number | -100)[][] = []; - for (let i = 0; i < config.batchSize; i++) { - const [prompt, completion] = modAddSequenceTokenized(config); - const [inputSeq, targetSeq] = createAutoregressivePair( - prompt, - completion, - config.maskOutPrefix - ); - inputs.push(inputSeq); - targets.push(targetSeq); - } - return [inputs, targets]; - }, - eval: (config: ModAdditionConfig) => { - return { - example: () => modAddSequenceTokenized(config), - metric: (completion, target) => { - if (completion.length !== target.length) return completion.map(() => false); - return completion.map((t, i) => t === target[i]); - } - }; - } - }; - })(), - sort: (() => { - const createTokenizer = (config: NumberSequenceConfig) => - createSortTokenizer(config.maxNum, config.includeColon, config.includeCommas); - return { - metadata: taskMetadata.sort, - createTokenizer, - trainBatch: (config: TrainBatchConfig & NumberSequenceConfig) => { - const tokenizer = createTokenizer(config); - const inputs: number[][] = []; - const targets: (number | -100)[][] = []; - for (let i = 0; i < config.batchSize; i++) { - const [prompt, completion] = sortSequenceTokenized(config, tokenizer); - const [inputSeq, targetSeq] = createAutoregressivePair( - prompt, - completion, - config.maskOutPrefix - ); - inputs.push(inputSeq); - targets.push(targetSeq); - } - return [inputs, targets]; - }, - eval: (config: NumberSequenceConfig) => { - const tokenizer = createTokenizer(config); - return { - example: () => sortSequenceTokenized(config, tokenizer), - metric: (completion, target) => { - if (completion.length !== target.length) return completion.map(() => false); - return completion.map((t, i) => t === target[i]); - } - }; - } - }; - })(), - repeat: (() => { - const createTokenizer = (config: NumberSequenceConfig) => - createSortTokenizer(config.maxNum, config.includeColon, config.includeCommas); - return { - metadata: taskMetadata.repeat, - createTokenizer, - trainBatch: (config: TrainBatchConfig & NumberSequenceConfig) => { - const tokenizer = createTokenizer(config); - const inputs: number[][] = []; - const targets: (number | -100)[][] = []; - for (let i = 0; i < config.batchSize; i++) { - const [prompt, completion] = repeatSequenceTokenized(config, tokenizer); - const [inputSeq, targetSeq] = createAutoregressivePair( - prompt, - completion, - config.maskOutPrefix - ); - inputs.push(inputSeq); - targets.push(targetSeq); - } - return [inputs, targets]; - }, - eval: (config: NumberSequenceConfig) => { - const tokenizer = createTokenizer(config); - return { - example: () => repeatSequenceTokenized(config, tokenizer), - metric: (completion, target) => { - if (completion.length !== target.length) return completion.map(() => false); - return completion.map((t, i) => t === target[i]); - } - }; - } - }; - })(), - two_sum: (() => { - const createTokenizer = (config: NumberSequenceConfig) => - createTwoSumTokenizer(config.maxNum, config.includeCommas); - return { - metadata: taskMetadata.two_sum, - createTokenizer, - trainBatch: (config: TrainBatchConfig & NumberSequenceConfig) => { - const inputs: number[][] = []; - const targets: (number | -100)[][] = []; - for (let i = 0; i < config.batchSize; i++) { - const [prompt, completion] = twoSumSequenceTokenized(config); - const [inputSeq, targetSeq] = createAutoregressivePair( - prompt, - completion, - config.maskOutPrefix - ); - inputs.push(inputSeq); - targets.push(targetSeq); - } - return [inputs, targets]; - }, - eval: (config: NumberSequenceConfig) => { - return { - example: () => twoSumSequenceTokenized(config), - metric: (completion, target) => { - if (completion.length !== target.length) return completion.map(() => false); - return completion.map((t, i) => t === target[i]); - } - }; - } - }; - })(), - zeros: { - metadata: taskMetadata.zeros, - createTokenizer: (_config: FixedLengthConfig) => - vocabToSimpleTokenizer(taskMetadata.zeros.vocab!), - trainBatch: (config: TrainBatchConfig & FixedLengthConfig) => { - const tokenizer = vocabToSimpleTokenizer(taskMetadata.zeros.vocab!); - const inputs: number[][] = []; - const targets: (number | -100)[][] = []; - for (let i = 0; i < config.batchSize; i++) { - // Zeros returns strings; tokenize them. - const [promptStr, completionStr] = zerosSequence(config); - const promptTokens = sequenceToTokens(promptStr, tokenizer); - const completionTokens = sequenceToTokens(completionStr, tokenizer); - const [inputSeq, targetSeq] = createAutoregressivePair( - promptTokens, - completionTokens, - false - ); - inputs.push(inputSeq); - targets.push(targetSeq); - } - return [inputs, targets]; - }, - eval: (config: FixedLengthConfig) => { - const tokenizer = vocabToSimpleTokenizer(taskMetadata.zeros.vocab!); - return { - example: () => { - const [promptStr, targetStr] = zerosSequence(config); - return [sequenceToTokens(promptStr, tokenizer), sequenceToTokens(targetStr, tokenizer)]; - }, - metric: (completion, target) => { - if (completion.length !== target.length) return completion.map(() => false); - return completion.map((t, i) => t === target[i]); - } - }; - } - } - // TODO: Add count and slapjack -}; diff --git a/examples/piston-train-toy/src/lib/trainWorker.ts b/examples/piston-train-toy/src/lib/trainWorker.ts deleted file mode 100644 index 1ed93156..00000000 --- a/examples/piston-train-toy/src/lib/trainWorker.ts +++ /dev/null @@ -1,259 +0,0 @@ -import { type TaskConfigMap, tasks, type TaskSpec, type SimpleTokenizer } from '$lib/tasks'; -import { Trainer, init } from '@piston-ml/piston-web'; - -let trainer: Trainer; -let sessionCounter = 0; -let currentSession = 0; -const trainingSessions: Record = {}; -let saveCheckpointPath: string | null = null; - -export interface TrainerConfig { - vocab_size: number; - n_embd: number; - n_layer: number; - n_head: number; - block_size: number; - batch_size: number; - dataset: keyof typeof tasks; - task_parameters: TaskConfigMap[keyof TaskConfigMap]; - activation: string; - attention_only: boolean; - position_encoding: string; - seed?: number; - layernorm_position: string; - label_smoothing: number; - caching_enabled: boolean; - inplace_support: boolean; - debug_selection: string; - optimizer: { - optimizer_type: string; - lr: number; - beta1: number; - beta2: number; - eps: number; - weight_decay: number; - momentum: number; - scheduler_type: string; - scheduler_factor: number; - scheduler_steps: number; - scheduler_eta_min: number; - }; -} - -function markStopTraining() { - Object.keys(trainingSessions).forEach((key) => { - trainingSessions[Number(key)] = false; - }); -} - -async function initializeTrainer(config: TrainerConfig) { - await init(); - // Stop all existing training sessions - markStopTraining(); - - const task = tasks[config.dataset] as TaskSpec; - const tokenizer = task.createTokenizer(config.task_parameters); - - // Get the appropriate task generator - if (!task) { - console.error(`Unknown dataset type: ${config.dataset}`); - return; - } - - trainer = await new Trainer({ - ...config, - // Add 1 to the vocab size to account for the end-of-sequence token - vocab_size: tokenizer.lastToken - }); - self.postMessage({ type: 'modelReady' }); - currentSession = sessionCounter++; - trainingSessions[currentSession] = true; - trainingLoop(currentSession, config, task, tokenizer); -} - -async function trainingLoop( - sessionId: number, - config: TrainerConfig, - task: TaskSpec, - tokenizer: SimpleTokenizer -) { - if (!trainer) { - console.error('Trainer not initialized'); - return; - } - - let step = 0; - while (trainingSessions[sessionId]) { - // Skip if this isn't the current session anymore - if (sessionId !== currentSession) { - break; - } - - // Check if we need to save a checkpoint - if (saveCheckpointPath !== null) { - try { - // Get the URL from the save_checkpoint method instead of handling the download directly - const url = await trainer.save_checkpoint(); - self.postMessage({ - type: 'checkpoint_saved', - success: true, - url: url, - filename: saveCheckpointPath - }); - } catch (error) { - self.postMessage({ - type: 'checkpoint_saved', - success: false, - error: error instanceof Error ? error.message : String(error) - }); - } - // Reset the path after saving - saveCheckpointPath = null; - } - - try { - // Combine batch size with task-specific parameters before invoking the generator - const taskConfig = { - batchSize: config.batch_size, - ...config.task_parameters - }; - const [input, target] = task.trainBatch(taskConfig); - - // Train on the batch - const result = await trainer.train_on_batch(input, target); - - const logOutput = await trainer.take_step_log(); - const usingCache = logOutput?.cached ?? false; - const totalElapsed = 0; - const averageElapsed = 0; - const logits = result.get('logits') as Map; - const loss = result.get('loss') as Map; - const attn_masks = result.get('attn_masks') as Map; - const usage_bytes = trainer.usage_bytes(); - - let winCount = null; - const EVAL_TRIAL_COUNT = 5; - - const { example: evalExample, metric: evalMetric } = task.eval(config.task_parameters); - - // After every n steps, evaluate the model - if (step % 50 === 0) { - const [streamingSequence, streamingTarget] = evalExample(); - let streamingExampleCompletion: number[] | null = null; - const streamingExampleLogits: number[] = []; - let streamingExampleMetric: boolean | null = null; - - // For the first eval, we'll stream it to the frontend - await trainer.generate( - streamingSequence, - /* max_tokens= */ streamingTarget.length + 1, - (tokens: number[], logitsObj: { shape: number[]; data: number[] }) => { - // For the first call (prompt), just store logits - if (streamingExampleCompletion === null) { - streamingExampleCompletion = []; - streamingExampleLogits.push(...logitsObj.data); - return; - } - - // For each subsequent token, append it and evaluate - streamingExampleCompletion.push(...tokens); - streamingExampleLogits.push(...logitsObj.data); - - // Run the metric on the current completion - const evalResult = evalMetric( - streamingExampleCompletion, - streamingTarget, - streamingSequence - ); - - streamingExampleMetric = evalResult.every((result) => result); - - // Send the streaming eval result to the frontend - if (sessionId === currentSession) { - self.postMessage({ - type: 'evalStreaming', - sequence: streamingSequence.map((t) => tokenizer.ids[t]), - completion: streamingExampleCompletion.map((t) => tokenizer.ids[t]), - target: streamingTarget.map((t) => tokenizer.ids[t]), - evalResult, - logits: { - data: streamingExampleLogits, - shape: logitsObj.shape - } - }); - } - } - ); - - winCount = streamingExampleMetric === true ? 1 : 0; - - // We'll do best of 5 evals, so we'll do the rest non-streaming - for (let i = 0; i < EVAL_TRIAL_COUNT - 1; i++) { - const [sequence, target] = evalExample(); - const result = await trainer.generate(sequence, target.length + 1); - const tokens = result.get('tokens') as number[]; - const evalResult = evalMetric(tokens, target, sequence); - if (evalResult.every((result) => result)) { - winCount++; - } - } - } - - // Only send message if this is still the current session - if (sessionId === currentSession) { - self.postMessage({ - type: 'step', - totalElapsed, - averageElapsed, - usingCache, - input: input.map((x) => x.map((t) => tokenizer.ids[t])), - target: target.map((t) => t.map((t) => tokenizer.ids[t])), - accuracy: winCount === null ? null : winCount / EVAL_TRIAL_COUNT, - loss: { - total: loss.get('total'), - tokens: loss.get('tokens') - }, - learning_rate: result.get('learning_rate'), - usage_bytes, - attn_masks: { - data: attn_masks.get('data'), - shape: attn_masks.get('shape') - }, - logits: { - data: logits.get('data'), - shape: logits.get('shape') - } - }); - } - } catch (error: Error | unknown) { - console.error('Training error in worker:', error); - if (sessionId === currentSession) { - self.postMessage({ - type: 'error', - error: error instanceof Error ? error.message : String(error) - }); - } - break; - } - step++; - // Yield to keep the worker responsive - await new Promise((resolve) => setTimeout(resolve, 0)); - } -} - -self.onmessage = async (e: MessageEvent) => { - if (e.data.type === 'stop') { - markStopTraining(); - return; - } - if (e.data.type === 'save_checkpoint') { - saveCheckpointPath = e.data.path; - return; - } - // If we receive a configuration object, initialize a new trainer - if (typeof e.data === 'object') { - await initializeTrainer(e.data); - } -}; - -self.postMessage({ type: 'ready' }); diff --git a/examples/piston-train-toy/src/routes/old-trainer/+layout.svelte b/examples/piston-train-toy/src/routes/old-trainer/+layout.svelte deleted file mode 100644 index 9b776b77..00000000 --- a/examples/piston-train-toy/src/routes/old-trainer/+layout.svelte +++ /dev/null @@ -1,6 +0,0 @@ - - -{@render children()} diff --git a/examples/piston-train-toy/src/routes/old-trainer/+layout.ts b/examples/piston-train-toy/src/routes/old-trainer/+layout.ts deleted file mode 100644 index 189f71e2..00000000 --- a/examples/piston-train-toy/src/routes/old-trainer/+layout.ts +++ /dev/null @@ -1 +0,0 @@ -export const prerender = true; diff --git a/examples/piston-train-toy/src/routes/old-trainer/+page.svelte b/examples/piston-train-toy/src/routes/old-trainer/+page.svelte deleted file mode 100644 index e18a7f91..00000000 --- a/examples/piston-train-toy/src/routes/old-trainer/+page.svelte +++ /dev/null @@ -1,1572 +0,0 @@ - - -
    - {#if !hasWebGPU} - - {/if} -
    -
    -
    -

    toy transformer

    - -
    -
    -

    train loss: {loss.toFixed(4)}

    -

    val accuracy: {evalAccuracy.toFixed(4)}

    -
    -
    - -
    -
    - -
    - - - {#if isTraining} -
    -
    - - -
    - - {#if showAttention && attentionCanvases.length > 0} - -
    - {#each attentionCanvases as layerCanvases, layerIdx} -
    -

    Layer {layerIdx + 1}

    -
    - {#each layerCanvases as canvas, headIdx} -
    - -
    Head {headIdx + 1}
    -
    - {/each} -
    -
    - {/each} -
    - {/if} -
    - {/if} - - {#if isTraining} -
    -
    -

    Train Batches

    -
    Showing last {MAX_HISTORY} batches
    -
    -
    - {#each batchHistory as batch} -
    - {#each batch.input as input, batchIdx} -
    -
    - {input[0]}{#each [...input.slice(1), batch.target[batchIdx][batch.target[batchIdx].length - 1]] as char, i} - - {char} - - {/each} -
    -
    - {/each} -
    - {/each} -
    -
    - - {#if evalHistory.length > 0} -
    -
    -

    Eval Results

    -
    Showing last {MAX_EVAL_HISTORY} evaluations
    -
    -
    - {#each evalHistory as evalEntry} -
    -
    -
    - {evalEntry.sequence.join('')}{#each evalEntry.completion as char, i} - - {char} - - {/each} - → {evalEntry.target.join('')} -
    -
    -
    - {/each} -
    -
    - {/if} - {/if} -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    - -
    -
    - {#each messages as message} -
    - [{message.timestamp}] - {message.text} -
    - {/each} -
    -
    -
    -
    -
    - - {#if isTraining} - - - {/if} -
    -
    -

    Dataset

    -
    - -
    - -
    - -
    -
    - {#if dataset && taskMetadata[dataset]} -

    {taskMetadata[dataset].description}

    -
    - {#each Object.entries(taskMetadata[dataset].parameters) as [key, param]} -
    - {#if param.type === 'boolean'} -
    - - -
    - {#if param.description} -

    {param.description}

    - {/if} - {:else} - -

    {param.description}

    - {/if} -
    - {/each} -
    - {/if} -
    -
    -
    -

    Model Architecture

    - - - - - `${v * 8} per head`} - /> - -
    - - -
    - -
    - -
    - -
    - - - -
    -
    -
    - -
    - -
    - -
    - - - -
    -
    -
    - - - - - -
    -
    - - -
    -
    -
    - -
    -

    Optimizer

    - -
    - -
    - -
    - - - -
    -
    -
    - - - - {#if optimizer_type === 'adamw'} - - - {#if showAdvanced} -
    -
    -
    - - -
    - -
    - - -
    -
    - -
    - -
    - -
    - -
    -
    - {/if} - {/if} - -
    - -
    - -
    - - - -
    -
    -
    - - {#if scheduler_type !== 'none'} -
    -
    - - -
    - - {#if scheduler_type === 'constant' || scheduler_type === 'linear'} -
    - - -
    - {/if} - - {#if scheduler_type === 'cosine'} -
    - - -
    - {/if} -
    - {/if} -
    - -
    -

    Training Options

    - -
    - -
    - -
    - Math.pow(10, v).toFixed(3)} - /> -
    - -
    - - -
    -
    -
    - - -
    -
    - -
    -
    - - -
    -
    -
    -
    -
    -
    From f39f0e04c65fa157b0630d166cd6c49866dbdc8e Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 26 Oct 2025 15:55:58 -0600 Subject: [PATCH 512/590] Add initial model modules --- .../src/lib/train/model/bidirectional.ts | 76 +++ .../src/lib/train/model/cache.ts | 20 + .../src/lib/train/model/config.ts | 46 ++ .../src/lib/train/model/modules/attention.ts | 256 ++++++++ .../src/lib/train/model/modules/decoder.ts | 135 +++++ .../src/lib/train/model/modules/encoder.ts | 74 +++ .../src/lib/train/model/modules/mlp.ts | 37 ++ .../src/lib/train/model/modules/norm.ts | 15 + .../src/lib/train/model/transformer.ts | 556 ++++++++++++++++++ .../src/lib/train/model/utils.ts | 145 +++++ 10 files changed, 1360 insertions(+) create mode 100644 examples/piston-train-toy/src/lib/train/model/bidirectional.ts create mode 100644 examples/piston-train-toy/src/lib/train/model/cache.ts create mode 100644 examples/piston-train-toy/src/lib/train/model/config.ts create mode 100644 examples/piston-train-toy/src/lib/train/model/modules/attention.ts create mode 100644 examples/piston-train-toy/src/lib/train/model/modules/decoder.ts create mode 100644 examples/piston-train-toy/src/lib/train/model/modules/encoder.ts create mode 100644 examples/piston-train-toy/src/lib/train/model/modules/mlp.ts create mode 100644 examples/piston-train-toy/src/lib/train/model/modules/norm.ts create mode 100644 examples/piston-train-toy/src/lib/train/model/transformer.ts create mode 100644 examples/piston-train-toy/src/lib/train/model/utils.ts diff --git a/examples/piston-train-toy/src/lib/train/model/bidirectional.ts b/examples/piston-train-toy/src/lib/train/model/bidirectional.ts new file mode 100644 index 00000000..d2174325 --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/model/bidirectional.ts @@ -0,0 +1,76 @@ +import type { LayerNormalizationConfig } from '$lib/workspace/config'; + +import { nn, Tensor } from '@piston-ml/piston-web'; + +import { createNorm } from './modules/norm'; + +/** + * MLM Head for masked language modeling + */ +export class MLMHead extends nn.Module { + private readonly transform: nn.Linear; + private readonly layernorm?: nn.Module<[Tensor], Tensor>; + private readonly decoder: nn.Linear; + + /** + * @param hiddenSize - Hidden dimension + * @param vocabSize - Vocabulary size + * @param layerNormalization - Layer normalization configuration + */ + constructor(hiddenSize: number, vocabSize: number, layerNormalization: LayerNormalizationConfig) { + super(); + + this.transform = new nn.Linear(hiddenSize, hiddenSize); + if (layerNormalization.transformer.present) { + this.layernorm = createNorm(hiddenSize, layerNormalization); + } + + this.decoder = new nn.Linear(hiddenSize, vocabSize, false); + } + + /** + * @param hiddenStates - Hidden states from encoder + * @returns MLM logits + */ + forward(hiddenStates: Tensor): Tensor { + let x = this.transform.forward(hiddenStates); + x = x.gelu(); // Standard activation for MLM head + if (this.layernorm) { + x = this.layernorm.forward(x) as Tensor; + } + return this.decoder.forward(x); + } +} + +/** + * Pooler for classification tasks + */ +export class Pooler extends nn.Module { + private readonly dense: nn.Linear; + + /** + * @param hiddenSize - Hidden dimension + */ + constructor(hiddenSize: number) { + super(); + this.dense = new nn.Linear(hiddenSize, hiddenSize); + } + + /** + * @param hiddenStates - Hidden states from encoder [B, T, H] + * @returns Pooled representation [B, H] + */ + forward(hiddenStates: Tensor): Tensor { + // Take the first token (CLS token) representation + let pooled = hiddenStates + .slice([ + [0, hiddenStates.size(0)], + [0, 1], + [0, hiddenStates.size(2)] + ]) + .squeeze({ dim: 1 }); + + pooled = this.dense.forward(pooled); + return pooled.tanh(); + } +} diff --git a/examples/piston-train-toy/src/lib/train/model/cache.ts b/examples/piston-train-toy/src/lib/train/model/cache.ts new file mode 100644 index 00000000..7dd33524 --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/model/cache.ts @@ -0,0 +1,20 @@ +import type { Tensor } from '@piston-ml/piston-web'; + +export type SelfAttentionCache = { + k: Tensor; + v: Tensor; + length: number; +}; + +export type DecoderLayerCache = { + self?: SelfAttentionCache; + cross?: SelfAttentionCache; +}; + +export type DecoderKVCache = { + layers: DecoderLayerCache[]; +}; + +export function createEmptyDecoderKVCache(nLayers: number): DecoderKVCache { + return { layers: Array.from({ length: nLayers }, () => ({})) }; +} diff --git a/examples/piston-train-toy/src/lib/train/model/config.ts b/examples/piston-train-toy/src/lib/train/model/config.ts new file mode 100644 index 00000000..d72a1c1b --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/model/config.ts @@ -0,0 +1,46 @@ +import type { + Config, + DropoutConfig, + LayerNormalizationConfig, + MLPConfig, + PositionEncodingConfig, + TransformerAttentionConfig +} from '$lib/workspace/config'; + +export interface TransformerModuleConfig { + vocabSize: number; + embeddingSize: number; + attention: TransformerAttentionConfig; + layerNormalization: LayerNormalizationConfig; + mlp: MLPConfig; + positionalEncoding: PositionEncodingConfig; + dropout: DropoutConfig; +} + +export function buildTransformerConfigCommon( + config: Config, + vocabSize: number +): TransformerModuleConfig { + const effectiveHeads = config.model.transformer.attention.present + ? config.model.transformer.attention.nKeyValueHeads + : 1; + + return { + vocabSize: vocabSize, + embeddingSize: config.model.transformer.headDim * effectiveHeads, + attention: config.model.transformer.attention, + mlp: config.model.transformer.mlp, + positionalEncoding: config.model.transformer.positionalEncoding, + layerNormalization: config.model.layerNormalization, + dropout: (({ present, embedding: embd, transformer }: DropoutConfig) => { + return { + present, + embedding: present ? embd : 0, + transformer: { + attention: present ? transformer.attention : 0, + residual: present ? transformer.residual : 0 + } + }; + })(config.training.dropout) + }; +} diff --git a/examples/piston-train-toy/src/lib/train/model/modules/attention.ts b/examples/piston-train-toy/src/lib/train/model/modules/attention.ts new file mode 100644 index 00000000..a0fe9186 --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/model/modules/attention.ts @@ -0,0 +1,256 @@ +import { cat, Linear, nn, Tensor } from '@piston-ml/piston-web'; + +import type { SelfAttentionCache } from '../cache'; +import type { TransformerModuleConfig } from '../config'; + +import { createCausalMask, maskedFill } from '../utils'; + +abstract class CoreAttention extends nn.Module { + protected readonly nHeads: number; + protected readonly nKvHeads: number; + protected readonly embeddingSize: number; + protected readonly headDim: number; + protected abstract cProj: Linear; + protected attnDropout?: nn.Dropout; + protected residDropout?: nn.Dropout; + protected readonly isCausal: boolean; + protected readonly config: TransformerModuleConfig; + + constructor(embeddingSize: number, config: TransformerModuleConfig, isCausal: boolean = false) { + super(); + + this.nHeads = config.attention.nKeyValueHeads; + this.nKvHeads = config.attention.nKeyValueHeads; + this.embeddingSize = embeddingSize; + this.headDim = embeddingSize / this.nHeads; + this.isCausal = isCausal; + + this.config = config; + } + + protected runAttention( + q: Tensor, + k: Tensor, + v: Tensor, + options: { + attentionMask?: Tensor | null; + cache?: SelfAttentionCache | null; + } + ): [Tensor, SelfAttentionCache | null] { + const B = q.size(0); + const T_q = q.size(2); + + // Concatenate with cache if present + const kCat = options.cache ? cat([options.cache.k, k], { dim: 2 }) : k; + const vCat = options.cache ? cat([options.cache.v, v], { dim: 2 }) : v; + const T_kv = kCat.size(2); + + // Compute attention scores + let att = q.matmul(kCat, { transRhs: true }).div(Math.sqrt(this.headDim)); + + // Apply causal mask if needed + if (this.isCausal) { + att = this.applyCausalMask(att, T_q, T_kv); + } + if (options.attentionMask) { + att = this.applyAttentionMask(att, options.attentionMask); + } + + // Apply softmax over keys + att = att.softmax(3); + att = this.attnDropout ? this.attnDropout.forward(att) : att; + + let y = att.matmul(vCat).transpose(1, 2).view([B, T_q, this.embeddingSize]); + + // Apply output projection + y = this.cProj.forward(y); + + if (this.residDropout) { + y = this.residDropout.forward(y); + } + + const newCache: SelfAttentionCache | null = { + k: kCat, + v: vCat, + length: T_kv + }; + return [y, newCache]; + } + + applyAttentionMask(att: Tensor, attentionMask: Tensor) { + // Convert attention mask to appropriate shape [B, 1, 1, T_kv] and broadcast + const mask = attentionMask.unsqueeze(1).unsqueeze(2).broadcastTo(att.size()); + return maskedFill(att, mask, -1e9); + } + + applyCausalMask(att: Tensor, queryLen: number, keyLen: number) { + // Apply causal mask if needed + return queryLen <= 1 + ? att + : (() => { + const mask = createCausalMask(queryLen, keyLen).broadcastTo(att.size()); + return maskedFill(att, mask, -1e9); + })(); + } + + /** + * Apply grouped-query attention broadcasting to key and value tensors + * @param k Key tensor [B, nKvHeads, seqLen, headDim] + * @param v Value tensor [B, nKvHeads, seqLen, headDim] + * @param B Batch size + * @param seqLen Sequence length + * @returns Broadcasted [k, v] tensors [B, nHeads, seqLen, headDim] + */ + applyGroupedQueryBroadcast(k: Tensor, v: Tensor, B: number, seqLen: number): [Tensor, Tensor] { + if (this.nHeads !== this.nKvHeads) { + const repeatFactor = this.nHeads / this.nKvHeads; + const th = seqLen * this.headDim; + + k = k + .view([B, this.nKvHeads, th]) + .unsqueeze(2) + .broadcastTo([B, this.nKvHeads, repeatFactor, th]) + .view([B, this.nHeads, seqLen, this.headDim]); + v = v + .view([B, this.nKvHeads, th]) + .unsqueeze(2) + .broadcastTo([B, this.nKvHeads, repeatFactor, th]) + .view([B, this.nHeads, seqLen, this.headDim]); + } + return [k, v]; + } +} + +export class SelfAttention extends CoreAttention { + private readonly cAttn: Linear; + protected readonly cProj: Linear; + + constructor(embeddingSize: number, config: TransformerModuleConfig, isCausal: boolean = false) { + super(embeddingSize, config, isCausal); + + const kvDim = this.headDim * this.nKvHeads; + const qkvOutDim = embeddingSize + 2 * kvDim; + this.cAttn = new nn.Linear(embeddingSize, qkvOutDim); + this.cProj = new nn.Linear(embeddingSize, embeddingSize); + + if (config.dropout.transformer.attention > 0) { + this.attnDropout = new nn.Dropout(config.dropout.transformer.attention); + } + + if (config.dropout.transformer.residual > 0) { + this.residDropout = new nn.Dropout(config.dropout.transformer.residual); + } + } + + private projectQkv(input: Tensor): [Tensor, Tensor, Tensor] { + const [B, T, _] = input.size(); + const kvDim = this.headDim * this.nKvHeads; + const keyPos = this.embeddingSize; + const valuePos = this.embeddingSize + kvDim; + const qkv = this.cAttn.forward(input); + let q = qkv.slice([ + [0, B], + [0, T], + [0, this.embeddingSize] + ]); + let k = qkv.slice([ + [0, B], + [0, T], + [keyPos, keyPos + kvDim] + ]); + let v = qkv.slice([ + [0, B], + [0, T], + [valuePos, valuePos + kvDim] + ]); + const qShape = [B, T, this.nHeads, this.headDim]; + const kvShape = [B, T, this.nKvHeads, this.headDim]; + q = q.view(qShape)?.transpose(1, 2); + k = k.view(kvShape)?.transpose(1, 2); + v = v.view(kvShape)?.transpose(1, 2); + [k, v] = this.applyGroupedQueryBroadcast(k, v, B, T); + return [q, k, v]; + } + + forward( + input: Tensor, + options: { attentionMask?: Tensor | null; cache?: SelfAttentionCache | null } = {} + ): { output: Tensor; pastKeyValues?: SelfAttentionCache } { + const qkv = this.projectQkv(input); + const [q, k, v] = qkv; + const [y, newCache] = this.runAttention(q, k, v, { + attentionMask: options.attentionMask ?? null, + cache: options.cache ?? null + }); + return { output: y, pastKeyValues: newCache ?? undefined }; + } +} + +export class CrossAttention extends CoreAttention { + private readonly qProj: Linear; + private readonly kvProj: Linear; + protected readonly cProj: Linear; + + constructor(embeddingSize: number, config: TransformerModuleConfig) { + super(embeddingSize, config); + this.qProj = new nn.Linear(embeddingSize, embeddingSize); + const kvDim = this.headDim * this.nKvHeads; + this.kvProj = new nn.Linear(embeddingSize, 2 * kvDim); + this.cProj = new nn.Linear(embeddingSize, embeddingSize); + + if (config.dropout.transformer.attention > 0) { + this.attnDropout = new nn.Dropout(config.dropout.transformer.attention); + } + + if (config.dropout.transformer.residual > 0) { + this.residDropout = new nn.Dropout(config.dropout.transformer.residual); + } + } + + forward( + query: Tensor, + keyValue: Tensor, + options: { attentionMask?: Tensor | null; cache?: SelfAttentionCache | null } = {} + ): { output: Tensor; pastKeyValues?: SelfAttentionCache } { + const [B, T_q, _q] = query.size(); + let q = this.qProj.forward(query); + const qShape = [B, T_q, this.nHeads, this.headDim]; + q = q.view(qShape)?.transpose(1, 2); + + let k: Tensor; + let v: Tensor; + let returnCache: SelfAttentionCache | null = null; + if (options.cache) { + // Reuse precomputed base encoder K/V from cache for cross-attention + k = options.cache.k; + v = options.cache.v; + returnCache = options.cache; + } else { + // Compute and reshape encoder K/V once, cache base K/V for reuse across decoding steps + const kvDim = this.headDim * this.nKvHeads; + const kv = this.kvProj.forward(keyValue); + const kProj = kv.slice([ + [0, B], + [0, keyValue.size(1)], + [0, kvDim] + ]); + const vProj = kv.slice([ + [0, B], + [0, keyValue.size(1)], + [kvDim, kvDim + kvDim] + ]); + const kvShape = [B, keyValue.size(1), this.nKvHeads, this.headDim]; + k = kProj.view(kvShape)?.transpose(1, 2); + v = vProj.view(kvShape)?.transpose(1, 2); + [k, v] = this.applyGroupedQueryBroadcast(k, v, B, keyValue.size(1)); + returnCache = { k, v, length: keyValue.size(1) }; + } + + const [y, _ignored] = this.runAttention(q, k, v, { + attentionMask: options.attentionMask ?? null, + // No KV concatenation for cross-attention; K/V are static from encoder + cache: null + }); + return { output: y, pastKeyValues: returnCache ?? undefined }; + } +} diff --git a/examples/piston-train-toy/src/lib/train/model/modules/decoder.ts b/examples/piston-train-toy/src/lib/train/model/modules/decoder.ts new file mode 100644 index 00000000..ed44a466 --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/model/modules/decoder.ts @@ -0,0 +1,135 @@ +import type { LayerNormPosition } from '$lib/workspace/config'; + +import { nn, Tensor } from '@piston-ml/piston-web'; + +import type { DecoderLayerCache } from '../cache'; +import type { TransformerModuleConfig } from '../config'; + +import { CrossAttention, SelfAttention } from './attention'; +import { MLP } from './mlp'; +import { createNorm } from './norm'; + +export type DecoderLayerForwardOptions = { + encoderHiddenStates?: Tensor | null; + srcPaddingMask?: Tensor | null; + tgtPaddingMask?: Tensor | null; + cache?: DecoderLayerCache | null; +}; + +export type DecoderLayerForwardResult = { + output: Tensor; + pastKeyValues?: DecoderLayerCache; +}; + +export class DecoderLayer extends nn.Module { + private readonly lnSelfAttn?: nn.Module<[Tensor], Tensor>; + private readonly selfAttn?: SelfAttention; + private readonly lnCrossAttn?: nn.Module<[Tensor], Tensor>; + private readonly crossAttn?: CrossAttention; + private readonly lnMlp?: nn.Module<[Tensor], Tensor>; + private readonly mlp?: MLP; + private readonly dropout?: nn.Dropout; + private readonly layernormPosition: LayerNormPosition; + + constructor(config: TransformerModuleConfig, crossAttention: boolean = false) { + super(); + + if (config.attention.present) { + this.selfAttn = new SelfAttention(config.embeddingSize, config, true); + + if (config.layerNormalization.transformer.present) { + this.lnSelfAttn = createNorm(config.embeddingSize, config.layerNormalization); + } + } + + if (crossAttention && config.attention.present) { + this.crossAttn = new CrossAttention(config.embeddingSize, config); + if (config.layerNormalization.transformer.present) { + this.lnCrossAttn = createNorm(config.embeddingSize, config.layerNormalization); + } + } + + if (config.mlp.present) { + this.mlp = new MLP(config.embeddingSize, config.mlp); + if (config.layerNormalization.transformer.present) { + this.lnMlp = createNorm(config.embeddingSize, config.layerNormalization); + } + } + + if (config.dropout.transformer.residual > 0) { + this.dropout = new nn.Dropout(config.dropout.transformer.residual); + } + + this.layernormPosition = config.layerNormalization.transformer.position; + } + + forward(input: Tensor, options: DecoderLayerForwardOptions = {}): DecoderLayerForwardResult { + const encoderHiddenStates = options.encoderHiddenStates ?? null; + const srcPaddingMask = options.srcPaddingMask ?? null; + const tgtPaddingMask = options.tgtPaddingMask ?? null; + const cache = options.cache ?? null; + let x = input; + let selfCache = cache?.self ?? null; + + if (this.selfAttn) { + const residual = input; + if (this.lnSelfAttn && this.layernormPosition === 'pre') { + x = this.lnSelfAttn.forward(input) as Tensor; + } + const selfResult = this.selfAttn.forward(x, { + attentionMask: tgtPaddingMask ?? null, + cache: selfCache ?? null + }); + selfCache = selfResult.pastKeyValues ?? null; + x = residual.add(selfResult.output); + if (this.lnSelfAttn && this.layernormPosition === 'post') { + x = this.lnSelfAttn.forward(x) as Tensor; + } + } + + if (this.crossAttn) { + if (!encoderHiddenStates) { + throw new Error('Encoder hidden states are required for cross-attention'); + } + const residual2 = x; + if (this.lnCrossAttn && this.layernormPosition === 'pre') { + x = this.lnCrossAttn.forward(x) as Tensor; + } + const crossResult = this.crossAttn.forward(x, encoderHiddenStates, { + attentionMask: srcPaddingMask ?? null, + cache: cache?.cross ?? null + }); + // If caching is enabled, store cross-attn K/V once + if (cache) { + if (!cache.cross && crossResult.pastKeyValues) { + cache.cross = crossResult.pastKeyValues; + } + } + x = residual2.add(crossResult.output); + if (this.lnCrossAttn && this.layernormPosition === 'post') { + x = this.lnCrossAttn.forward(x) as Tensor; + } + } + + if (this.mlp) { + const residual3 = x; + if (this.lnMlp && this.layernormPosition === 'pre') { + x = this.lnMlp.forward(x) as Tensor; + } + x = this.mlp.forward(x); + if (this.dropout) { + x = this.dropout.forward(x); + } + x = residual3.add(x); + if (this.lnMlp && this.layernormPosition === 'post') { + x = this.lnMlp.forward(x) as Tensor; + } + } + + const result: DecoderLayerForwardResult = { output: x }; + if (cache) { + result.pastKeyValues = { self: selfCache ?? undefined, cross: cache.cross ?? undefined }; + } + return result; + } +} diff --git a/examples/piston-train-toy/src/lib/train/model/modules/encoder.ts b/examples/piston-train-toy/src/lib/train/model/modules/encoder.ts new file mode 100644 index 00000000..b01686bc --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/model/modules/encoder.ts @@ -0,0 +1,74 @@ +import type { LayerNormPosition } from '$lib/workspace/config'; + +import { type Module, nn, Tensor } from '@piston-ml/piston-web'; + +import type { TransformerModuleConfig } from '../config'; + +import { SelfAttention } from './attention'; +import { MLP } from './mlp'; +import { createNorm } from './norm'; + +export class EncoderLayer extends nn.Module { + private readonly lnAttn?: Module<[Tensor], Tensor>; + private readonly attn?: SelfAttention; + private readonly lnMlp?: Module<[Tensor], Tensor>; + private readonly mlp?: MLP; + private readonly dropout?: nn.Dropout; + private readonly layernormPosition: LayerNormPosition; + + constructor(config: TransformerModuleConfig) { + super(); + + if (config.attention.present) { + this.attn = new SelfAttention(config.embeddingSize, config, false); + if (config.layerNormalization.transformer.present) { + this.lnAttn = createNorm(config.embeddingSize, config.layerNormalization); + } + } + + if (config.mlp.present) { + this.mlp = new MLP(config.embeddingSize, config.mlp); + if (config.layerNormalization.transformer.present) { + this.lnMlp = createNorm(config.embeddingSize, config.layerNormalization); + } + } + + if (config.dropout.transformer.residual > 0) { + this.dropout = new nn.Dropout(config.dropout.transformer.residual); + } + + this.layernormPosition = config.layerNormalization.transformer.position; + } + + forward(input: Tensor, attentionMask: Tensor | null = null): Tensor { + let x = input; + if (this.attn) { + const residual = input; + if (this.lnAttn && this.layernormPosition === 'pre') { + x = this.lnAttn.forward(input) as Tensor; + } + const attnOutput = this.attn.forward(x, { attentionMask }); + x = residual.add(attnOutput.output); + if (this.lnAttn && this.layernormPosition === 'post') { + x = this.lnAttn.forward(x) as Tensor; + } + } + + if (this.mlp) { + const residual2 = x; + if (this.lnMlp && this.layernormPosition === 'pre') { + x = this.lnMlp.forward(x) as Tensor; + } + x = this.mlp.forward(x); + if (this.dropout) { + x = this.dropout.forward(x); + } + x = residual2.add(x); + if (this.lnMlp && this.layernormPosition === 'post') { + x = this.lnMlp.forward(x) as Tensor; + } + } + + return x; + } +} diff --git a/examples/piston-train-toy/src/lib/train/model/modules/mlp.ts b/examples/piston-train-toy/src/lib/train/model/modules/mlp.ts new file mode 100644 index 00000000..6556d893 --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/model/modules/mlp.ts @@ -0,0 +1,37 @@ +import type { MLPConfig } from '$lib/workspace/config'; + +import { nn, Tensor } from '@piston-ml/piston-web'; + +export class MLP extends nn.Module { + private readonly upProj: nn.Linear; + private readonly downProj: nn.Linear; + private readonly activation: (x: Tensor) => Tensor; + private readonly config: MLPConfig; + + /** + * @param config - Model configuration + */ + constructor(nEmbed: number, mlpConfig: MLPConfig) { + super(); + + this.config = mlpConfig; + + const intermediateSize = this.config.hiddenExpansionFactor * nEmbed; + + this.upProj = new nn.Linear(nEmbed, intermediateSize); + this.downProj = new nn.Linear(intermediateSize, nEmbed); + + this.activation = (x: Tensor): Tensor => x[mlpConfig.activation](); + } + + /** + * Forward pass through the MLP + * @param input - Input tensor + * @returns Output tensor + */ + forward(input: Tensor): Tensor { + let h = this.upProj.forward(input); + h = this.activation(h); + return this.downProj.forward(h); + } +} diff --git a/examples/piston-train-toy/src/lib/train/model/modules/norm.ts b/examples/piston-train-toy/src/lib/train/model/modules/norm.ts new file mode 100644 index 00000000..a2e21808 --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/model/modules/norm.ts @@ -0,0 +1,15 @@ +import type { LayerNormalizationConfig } from '$lib/workspace/config'; + +import { nn } from '@piston-ml/piston-web'; + +export type NormModule = nn.LayerNorm | nn.RMSNorm; + +export function createNorm(normalizedShape: number, config: LayerNormalizationConfig): NormModule { + if (config.type === 'layernorm') { + return new nn.LayerNorm(normalizedShape, { eps: config.eps }); + } else if (config.type === 'rmsnorm') { + return new nn.RMSNorm(normalizedShape, { eps: config.eps }); + } else { + throw new Error(`Unknown norm type: ${config.type}`); + } +} diff --git a/examples/piston-train-toy/src/lib/train/model/transformer.ts b/examples/piston-train-toy/src/lib/train/model/transformer.ts new file mode 100644 index 00000000..f7991ab0 --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/model/transformer.ts @@ -0,0 +1,556 @@ +/** + * @fileoverview Implementation of generic encoder-decoder, encoder-only, and decoder-only + * transformer models + */ + +import type { Config } from '$lib/workspace/config'; +import type { CrossEntropyLoss, Tensor } from '@piston-ml/piston-web'; + +import { nn } from '@piston-ml/piston-web'; +import * as piston from '@piston-ml/piston-web'; + +import type { DecoderKVCache } from './cache'; + +import { MLMHead, Pooler } from './bidirectional'; +import { createEmptyDecoderKVCache } from './cache'; +import { buildTransformerConfigCommon, type TransformerModuleConfig } from './config'; +import { DecoderLayer } from './modules/decoder'; +import { EncoderLayer } from './modules/encoder'; +import { + addPositionalEncodingToEmbeddings as addPositionalEncodingToEmbedding, + buildLmHead, + computeAutoregressiveCrossEntropyLoss, + createCrossEntropyCriterion, + maybeCreateFinalLayerNorm, + maybeCreateLearnedPositionEmbedding +} from './utils'; + +export type EncoderDecoderTransformerConfig = TransformerModuleConfig & { + nEncoderLayer: number; + nDecoderLayer: number; + blockSizeSrc: number; + blockSizeTgt: number; +}; + +export type EncoderForEncoderDecoderDict = { + drop?: nn.Dropout; + wordEmbedding: nn.Embedding; + positionEmbedding?: nn.Embedding; + layer: nn.ModuleList; + layerNorm?: nn.Module<[Tensor], Tensor>; +}; + +export type DecoderForEncoderDecoderDict = { + drop?: nn.Dropout; + wordEmbedding: nn.Embedding; + positionEmbedding?: nn.Embedding; + layer: nn.ModuleList; + layerNorm?: nn.Module<[Tensor], Tensor>; +}; + +export type EncoderDecoderDict = { + encoder: nn.ModuleDict; + decoder: nn.ModuleDict; +}; + +/** + * Encoder-Decoder Transformer model implementation + */ +export type EncoderDecoderForwardOptions = { + targets?: Tensor | null; + srcPaddingMask?: Tensor | null; + tgtPaddingMask?: Tensor | null; + encoderHiddenStates?: Tensor | null; + kvCache?: DecoderKVCache | null; +}; + +export class EncoderDecoderTransformer extends nn.Module { + public config: EncoderDecoderTransformerConfig; + readonly lmHead: nn.Linear; + public encoder: nn.ModuleDict; + public decoder: nn.ModuleDict; + private readonly criterion: CrossEntropyLoss; + + constructor(config: Config, vocabSize: number, blockSizeSrc: number, blockSizeTgt: number) { + super(); + + this.config = { + ...buildTransformerConfigCommon(config, vocabSize), + nEncoderLayer: config.model.encoderDecoder.encoderLayers, + nDecoderLayer: config.model.encoderDecoder.decoderLayers, + blockSizeSrc, + blockSizeTgt + }; + + const encoderDict: EncoderForEncoderDecoderDict = { + drop: this.config.dropout.present ? new nn.Dropout(this.config.dropout.embedding) : undefined, + wordEmbedding: new nn.Embedding(this.config.vocabSize, this.config.embeddingSize), + layer: new nn.ModuleList( + Array.from({ length: this.config.nEncoderLayer }).map(() => new EncoderLayer(this.config)) + ) + }; + + const decoderDict: DecoderForEncoderDecoderDict = { + drop: this.config.dropout.present ? new nn.Dropout(this.config.dropout.embedding) : undefined, + wordEmbedding: new nn.Embedding(this.config.vocabSize, this.config.embeddingSize), + layer: new nn.ModuleList( + Array.from({ length: this.config.nDecoderLayer }).map( + // With cross-attention to consume encoder hidden states + () => new DecoderLayer(this.config, true) + ) + ) + }; + + // Learned positional embeddings (transformer Embedding variant) + encoderDict.positionEmbedding = maybeCreateLearnedPositionEmbedding( + this.config.positionalEncoding, + this.config.blockSizeSrc, + this.config.embeddingSize + ); + decoderDict.positionEmbedding = maybeCreateLearnedPositionEmbedding( + this.config.positionalEncoding, + this.config.blockSizeTgt, + this.config.embeddingSize + ); + + // Final layer norms if configured + encoderDict.layerNorm = maybeCreateFinalLayerNorm( + this.config.embeddingSize, + this.config.layerNormalization + ); + decoderDict.layerNorm = maybeCreateFinalLayerNorm( + this.config.embeddingSize, + this.config.layerNormalization + ); + + this.encoder = new nn.ModuleDict(encoderDict); + this.decoder = new nn.ModuleDict(decoderDict); + + this.lmHead = buildLmHead(this.config.embeddingSize, this.config.vocabSize); + + this.criterion = createCrossEntropyCriterion(); + } + + forward( + inputIdsSrc: Tensor | null, + inputIdsTgt: Tensor, + options: EncoderDecoderForwardOptions = {} + ): [Tensor, Tensor | null] { + const targets = options.targets ?? null; + const srcPaddingMask = options.srcPaddingMask ?? null; + const tgtPaddingMask = options.tgtPaddingMask ?? null; + const explicitEncStates = options.encoderHiddenStates ?? null; + const kvCache = options.kvCache ?? null; + // Encode source sequence if not provided + let finalEncoderHiddenStates: Tensor; + if (explicitEncStates) { + finalEncoderHiddenStates = explicitEncStates; + } else { + if (!inputIdsSrc) { + throw new Error('Either inputIdsSrc or encoderHiddenStates must be provided'); + } + finalEncoderHiddenStates = this.encode(inputIdsSrc, { srcPaddingMask }); + } + + // Decode target sequence + const [_batchSize, tgtSeqLen] = inputIdsTgt.size(); + + if (!tgtSeqLen) { + throw new Error( + 'Target input tensor has no sequence length (did you forget to pass input as batches?)' + ); + } + + // Get target embeddings + let targetWordEmbeddings = this.decoder.dict.wordEmbedding.forward(inputIdsTgt); + + // Use cache length (if any) as position offset for absolute encodings during incremental decoding + const posOffsetDec = kvCache?.layers?.[0]?.self?.length ?? 0; + + targetWordEmbeddings = addPositionalEncodingToEmbedding( + targetWordEmbeddings, + this.config.positionalEncoding, + this.decoder.dict.positionEmbedding, + this.decoder.dict.drop, + posOffsetDec + ); + + // Pass through each decoder layer + let hiddenStates = targetWordEmbeddings; + + const useCache = kvCache !== null; + const cacheObj = useCache ? kvCache! : createEmptyDecoderKVCache(this.config.nDecoderLayer); + for (let i = 0; i < this.config.nDecoderLayer; i++) { + const layerModule = this.decoder.dict.layer[i] as DecoderLayer; + const result = layerModule.forward(hiddenStates, { + encoderHiddenStates: finalEncoderHiddenStates, + srcPaddingMask, + tgtPaddingMask, + cache: cacheObj.layers[i] + }); + if (useCache) { + cacheObj.layers[i] = result.pastKeyValues!; + hiddenStates = result.output; + } else { + hiddenStates = result.output; + } + } + + // Apply final layer normalization + if (this.decoder.dict.layerNorm) { + hiddenStates = this.decoder.dict.layerNorm.forward(hiddenStates); + } + + // Project to vocabulary + const logits = this.lmHead.forward(hiddenStates); + + const loss = computeAutoregressiveCrossEntropyLoss(logits, targets, this.criterion); + + return [logits, loss]; + } + + /** + * Encode source sequence + * @param inputIdsSrc - Source input tensor + * @param srcPaddingMask - Source padding mask + * @returns Encoder hidden states + */ + encode( + inputIdsSrc: Tensor, + { srcPaddingMask }: { srcPaddingMask: Tensor | null } = { srcPaddingMask: null } + ): Tensor { + const [_batchSize, srcSeqLen] = inputIdsSrc.size(); + + if (!srcSeqLen) { + throw new Error( + 'Source input tensor has no sequence length (did you forget to pass input as batches?)' + ); + } + + // Get source embeddings + let sourceWordEmbeddings = this.encoder.dict.wordEmbedding.forward(inputIdsSrc); + + sourceWordEmbeddings = addPositionalEncodingToEmbedding( + sourceWordEmbeddings, + this.config.positionalEncoding, + this.encoder.dict.positionEmbedding, + this.encoder.dict.drop + ); + + // Pass through each encoder layer + let hiddenStates = sourceWordEmbeddings; + + for (let i = 0; i < this.config.nEncoderLayer; i++) { + const layerModule = this.encoder.dict.layer[i] as EncoderLayer; + const layerOutput = layerModule.forward(hiddenStates, srcPaddingMask); + hiddenStates = layerOutput; + } + + // Apply final layer normalization + if (this.encoder.dict.layerNorm) { + hiddenStates = this.encoder.dict.layerNorm.forward(hiddenStates) as Tensor; + } + + return hiddenStates; + } +} + +type DecoderTransformerConfig = TransformerModuleConfig & { + nLayers: number; + blockSize: number; +}; + +type DecoderTransformerDict = { + drop?: nn.Dropout; + wordEmbedding: nn.Embedding; + positionEmbedding?: nn.Embedding; + layer: nn.ModuleList; + lnF?: nn.Module<[Tensor], Tensor>; +}; + +export class DecoderTransformer extends nn.Module { + public config: DecoderTransformerConfig; + public decoder: nn.ModuleDict; + readonly lmHead: nn.Linear; + private readonly criterion: CrossEntropyLoss; + + constructor(config: Config, vocabSize: number, blockSize: number) { + super(); + + this.config = { + ...buildTransformerConfigCommon(config, vocabSize), + blockSize, + nLayers: config.model.layers + }; + + const transformerDict: DecoderTransformerDict = { + drop: this.config.dropout.present ? new nn.Dropout(this.config.dropout.embedding) : undefined, + wordEmbedding: new nn.Embedding(this.config.vocabSize, this.config.embeddingSize), + layer: new nn.ModuleList( + Array.from({ length: this.config.nLayers }).map(() => new DecoderLayer(this.config, false)) + ) + }; + + // Only create positional embedding layer for learned positional encoding + transformerDict.positionEmbedding = maybeCreateLearnedPositionEmbedding( + this.config.positionalEncoding, + this.config.blockSize, + this.config.embeddingSize + ); + + transformerDict.lnF = maybeCreateFinalLayerNorm( + this.config.embeddingSize, + this.config.layerNormalization + ); + + this.decoder = new nn.ModuleDict(transformerDict); + + // Output projection with optional weight tying to token embeddings + this.lmHead = buildLmHead(this.config.embeddingSize, this.config.vocabSize); + + this.criterion = createCrossEntropyCriterion(); + } + + /** + * @param input - Input tensor of token IDs [batch_size, seq_len] + * @param targets - Target tensor of token IDs [batch_size, + * seq_len] + * @returns [logits, loss] + */ + forward( + input: Tensor, + options: { targets?: Tensor | null; kvCache?: DecoderKVCache | null } = {} + ): [Tensor, Tensor | null] { + const targets = options.targets ?? null; + const kvCache = options.kvCache ?? null; + const [_batchSize, seqLen] = input.size(); + + if (!seqLen) { + throw new Error( + 'Input tensor has no sequence length (did you forget to pass input as batches?)' + ); + } + + // Get token embeddings + let wordEmbeddings = this.decoder.dict.wordEmbedding.forward(input); + + // Use cache length (if any) as position offset for absolute encodings during incremental decoding + const posOffset = kvCache?.layers?.[0]?.self?.length ?? 0; + + wordEmbeddings = addPositionalEncodingToEmbedding( + wordEmbeddings, + this.config.positionalEncoding, + this.decoder.dict.positionEmbedding, + this.decoder.dict.drop, + posOffset + ); + + // Pass through each transformer layer + let hiddenStates = wordEmbeddings; + + const useCache = kvCache !== null; + const cacheObj = useCache ? kvCache! : createEmptyDecoderKVCache(this.config.nLayers); + for (let i = 0; i < this.config.nLayers; i++) { + const layerModule = this.decoder.dict.layer[i] as DecoderLayer; + const result = layerModule.forward(hiddenStates, { cache: cacheObj.layers[i] }); + if (useCache) { + cacheObj.layers[i] = result.pastKeyValues!; + hiddenStates = result.output; + } else { + hiddenStates = result.output; + } + } + + // Apply final layer normalization + if (this.decoder.dict.lnF) { + hiddenStates = this.decoder.dict.lnF.forward(hiddenStates) as Tensor; + } + + // Project to vocabulary + const logits = this.lmHead.forward(hiddenStates); + + const loss = computeAutoregressiveCrossEntropyLoss(logits, targets, this.criterion); + + return [logits, loss ?? null]; + } +} + +export type EncoderTransformerConfig = TransformerModuleConfig & { + typeVocabSize: number; + nLayers: number; + blockSize: number; + attentionMasking: { + padMask: boolean; + }; + pooling: { + present: boolean; + }; + mlmHead: { + present: boolean; + }; +}; + +type EncoderTransformerDict = { + dropout?: nn.Dropout; + wordEmbedding: nn.Embedding; + positionEmbedding?: nn.Embedding; + tokenTypeEmbedding: nn.Embedding; + layer: nn.ModuleList; + layerNorm?: nn.Module<[Tensor], Tensor>; +}; + +export class EncoderTransformer extends nn.Module { + public config: EncoderTransformerConfig; + public encoder: nn.ModuleDict; + readonly mlmHead?: MLMHead; + readonly pooler?: Pooler; + private readonly criterion: CrossEntropyLoss; + + constructor(config: Config, vocabSize: number, blockSize: number, typeVocabSize: number = 2) { + super(); + + this.config = { + ...buildTransformerConfigCommon(config, vocabSize), + typeVocabSize: typeVocabSize, + nLayers: config.model.layers, + blockSize, + // We hardcode these for now, as all of our tasks focus on MLM right now. + attentionMasking: { + padMask: true + }, + pooling: { + present: false + }, + mlmHead: { + present: true, + } + }; + + const encoderDict: EncoderTransformerDict = { + dropout: this.config.dropout.present + ? new nn.Dropout(this.config.dropout.embedding) + : undefined, + wordEmbedding: new nn.Embedding(this.config.vocabSize, this.config.embeddingSize), + tokenTypeEmbedding: new nn.Embedding(this.config.typeVocabSize, this.config.embeddingSize), + layer: new nn.ModuleList( + Array.from({ length: this.config.nLayers }).map(() => new EncoderLayer(this.config)) + ) + }; + + // Only create positional embedding layer for learned positional encoding + if (this.config.positionalEncoding.present) { + encoderDict.positionEmbedding = new nn.Embedding( + this.config.blockSize, + this.config.embeddingSize + ); + } + + encoderDict.layerNorm = maybeCreateFinalLayerNorm( + this.config.embeddingSize, + this.config.layerNormalization + ) as nn.Module<[Tensor], Tensor> | undefined; + + this.encoder = new nn.ModuleDict(encoderDict); + + // MLM head for masked language modeling + if (this.config.mlmHead.present) { + this.mlmHead = new MLMHead( + this.config.embeddingSize, + this.config.vocabSize, + this.config.layerNormalization, + ); + } + + // Pooler for classification tasks + if (this.config.pooling.present) { + this.pooler = new Pooler(this.config.embeddingSize); + } + + this.criterion = createCrossEntropyCriterion(); + } + + /** + * @param inputIds - Input tensor of token IDs [batch_size, seq_len] + * @param tokenTypeIds - Token type/segment IDs [batch_size, seq_len] + * @param attentionMask - Attention mask [batch_size, seq_len] (1 for real tokens, 0 for padding) + * @param targets - Target tensor for MLM [batch_size, seq_len] (-100 for non-masked positions) + * @returns [lastHiddenState, pooledOutput?, mlmLogits?, loss?] + */ + forward( + inputIds: Tensor, + { + tokenTypeIds, + attentionMask, + targets + }: { tokenTypeIds?: Tensor | null; attentionMask?: Tensor | null; targets?: Tensor | null } = { + tokenTypeIds: null, + attentionMask: null, + targets: null + } + ): [Tensor, Tensor | null, Tensor | null, Tensor | null] { + const [batchSize, seqLen] = inputIds.size(); + + if (!seqLen) { + throw new Error( + 'Input tensor has no sequence length (did you forget to pass input as batches?)' + ); + } + + // Get token embeddings + let wordEmbedding = this.encoder.dict.wordEmbedding.forward(inputIds); + + // Add segment/token type embeddings + if (tokenTypeIds == null) { + tokenTypeIds = piston.zeros([batchSize, seqLen], { + device: inputIds.device, + dtype: piston.int32 + }); + } + const typeEmbeddings = this.encoder.dict.tokenTypeEmbedding.forward(tokenTypeIds!); + wordEmbedding = wordEmbedding.add(typeEmbeddings); + + wordEmbedding = addPositionalEncodingToEmbedding( + wordEmbedding, + this.config.positionalEncoding, + this.encoder.dict.positionEmbedding, + this.encoder.dict.dropout + ); + + // Pass through each encoder layer + let hiddenStates = wordEmbedding; + + for (let i = 0; i < this.config.nLayers; i++) { + const layerModule = this.encoder.dict.layer[i] as EncoderLayer; + const layerOutput = layerModule.forward(hiddenStates, attentionMask); + hiddenStates = layerOutput; + } + + // Apply final layer normalization + if (this.encoder.dict.layerNorm) { + hiddenStates = this.encoder.dict.layerNorm.forward(hiddenStates) as Tensor; + } + + // Pooled output for classification tasks + let pooledOutput: Tensor | null = null; + if (this.pooler) { + pooledOutput = this.pooler.forward(hiddenStates); + } + + // MLM logits + let mlmLogits: Tensor | null = null; + if (this.mlmHead) { + mlmLogits = this.mlmHead.forward(hiddenStates); + } + + // Calculate MLM loss if targets are provided + let loss: Tensor | null = null; + if (targets && mlmLogits) { + // Only compute loss on masked positions (where targets != -100) + const flatLogits = mlmLogits.view([-1, mlmLogits.size(-1)]); + const flatTargets = targets.view(-1); + loss = this.criterion.forward(flatLogits, flatTargets); + } + + return [hiddenStates, pooledOutput, mlmLogits, loss]; + } +} diff --git a/examples/piston-train-toy/src/lib/train/model/utils.ts b/examples/piston-train-toy/src/lib/train/model/utils.ts new file mode 100644 index 00000000..319565c2 --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/model/utils.ts @@ -0,0 +1,145 @@ +import type { LayerNormalizationConfig, PositionEncodingConfig } from '$lib/workspace/config'; + +import { + arange, + CrossEntropyLoss, + Device, + gpu, + int32, + type Module as ModuleType, + nn, + type Tensor +} from '@piston-ml/piston-web'; + +import { createNorm } from './modules/norm'; + +/** + * Create a causal (lower triangular) mask. + * @param queryLen - Length of the current query. + * @param keyLen - Length of the key (which may include cached tokens). + * @returns Causal mask tensor of shape [1, numHeads, queryLen, keyLen]. + */ +export function createCausalMask(queryLen: number, keyLen: number): Tensor { + // General causal mask supporting past KV cache where keyLen may exceed queryLen. + // We want to mask future positions: for each query i, keys j > pastLen + i are masked. + // pastLen is inferred as keyLen - queryLen when using KV cache (else 0). + const pastLen = Math.max(0, keyLen - queryLen); + const i = arange({ end: queryLen, device: gpu, dtype: int32 }) + .unsqueeze(1) + .broadcastTo([queryLen, keyLen]); + const j = arange({ end: keyLen, device: gpu, dtype: int32 }) + .unsqueeze(0) + .broadcastTo([queryLen, keyLen]); + // Mask is true where positions are allowed: j <= pastLen + i + return j.le(i.add(pastLen)); +} + +/** + * Create position IDs tensor [0, 1, 2, ..., seqLen-1] and broadcast to batch size + * @param seqLen - Sequence length + * @param batchSize - Batch size + * @param device - Device to place tensor on + * @returns Position IDs tensor + */ +function createPositionIds( + seqLen: number, + batchSize: number, + device: Device, + offset: number = 0 +): Tensor { + // Create position IDs tensor [offset, offset+1, ..., offset+seqLen-1] and broadcast to batch + const positionIds = arange({ end: seqLen, device, dtype: int32 }).add(offset).cast(int32); + // Reshape to [1, seqLen] and broadcast to [batchSize, seqLen] + return positionIds.unsqueeze(0).broadcastTo([batchSize, seqLen]); +} + +/** + * Apply mask to attention scores + * @param onFalse - Attention scores + * @param mask - Mask tensor + * @param onTrueValue - Value to fill masked positions with + * @returns Masked scores + */ +export function maskedFill(onTrue: Tensor, mask: Tensor, onFalseValue: number): Tensor { + return onTrue.where(mask, onFalseValue); +} + +export function addPositionalEncodingToEmbeddings( + embeddings: Tensor, + positionalEncodingConfig: PositionEncodingConfig, + positionEmbeddings?: ModuleType<[Tensor], Tensor>, + dropout?: ModuleType<[Tensor], Tensor>, + additionalPositionOffset: number = 0 +): Tensor { + const [batchSize, seqLen] = embeddings.size(); + if (positionalEncodingConfig.present) { + // Add positional embeddings + const positions = createPositionIds( + seqLen, + batchSize, + embeddings.device, + additionalPositionOffset + ); + const positionEmbeddingsOutput = positionEmbeddings!.forward(positions); + embeddings = embeddings.add(positionEmbeddingsOutput); + // Apply embedding dropout if configured + if (dropout) { + embeddings = dropout.forward(embeddings); + } + } + return embeddings; +} + +export function createCrossEntropyCriterion() { + return new CrossEntropyLoss({ + ignoreIndex: -100 + }); +} + +/** + * Optionally create a learned positional embedding layer (nn.Embedding variant) + */ +export function maybeCreateLearnedPositionEmbedding( + positionalEncoding: PositionEncodingConfig, + blockSize: number, + embeddingSize: number +): nn.Embedding | undefined { + if (positionalEncoding.present) { + return new nn.Embedding(blockSize, embeddingSize); + } + return undefined; +} + +/** + * Optionally create final layer norm if transformer normalization position is 'pre' + */ +export function maybeCreateFinalLayerNorm( + embeddingSize: number, + layerNormalization: LayerNormalizationConfig +): ModuleType<[Tensor], Tensor> | undefined { + if (layerNormalization.transformer.present && layerNormalization.transformer.position === 'pre') { + return createNorm(embeddingSize, layerNormalization) as unknown as ModuleType<[Tensor], Tensor>; + } + return undefined; +} + +/** + * Build a language modeling head using standard nn.Linear with optional weight tying. + */ +export function buildLmHead(embeddingSize: number, vocabSize: number): nn.Linear { + return new nn.Linear(embeddingSize, vocabSize, false); +} + +/** + * Compute cross-entropy loss if both logits and targets are provided, flattening as needed + */ +export function computeAutoregressiveCrossEntropyLoss( + logits: Tensor | null, + targets: Tensor | null, + criterion: CrossEntropyLoss +): Tensor | null { + if (!logits || !targets) { + return null; + } + return criterion.forward(logits.view([-1, logits.size(-1)]), targets.view(-1)); +} From 234498446ff16097d9e582022dca1dc9f43c86e8 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 26 Oct 2025 16:02:12 -0600 Subject: [PATCH 513/590] Improve typing in dataloader --- packages/web/src/utils/data/dataloader.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/web/src/utils/data/dataloader.ts b/packages/web/src/utils/data/dataloader.ts index 69b92e2b..ebd3848c 100644 --- a/packages/web/src/utils/data/dataloader.ts +++ b/packages/web/src/utils/data/dataloader.ts @@ -55,7 +55,7 @@ class DataLoaderIterator implements Iterator { private nextData(): IteratorResult { const nextIdx = this.samplerIter.next(); if (nextIdx.done) { - return { value: undefined as unknown as B, done: true }; + return { value: undefined, done: true }; } const idxOrIndices = nextIdx.value as number | number[]; @@ -69,12 +69,12 @@ class DataLoaderIterator implements Iterator { } else { data = this.dataset.getItem(idxOrIndices as number); } - const result = this.collateFn ? this.collateFn(data as T[]) : (data as unknown as B); + const result = this.collateFn ? this.collateFn(data as T[]) : (data as B); return { value: result, done: false }; } else { // Iterable dataset fetch logic if (this.ended) { - return { value: undefined as unknown as B, done: true }; + return { value: undefined, done: true }; } let data: T | T[]; @@ -92,18 +92,18 @@ class DataLoaderIterator implements Iterator { batch.length === 0 || (this.dropLast && batch.length < (idxOrIndices as number[]).length) ) { - return { value: undefined as unknown as B, done: true }; + return { value: undefined, done: true }; } data = batch; } else { const nextVal = this.datasetIter!.next(); if (nextVal.done) { this.ended = true; - return { value: undefined as unknown as B, done: true }; + return { value: undefined, done: true }; } data = nextVal.value; } - const result = this.collateFn ? this.collateFn(data as T[]) : (data as unknown as B); + const result = this.collateFn ? this.collateFn(data as T[]) : (data as B); return { value: result, done: false }; } } @@ -222,12 +222,12 @@ export class DataLoader implements Iterable { sampler = config.sampler; } else { sampler = shuffle - ? new RandomSampler(dataset, { + ? new RandomSampler(dataset as { length: number }, { replacement: false, numSamples: undefined, generator: config.generator, }) - : new SequentialSampler(dataset); + : new SequentialSampler(dataset as { length: number }); } } From 25e27a36529a9a54496e742473ac431f62707dae Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 26 Oct 2025 16:02:44 -0600 Subject: [PATCH 514/590] Add async iterator support to backend data utils --- packages/web/src/utils/data/dataloader.ts | 114 ++++++++++++++++++++-- packages/web/src/utils/data/dataset.ts | 12 ++- 2 files changed, 117 insertions(+), 9 deletions(-) diff --git a/packages/web/src/utils/data/dataloader.ts b/packages/web/src/utils/data/dataloader.ts index ebd3848c..f9880990 100644 --- a/packages/web/src/utils/data/dataloader.ts +++ b/packages/web/src/utils/data/dataloader.ts @@ -3,7 +3,7 @@ import { Random } from "random-js"; import type { Tensor } from "@/tensor"; import { defaultCollate } from "./collate"; -import { Dataset, IterableDataset } from "./dataset"; +import { AsyncIterableDataset, Dataset, IterableDataset } from "./dataset"; import { BatchSampler, RandomSampler, Sampler, SequentialSampler } from "./sampler"; /** @@ -131,6 +131,80 @@ class DataLoaderIterator implements Iterator { } } +class DataLoaderAsyncIterator implements AsyncIterator { + private dataset: Dataset; + private autoCollation: boolean; + private dropLast: boolean; + private indexSampler: Sampler; + private collateFn?: (batch: T[]) => B; + private samplerIter: Iterator; + private datasetAsyncIter?: AsyncIterator; + private ended: boolean; + + constructor(loader: DataLoader) { + this.dataset = loader.dataset; + this.autoCollation = loader.autoCollation; + this.dropLast = loader.dropLast!; + this.indexSampler = loader.indexSampler; + this.collateFn = loader.collateFn; + this.samplerIter = this.indexSampler[Symbol.iterator](); + this.ended = false; + + this.datasetAsyncIter = (this.dataset as AsyncIterableDataset)[Symbol.asyncIterator](); + } + + private async nextData(): Promise> { + const nextIdx = this.samplerIter.next(); + if (nextIdx.done) { + return { value: undefined, done: true }; + } + + const idxOrIndices = nextIdx.value as number | number[]; + + if (!this.datasetAsyncIter || this.ended) { + // Shouldn't happen: async iterator used with non-async dataset + return { value: undefined, done: true }; + } + + let data: T | T[]; + if (this.autoCollation) { + const batch: T[] = []; + const count = (idxOrIndices as number[]).length; + for (let i = 0; i < count; i++) { + const nextVal = await this.datasetAsyncIter.next(); + if (nextVal.done) { + this.ended = true; + break; + } + batch.push(nextVal.value); + } + if ( + batch.length === 0 || + (this.dropLast && batch.length < (idxOrIndices as number[]).length) + ) { + return { value: undefined, done: true }; + } + data = batch; + } else { + const nextVal = await this.datasetAsyncIter.next(); + if (nextVal.done) { + this.ended = true; + return { value: undefined, done: true }; + } + data = nextVal.value; + } + + const resultValue = this.collateFn + ? await Promise.resolve(this.collateFn(data as T[])) + : (data as B); + return { value: resultValue, done: false }; + } + + async next(): Promise> { + return this.nextData(); + } +} + export interface DataLoaderConfig { batchSize?: number; shuffle?: boolean; @@ -141,7 +215,7 @@ export interface DataLoaderConfig { generator?: Random; } -export class DataLoader implements Iterable { +export class DataLoader implements Iterable, AsyncIterable { public dataset: Dataset; public batchSize?: number; public dropLast?: boolean; @@ -150,6 +224,8 @@ export class DataLoader implements Iterable { public collateFn?: (batch: T[]) => B; /** @internal */ public isIterableDataset: boolean; + /** @internal */ + public isAsyncIterableDataset: boolean; public generator?: Random; /** @internal */ public iterableDatasetLenCalled?: number; @@ -168,10 +244,11 @@ export class DataLoader implements Iterable { }: DataLoaderConfig = config; let batchSize: number | undefined = "batchSize" in config ? config.batchSize : 1; - // Check if this is an iterable dataset using instanceof + // Check if this is an iterable or async-iterable dataset using instanceof this.isIterableDataset = dataset instanceof IterableDataset; + this.isAsyncIterableDataset = dataset instanceof AsyncIterableDataset; - if (this.isIterableDataset) { + if (this.isIterableDataset || this.isAsyncIterableDataset) { if (config.sampler !== undefined) { throw new Error( `DataLoader with IterableDataset: expected unspecified sampler option, but got sampler=${config.sampler}`, @@ -208,7 +285,7 @@ export class DataLoader implements Iterable { } } - if (this.isIterableDataset) { + if (this.isIterableDataset || this.isAsyncIterableDataset) { if (shuffle) { console.warn( "shuffle=True with IterableDataset has no effect. " + @@ -275,8 +352,11 @@ export class DataLoader implements Iterable { } public get length(): number { - if (this.isIterableDataset) { - const length = (this.iterableDatasetLenCalled = (this.dataset as IterableDataset).length); + if (this.isIterableDataset || this.isAsyncIterableDataset) { + const length = (this.iterableDatasetLenCalled = this.dataset.length); + if (length === undefined) { + throw new Error("Dataset does not implement length"); + } if (this.batchSize !== undefined) { // IterableDataset doesn't allow custom sampler or batch_sampler if (this.dropLast) { @@ -292,6 +372,11 @@ export class DataLoader implements Iterable { } public *[Symbol.iterator](): Iterator { + if (this.isAsyncIterableDataset) { + throw new Error( + "DataLoader with AsyncIterableDataset must be consumed with an async iterator", + ); + } const iterator = new DataLoaderIterator(this); let result = iterator.next(); while (!result.done) { @@ -299,6 +384,21 @@ export class DataLoader implements Iterable { result = iterator.next(); } } + + public [Symbol.asyncIterator](): AsyncIterator { + if (this.isAsyncIterableDataset) { + return new DataLoaderAsyncIterator(this); + } + + // Fallback: wrap sync iterator into an async iterator so for-await works universally + const syncIterator = this[Symbol.iterator](); + return { + next: async () => { + const result = syncIterator.next(); + return Promise.resolve(result); + }, + } as AsyncIterator; + } } export { defaultCollate }; \ No newline at end of file diff --git a/packages/web/src/utils/data/dataset.ts b/packages/web/src/utils/data/dataset.ts index 59612def..6b116e87 100644 --- a/packages/web/src/utils/data/dataset.ts +++ b/packages/web/src/utils/data/dataset.ts @@ -21,8 +21,8 @@ export abstract class Dataset { /** * Optional: subclasses can implement length if known */ - public get length(): number { - throw new Error("Dataset subclass should implement length getter"); + public get length(): number | undefined { + return undefined; } } @@ -43,6 +43,14 @@ export abstract class IterableDataset extends Dataset implements Iterable< } } +export abstract class AsyncIterableDataset extends Dataset implements AsyncIterable { + public abstract [Symbol.asyncIterator](): AsyncIterator; + + public getItem(_index: number): T { + throw new Error("AsyncIterableDataset does not support indexed access"); + } +} + /** * {@link Dataset} wrapping tensors. * From f0da57bcc87397ef95f58a46feb318fabc48672e Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 26 Oct 2025 16:02:51 -0600 Subject: [PATCH 515/590] Newline --- packages/web/src/utils/data/dataloader.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web/src/utils/data/dataloader.ts b/packages/web/src/utils/data/dataloader.ts index f9880990..d2913689 100644 --- a/packages/web/src/utils/data/dataloader.ts +++ b/packages/web/src/utils/data/dataloader.ts @@ -401,4 +401,4 @@ export class DataLoader implements Iterable, AsyncIterable } } -export { defaultCollate }; \ No newline at end of file +export { defaultCollate }; From 61dd614cfed729dab1a8fe79045c0d3652f668ec Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 26 Oct 2025 16:06:53 -0600 Subject: [PATCH 516/590] Formatting --- examples/piston-train-toy/svelte.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/piston-train-toy/svelte.config.js b/examples/piston-train-toy/svelte.config.js index 92d0e09f..64694ad1 100644 --- a/examples/piston-train-toy/svelte.config.js +++ b/examples/piston-train-toy/svelte.config.js @@ -1,4 +1,4 @@ -import adapter from "@sveltejs/adapter-static"; +import adapter from '@sveltejs/adapter-static'; import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; /** @type {import('@sveltejs/kit').Config} */ From be1c61c20e32a408a20a00b43ddfdf815304799a Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 26 Oct 2025 16:07:50 -0600 Subject: [PATCH 517/590] Set opt-level to 3 in workspace Cargo.toml --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index fe3fad39..7e992470 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ debug-assertions = true [profile.release] panic = 'abort' lto = "fat" -codegen-units = 1 +opt-level = 3 [profile.profiling] inherits = "release" From 1825c30910219fe3188a2985a4d6867547686280 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 26 Oct 2025 16:10:29 -0600 Subject: [PATCH 518/590] Reformat example package.json --- examples/piston-train-toy/package.json | 86 +++++++++++++------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/examples/piston-train-toy/package.json b/examples/piston-train-toy/package.json index f6bdc02c..76a5d1cc 100644 --- a/examples/piston-train-toy/package.json +++ b/examples/piston-train-toy/package.json @@ -1,44 +1,44 @@ { - "name": "piston-train-toy", - "private": true, - "version": "0.0.1", - "type": "module", - "scripts": { - "dev": "vite dev", - "build": "vite build", - "preview": "vite preview", - "prepare": "svelte-kit sync || echo ''", - "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", - "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", - "lint": "eslint . && prettier --check .", - "format": "prettier --write ." - }, - "dependencies": { - "@piston-ml/piston-web": "workspace:^", - "chart.js": "^4.4.7", - "mathjs": "^14.2.1" - }, - "devDependencies": { - "@eslint/compat": "^1.2.5", - "@eslint/js": "^9.18.0", - "@sveltejs/adapter-static": "^3.0.8", - "@sveltejs/kit": "^2.16.0", - "@sveltejs/vite-plugin-svelte": "^5.0.0", - "@tailwindcss/vite": "^4.0.0", - "@webgpu/types": "^0.1.60", - "eslint": "^9.18.0", - "eslint-config-prettier": "^10.0.1", - "eslint-plugin-svelte": "^2.46.1", - "globals": "^15.14.0", - "prettier": "^3.4.2", - "prettier-plugin-svelte": "^3.3.3", - "svelte": "^5.0.0", - "svelte-check": "^4.0.0", - "tailwindcss": "^4.0.0", - "typescript": "^5.8.2", - "typescript-eslint": "^8.20.0", - "vite": "^6.2.0", - "vite-plugin-top-level-await": "^1.4.4", - "vite-plugin-wasm": "^3.4.1" - } -} \ No newline at end of file + "name": "piston-train-toy", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "prepare": "svelte-kit sync || echo ''", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", + "lint": "eslint . && prettier --check .", + "format": "prettier --write ." + }, + "dependencies": { + "@piston-ml/piston-web": "workspace:^", + "chart.js": "^4.4.7", + "mathjs": "^14.2.1" + }, + "devDependencies": { + "@eslint/compat": "^1.2.5", + "@eslint/js": "^9.18.0", + "@sveltejs/adapter-static": "^3.0.8", + "@sveltejs/kit": "^2.16.0", + "@sveltejs/vite-plugin-svelte": "^5.0.0", + "@tailwindcss/vite": "^4.0.0", + "@webgpu/types": "^0.1.60", + "eslint": "^9.18.0", + "eslint-config-prettier": "^10.0.1", + "eslint-plugin-svelte": "^2.46.1", + "globals": "^15.14.0", + "prettier": "^3.4.2", + "prettier-plugin-svelte": "^3.3.3", + "svelte": "^5.0.0", + "svelte-check": "^4.0.0", + "tailwindcss": "^4.0.0", + "typescript": "^5.8.2", + "typescript-eslint": "^8.20.0", + "vite": "^6.2.0", + "vite-plugin-top-level-await": "^1.4.4", + "vite-plugin-wasm": "^3.4.1" + } +} From 317c791855f296a6bb67c7599e1d46464326906b Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 26 Oct 2025 16:56:33 -0600 Subject: [PATCH 519/590] Remove topLevelAwait from vite config --- examples/piston-train-toy/vite.config.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/piston-train-toy/vite.config.ts b/examples/piston-train-toy/vite.config.ts index 8ce02c6f..2a2eb0ca 100644 --- a/examples/piston-train-toy/vite.config.ts +++ b/examples/piston-train-toy/vite.config.ts @@ -2,7 +2,6 @@ import tailwindcss from '@tailwindcss/vite'; import { sveltekit } from '@sveltejs/kit/vite'; import { defineConfig } from 'vite'; import wasm from 'vite-plugin-wasm'; -import topLevelAwait from 'vite-plugin-top-level-await'; import { fileURLToPath } from 'url'; import path from 'path'; @@ -10,10 +9,10 @@ import path from 'path'; const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..'); export default defineConfig({ - plugins: [sveltekit(), tailwindcss(), wasm(), topLevelAwait()], + plugins: [sveltekit(), tailwindcss(), wasm()], worker: { format: 'es', - plugins: () => [wasm(), topLevelAwait()] + plugins: () => [wasm()] }, esbuild: { supported: { From dd43b4ae874d05debebdbc03c72bc5e2c52bc97e Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 26 Oct 2025 16:57:20 -0600 Subject: [PATCH 520/590] Sort vite config imports --- examples/piston-train-toy/vite.config.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/piston-train-toy/vite.config.ts b/examples/piston-train-toy/vite.config.ts index 2a2eb0ca..d0b1d574 100644 --- a/examples/piston-train-toy/vite.config.ts +++ b/examples/piston-train-toy/vite.config.ts @@ -1,9 +1,9 @@ -import tailwindcss from '@tailwindcss/vite'; import { sveltekit } from '@sveltejs/kit/vite'; +import tailwindcss from '@tailwindcss/vite'; +import path from 'path'; +import { fileURLToPath } from 'url'; import { defineConfig } from 'vite'; import wasm from 'vite-plugin-wasm'; -import { fileURLToPath } from 'url'; -import path from 'path'; // Get the project root directory const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..'); From cafadd0271e4007084a17d8c293dd436a2d7eaaf Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 26 Oct 2025 16:59:25 -0600 Subject: [PATCH 521/590] vite config formatting --- examples/piston-train-toy/vite.config.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/examples/piston-train-toy/vite.config.ts b/examples/piston-train-toy/vite.config.ts index d0b1d574..fc67d508 100644 --- a/examples/piston-train-toy/vite.config.ts +++ b/examples/piston-train-toy/vite.config.ts @@ -12,12 +12,10 @@ export default defineConfig({ plugins: [sveltekit(), tailwindcss(), wasm()], worker: { format: 'es', - plugins: () => [wasm()] + plugins: () => [wasm(), sveltekit()] }, esbuild: { - supported: { - 'top-level-await': true - } + supported: { 'top-level-await': true } }, server: { fs: { From b019c8dd3a0c192b8d390c4cff38798d3a3163fb Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 26 Oct 2025 16:59:45 -0600 Subject: [PATCH 522/590] Use function mode vite config --- examples/piston-train-toy/vite.config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/piston-train-toy/vite.config.ts b/examples/piston-train-toy/vite.config.ts index fc67d508..44680312 100644 --- a/examples/piston-train-toy/vite.config.ts +++ b/examples/piston-train-toy/vite.config.ts @@ -8,7 +8,7 @@ import wasm from 'vite-plugin-wasm'; // Get the project root directory const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..'); -export default defineConfig({ +export default defineConfig((_) => ({ plugins: [sveltekit(), tailwindcss(), wasm()], worker: { format: 'es', @@ -30,4 +30,4 @@ export default defineConfig({ ] } } -}); +})); From 14004b0631dfae52426353bf938a191194b3cf21 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 26 Oct 2025 16:59:58 -0600 Subject: [PATCH 523/590] Reorder vite config plugins --- examples/piston-train-toy/vite.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/piston-train-toy/vite.config.ts b/examples/piston-train-toy/vite.config.ts index 44680312..9e9e4b01 100644 --- a/examples/piston-train-toy/vite.config.ts +++ b/examples/piston-train-toy/vite.config.ts @@ -9,7 +9,7 @@ import wasm from 'vite-plugin-wasm'; const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..'); export default defineConfig((_) => ({ - plugins: [sveltekit(), tailwindcss(), wasm()], + plugins: [tailwindcss(), sveltekit(), wasm()], worker: { format: 'es', plugins: () => [wasm(), sveltekit()] From 6b40a20a92950a740cdb21d6952903f1d8d26743 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 26 Oct 2025 17:11:04 -0600 Subject: [PATCH 524/590] Move to eslint defineConfig --- examples/piston-train-toy/eslint.config.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/piston-train-toy/eslint.config.js b/examples/piston-train-toy/eslint.config.js index 85d7229a..c1f4bceb 100644 --- a/examples/piston-train-toy/eslint.config.js +++ b/examples/piston-train-toy/eslint.config.js @@ -2,12 +2,13 @@ import prettier from 'eslint-config-prettier'; import js from '@eslint/js'; import { includeIgnoreFile } from '@eslint/compat'; import svelte from 'eslint-plugin-svelte'; +import { defineConfig } from 'eslint/config'; import globals from 'globals'; import { fileURLToPath } from 'node:url'; import ts from 'typescript-eslint'; const gitignorePath = fileURLToPath(new URL('./.gitignore', import.meta.url)); -export default ts.config( +export default defineConfig( includeIgnoreFile(gitignorePath), js.configs.recommended, ...ts.configs.recommended, From 5df2400bf8935ef18bb209d8f7717512d535ed9d Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 26 Oct 2025 17:11:55 -0600 Subject: [PATCH 525/590] Add other svelte filetypes to eslint typescript --- examples/piston-train-toy/eslint.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/piston-train-toy/eslint.config.js b/examples/piston-train-toy/eslint.config.js index c1f4bceb..6b6de1f3 100644 --- a/examples/piston-train-toy/eslint.config.js +++ b/examples/piston-train-toy/eslint.config.js @@ -24,7 +24,7 @@ export default defineConfig( } }, { - files: ['**/*.svelte'], + files: ['**/*.svelte', '**/*.svelte.ts', '**/*.ts'], languageOptions: { parserOptions: { From 39cfcb198a009e5a4d238412dd3a2132638738ab Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 26 Oct 2025 17:14:42 -0600 Subject: [PATCH 526/590] Add some typescript-eslint rules --- examples/piston-train-toy/eslint.config.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/piston-train-toy/eslint.config.js b/examples/piston-train-toy/eslint.config.js index 6b6de1f3..22d7dcea 100644 --- a/examples/piston-train-toy/eslint.config.js +++ b/examples/piston-train-toy/eslint.config.js @@ -35,6 +35,8 @@ export default defineConfig( { rules: { 'no-unused-vars': 'off', + '@typescript-eslint/ban-types': 'off', + '@typescript-eslint/no-explicit-any': 'warn', '@typescript-eslint/no-unused-vars': [ 'warn', { @@ -42,7 +44,8 @@ export default defineConfig( varsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_' } - ] + ], + '@typescript-eslint/no-var-requires': 'off' } } ); From a05b503358227bfe712d0a46ced480519c4035ea Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 26 Oct 2025 17:21:07 -0600 Subject: [PATCH 527/590] Add perfectionist linter to example --- examples/piston-train-toy/eslint.config.js | 34 +++++++++++++++++++--- examples/piston-train-toy/package.json | 1 + 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/examples/piston-train-toy/eslint.config.js b/examples/piston-train-toy/eslint.config.js index 22d7dcea..2318336b 100644 --- a/examples/piston-train-toy/eslint.config.js +++ b/examples/piston-train-toy/eslint.config.js @@ -1,6 +1,7 @@ -import prettier from 'eslint-config-prettier'; -import js from '@eslint/js'; import { includeIgnoreFile } from '@eslint/compat'; +import js from '@eslint/js'; +import prettier from 'eslint-config-prettier'; +import perfectionist from 'eslint-plugin-perfectionist'; import svelte from 'eslint-plugin-svelte'; import { defineConfig } from 'eslint/config'; import globals from 'globals'; @@ -13,7 +14,6 @@ export default defineConfig( js.configs.recommended, ...ts.configs.recommended, ...svelte.configs['flat/recommended'], - prettier, ...svelte.configs['flat/prettier'], { languageOptions: { @@ -33,6 +33,10 @@ export default defineConfig( } }, { + plugins: { + prettier, + perfectionist + }, rules: { 'no-unused-vars': 'off', '@typescript-eslint/ban-types': 'off', @@ -45,7 +49,29 @@ export default defineConfig( caughtErrorsIgnorePattern: '^_' } ], - '@typescript-eslint/no-var-requires': 'off' + '@typescript-eslint/no-var-requires': 'off', + 'perfectionist/sort-imports': [ + 'error', + { + type: 'natural', + order: 'asc', + groups: [ + 'type', + ['builtin', 'external'], + 'internal-type', + 'internal', + ['parent-type', 'sibling-type', 'index-type'], + ['parent', 'sibling', 'index'], + 'side-effect', + 'style', + 'object', + 'unknown' + ] + } + ], + 'perfectionist/sort-exports': ['error', { type: 'natural' }], + 'perfectionist/sort-named-exports': ['error', { type: 'natural' }], + 'perfectionist/sort-named-imports': ['error', { type: 'natural' }] } } ); diff --git a/examples/piston-train-toy/package.json b/examples/piston-train-toy/package.json index 76a5d1cc..7daac633 100644 --- a/examples/piston-train-toy/package.json +++ b/examples/piston-train-toy/package.json @@ -28,6 +28,7 @@ "@webgpu/types": "^0.1.60", "eslint": "^9.18.0", "eslint-config-prettier": "^10.0.1", + "eslint-plugin-perfectionist": "^4.15.1", "eslint-plugin-svelte": "^2.46.1", "globals": "^15.14.0", "prettier": "^3.4.2", From 4c303ea109920c0d9ac46417543523ee14494eb3 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 26 Oct 2025 17:57:24 -0600 Subject: [PATCH 528/590] Add config schema --- .../src/lib/workspace/config.ts | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 examples/piston-train-toy/src/lib/workspace/config.ts diff --git a/examples/piston-train-toy/src/lib/workspace/config.ts b/examples/piston-train-toy/src/lib/workspace/config.ts new file mode 100644 index 00000000..db27e424 --- /dev/null +++ b/examples/piston-train-toy/src/lib/workspace/config.ts @@ -0,0 +1,119 @@ +export type { + AdditionConfig, + ModularAdditionConfig, + RepeatConfig, + SlapjackConfig, + SortConfig, + TwoSumConfig, + ZerosConfig +} from '../train/data/toy/config'; + +import type { DATASET_CONFIG_DEFAULTS } from '$lib/train/data'; + +import type { TOY_DATASET_CONFIG_DEFAULTS } from '../train/data/toy/config'; + +export interface DropoutConfig { + present: boolean; + embedding: number; + transformer: { + attention: number; + residual: number; + }; +} + +export interface TrainingConfig { + logSteps: number; + batchSize: number; + dropout: DropoutConfig; + sharedObjectAllocation: boolean; + cachingEnabled: boolean; + inplaceSupport: boolean; +} + +export interface TransformerAttentionConfig { + present: boolean; + nKeyValueHeads: number; +} + +export interface DataConfig { + dataset: keyof typeof DATASET_CONFIG_DEFAULTS; + trainOnPrompt: boolean; + datasets: typeof TOY_DATASET_CONFIG_DEFAULTS; + maskRatio: number; + specialTokens: { + includeEos: boolean; + }; +} + +export interface PositionEncodingConfig { + present: boolean; +} + +export type LayerNormPosition = 'pre' | 'post'; +export type NormalizationType = 'layernorm' | 'rmsnorm'; + +export interface LayerNormalizationConfig { + type: NormalizationType; + eps: number; + transformer: { + present: boolean; + position: LayerNormPosition; + }; +} + +export type Activation = 'relu' | 'relu2' | 'gelu' | 'silu' | 'sigmoid' | 'swiglu' | 'tanh'; + +export interface MLPConfig { + present: boolean; + activation: Activation; + hiddenExpansionFactor: number; +} + +export type ModelType = 'decoder' | 'encoder' | 'encoder-decoder'; + +export interface TransformerConfig { + headDim: number; + attention: TransformerAttentionConfig; + positionalEncoding: PositionEncodingConfig; + mlp: MLPConfig; +} + +export interface ModelConfig { + topology: ModelType; + layers: number; + encoderDecoder: { + decoderLayers: number; + encoderLayers: number; + }; + layerNormalization: LayerNormalizationConfig; + transformer: TransformerConfig; +} + +export interface OptimizerConfig { + type: 'AdamW' | 'Adam' | 'SGD'; + lr: number; + weightDecay: { + present: boolean; + value: number; + useWeightDecayGroups: boolean; + }; + adam: { + beta1: number; + beta2: number; + eps: number; + amsgrad: boolean; + }; + sgd: { + dampening: number; + momentum: number; + nesterov: boolean; + }; +} + +export interface Config { + version: number; + training: TrainingConfig; + data: DataConfig; + model: ModelConfig; + optimizer: OptimizerConfig; +} From 550570dbe299c50fdab19291089ceef8ba6a7912 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 26 Oct 2025 14:03:01 -0600 Subject: [PATCH 529/590] Add toy datasets --- .../src/lib/train/data/collate.ts | 28 ++ .../src/lib/train/data/index.ts | 14 + .../src/lib/train/data/pipeline.ts | 31 ++ .../src/lib/train/data/toy/addition.ts | 85 +++++ .../src/lib/train/data/toy/collate.ts | 335 ++++++++++++++++++ .../src/lib/train/data/toy/config.ts | 116 ++++++ .../src/lib/train/data/toy/copyMemory.ts | 137 +++++++ .../src/lib/train/data/toy/dataset.ts | 217 ++++++++++++ .../src/lib/train/data/toy/dyck.ts | 157 ++++++++ .../src/lib/train/data/toy/elman.ts | 88 +++++ .../src/lib/train/data/toy/index.ts | 139 ++++++++ .../src/lib/train/data/toy/markedAddition.ts | 127 +++++++ .../src/lib/train/data/toy/modularAddition.ts | 95 +++++ .../src/lib/train/data/toy/parity.ts | 96 +++++ .../src/lib/train/data/toy/random.ts | 82 +++++ .../src/lib/train/data/toy/repeat.ts | 110 ++++++ .../src/lib/train/data/toy/reverse.ts | 111 ++++++ .../src/lib/train/data/toy/slapjack.ts | 113 ++++++ .../src/lib/train/data/toy/sort.ts | 109 ++++++ .../src/lib/train/data/toy/temporalOrder.ts | 115 ++++++ .../src/lib/train/data/toy/twoSum.ts | 178 ++++++++++ .../src/lib/train/data/toy/types.ts | 6 + .../src/lib/train/data/toy/zeros.ts | 55 +++ 23 files changed, 2544 insertions(+) create mode 100644 examples/piston-train-toy/src/lib/train/data/collate.ts create mode 100644 examples/piston-train-toy/src/lib/train/data/index.ts create mode 100644 examples/piston-train-toy/src/lib/train/data/pipeline.ts create mode 100644 examples/piston-train-toy/src/lib/train/data/toy/addition.ts create mode 100644 examples/piston-train-toy/src/lib/train/data/toy/collate.ts create mode 100644 examples/piston-train-toy/src/lib/train/data/toy/config.ts create mode 100644 examples/piston-train-toy/src/lib/train/data/toy/copyMemory.ts create mode 100644 examples/piston-train-toy/src/lib/train/data/toy/dataset.ts create mode 100644 examples/piston-train-toy/src/lib/train/data/toy/dyck.ts create mode 100644 examples/piston-train-toy/src/lib/train/data/toy/elman.ts create mode 100644 examples/piston-train-toy/src/lib/train/data/toy/index.ts create mode 100644 examples/piston-train-toy/src/lib/train/data/toy/markedAddition.ts create mode 100644 examples/piston-train-toy/src/lib/train/data/toy/modularAddition.ts create mode 100644 examples/piston-train-toy/src/lib/train/data/toy/parity.ts create mode 100644 examples/piston-train-toy/src/lib/train/data/toy/random.ts create mode 100644 examples/piston-train-toy/src/lib/train/data/toy/repeat.ts create mode 100644 examples/piston-train-toy/src/lib/train/data/toy/reverse.ts create mode 100644 examples/piston-train-toy/src/lib/train/data/toy/slapjack.ts create mode 100644 examples/piston-train-toy/src/lib/train/data/toy/sort.ts create mode 100644 examples/piston-train-toy/src/lib/train/data/toy/temporalOrder.ts create mode 100644 examples/piston-train-toy/src/lib/train/data/toy/twoSum.ts create mode 100644 examples/piston-train-toy/src/lib/train/data/toy/types.ts create mode 100644 examples/piston-train-toy/src/lib/train/data/toy/zeros.ts diff --git a/examples/piston-train-toy/src/lib/train/data/collate.ts b/examples/piston-train-toy/src/lib/train/data/collate.ts new file mode 100644 index 00000000..331d904b --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/data/collate.ts @@ -0,0 +1,28 @@ +import type { ToyCollateFnType } from '$lib/train/types'; + +import { gpu, int32, tensor, Tensor } from '@piston-ml/piston-web'; + +import type { CollatedRawSequence, ToyDatasetLike, ToySequence } from './toy/dataset'; + +export type CollateWrapFunction = (sequences: number[][]) => T; + +export function tensorWrap(sequences: number[][]): Tensor { + return tensor(sequences, { dtype: int32, device: gpu }); +} + +// Utility functions for accessing raw format data +export function getCollatedSampleData( + dataset: ToyDatasetLike, + collateFn: ToyCollateFnType, + sampleCount: number = 4 +): { + samples: ToySequence[] | number[][]; + collated: CollatedRawSequence[]; +} { + // Generate sample sequences + const iterator = dataset[Symbol.iterator](); + const samples = Array.from({ length: sampleCount }, () => iterator.next().value); + const collated = (collateFn as ToyCollateFnType)(samples as ToySequence[]).raw; + + return { samples, collated }; +} diff --git a/examples/piston-train-toy/src/lib/train/data/index.ts b/examples/piston-train-toy/src/lib/train/data/index.ts new file mode 100644 index 00000000..02e3780d --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/data/index.ts @@ -0,0 +1,14 @@ +import { TOY_DATASET_CONFIG_DEFAULTS, TOY_DATASET_CONFIG_METADATA } from './toy/config'; + +export const DATASET_CONFIG_METADATA = { + ...Object.fromEntries( + Object.entries(TOY_DATASET_CONFIG_METADATA).map(([name, meta]) => [ + name, + { ...meta, type: 'toy' } + ]) + ) +} as const; + +export const DATASET_CONFIG_DEFAULTS = TOY_DATASET_CONFIG_DEFAULTS; + +export * from './collate'; diff --git a/examples/piston-train-toy/src/lib/train/data/pipeline.ts b/examples/piston-train-toy/src/lib/train/data/pipeline.ts new file mode 100644 index 00000000..aa8b972f --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/data/pipeline.ts @@ -0,0 +1,31 @@ +import type { Config } from '$lib/workspace/config'; +import type { Tensor } from '@piston-ml/piston-web'; +import type { Random } from 'random-js'; + +import type { ToyDatasetLike } from './toy/dataset'; + +import { tensorWrap } from '.'; +import { createDataloader } from '../utils/model'; +import { buildToyDataset } from './toy'; + +export type BuiltData = { + dataset: ToyDatasetLike; + iterator: AsyncIterator<{ + readonly tensors: Tensor[]; + readonly samples?: unknown[]; + }>; +}; + +export async function buildDataPipeline( + config: Config, + generator: Random, + trainDatasetOverride?: ToyDatasetLike +): Promise { + const trainDataset = trainDatasetOverride ?? buildToyDataset(config, generator); + const [trainDataloader] = createDataloader(config, trainDataset, generator, tensorWrap); + + return { + dataset: trainDataset, + iterator: trainDataloader[Symbol.asyncIterator]() + }; +} diff --git a/examples/piston-train-toy/src/lib/train/data/toy/addition.ts b/examples/piston-train-toy/src/lib/train/data/toy/addition.ts new file mode 100644 index 00000000..785807c1 --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/data/toy/addition.ts @@ -0,0 +1,85 @@ +import ToyDataset, { type ToySequence } from './dataset'; + +export interface AdditionConfig { + maxNumber: number; + includeExpressionTokens: boolean; +} + +export const ADDITION_CONFIG_METADATA = { + name: 'Addition', + description: 'Add two numbers.', + supportsModelTypes: ['encoder', 'encoder-decoder', 'decoder'], + parameters: { + maxNumber: { + name: 'Max Number', + description: 'Maximum value for each addend', + type: 'number' as const, + min: 5, + max: 100, + default: 20 + }, + includeExpressionTokens: { + name: 'Include Expression Tokens (+, =)', + type: 'boolean' as const, + default: true + } + } +} as const; + +export const ADDITION_CONFIG_DEFAULTS: AdditionConfig = { + maxNumber: ADDITION_CONFIG_METADATA.parameters.maxNumber.default, + includeExpressionTokens: ADDITION_CONFIG_METADATA.parameters.includeExpressionTokens.default +}; + +export class AdditionDataset extends ToyDataset { + /** + * Tokenizer for addition. + * - tokens 0 .. maxNum represent the numbers 0 … maxNum + * (When converting to a string, token id i <= maxNum becomes `<${i}>` with zero-padding.) + * - token maxNum+1 -> "+" + * - token maxNum+2 -> "=" + */ + protected buildVocab(): string[] { + const { maxNumber, includeExpressionTokens } = this.config; + const vocab: string[] = []; + const padWidth = maxNumber.toString().length; + // 1. Add tokens for numbers + vocab.push( + ...Array.from({ length: maxNumber + 1 }, (_, i) => i.toString().padStart(padWidth, '0')) + ); + // 2. Add tokens for + and = + if (includeExpressionTokens) { + vocab.push('+', '='); + } + return vocab; + } + + /** + * Addition: generate two addends (in [0, maxNum]) chosen so that their sum <= maxNum. + * Example: <15>+<03>=<18> + */ + public generateSequence(): ToySequence { + const { maxNumber, includeExpressionTokens } = this.config; + + // 1. Pick numbers and compute sum. Include maxNumber in the range. + const sum = this.generator.integer(0, maxNumber); + const num1 = sum > 0 ? this.generator.integer(0, sum - 1) : 0; + const num2 = sum - num1; + + // 2. Convert to token IDs: + // - 0..maxNum are numbers + // - maxNum+1 is '+' + // - maxNum+2 is '=' + let prompt; + if (includeExpressionTokens) { + prompt = [num1, maxNumber + 1, num2, maxNumber + 2]; + } else { + prompt = [num1, num2]; + } + + // 3. Target is a single token corresponding to the sum + const target = [sum]; + + return { prompt, target }; + } +} diff --git a/examples/piston-train-toy/src/lib/train/data/toy/collate.ts b/examples/piston-train-toy/src/lib/train/data/toy/collate.ts new file mode 100644 index 00000000..95679aff --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/data/toy/collate.ts @@ -0,0 +1,335 @@ +import type { Tensor } from '@piston-ml/piston-web'; + +import { MersenneTwister19937, Random } from 'random-js'; + +import { type CollateWrapFunction, tensorWrap } from '../collate'; +import { + deriveToySampleSeed, + type ToyAutoregressiveBatch, + type ToyBidirectionalBatch, + type ToyDatasetLike, + type ToyEncoderDecoderBatch, + type ToySequence +} from './dataset'; + +// Helper function to add special tokens to sequences for rawData +function withSpecials( + body: number[], + { bosId, eosId }: { bosId: number | null; eosId: number | null } +): number[] { + const seq = [...body]; + if (eosId !== null) seq.push(eosId); + return bosId !== null ? [bosId, ...seq] : seq; +} + +interface AutoregressiveCollateOptions { + ignorePrompt?: boolean; + wrapFunction?: CollateWrapFunction | null; +} + +interface BidirectionalCollateOptions { + maskPrompt?: boolean; + maskRatio?: number; + generator?: Random; + wrapFunction?: CollateWrapFunction | null; +} + +export interface EncoderDecoderCollateOptions { + wrapFunction?: CollateWrapFunction | null; +} + +export function toyDatasetAutoregressiveCollate( + batch: ToySequence[], + dataset: ToyDatasetLike, + options: AutoregressiveCollateOptions = {} +): ToyAutoregressiveBatch { + const { ignorePrompt = false, wrapFunction = tensorWrap } = options; + + const sequencesData = batch.map(({ prompt, target: completion, mask }) => { + // Build full sequence, potentially adding BOS at start and EOS at end + const fullSequence: number[] = []; + + // Add BOS if available + if (dataset.bosId !== null) { + fullSequence.push(dataset.bosId); + } + + // Add prompt and completion + fullSequence.push(...(prompt ?? []), ...completion); + + // Add EOS if available + if (dataset.eosId !== null) { + fullSequence.push(dataset.eosId); + } + + const input = fullSequence.slice(0, -1); + const target = fullSequence.slice(1); + + // Mask prompt tokens in target if ignorePrompt is true + // Note: target has already been shifted by slice(1), so no bosOffset needed + const promptLength = prompt?.length ?? 0; + if (ignorePrompt && promptLength > 0) { + target.fill(-100, 0, promptLength); + } + + // Apply dataset-provided mask to target positions that correspond to completion tokens + if (mask && mask.length > 0) { + for (let i = 0; i < completion.length; i++) { + if (mask[i] === false) { + const targetIndex = promptLength + i; + if (targetIndex >= 0 && targetIndex < target.length) { + target[targetIndex] = -100; + } + } + } + } + + // Create visible completion with mask tokens for rawData + const visibleCompletion = [...completion]; + if (mask && dataset.maskId !== null) { + for (let i = 0; i < visibleCompletion.length; i++) { + if (mask[i] === false) { + visibleCompletion[i] = dataset.maskId; + } + } + } + + return { + input, + target, + rawData: { + fullSequence, + prompt: withSpecials(prompt ?? [], { bosId: dataset.bosId, eosId: null }), + target: withSpecials(visibleCompletion, { bosId: null, eosId: dataset.eosId }), + ignored: target.map((t) => t === -100) + } + }; + }); + + const inputSequences = sequencesData.map(({ input }) => input); + const targetSequences = sequencesData.map(({ target }) => target); + + const input = wrapFunction ? wrapFunction(inputSequences) : inputSequences; + const target = wrapFunction ? wrapFunction(targetSequences) : targetSequences; + + return { + tensors: [input as T, target as T], + raw: sequencesData.map(({ rawData }) => rawData), + samples: batch + }; +} + +export function toyDatasetBidirectionalCollate( + batch: ToySequence[], + dataset: ToyDatasetLike, + options: BidirectionalCollateOptions = {} +): ToyBidirectionalBatch { + const { maskPrompt = false, maskRatio = 0.15, generator, wrapFunction = tensorWrap } = options; + + if (dataset.maskId === null) { + throw new Error( + 'Encoder-only collation requires a mask token, but none was configured in the dataset' + ); + } + + const maskToken = dataset.maskId; + + const sequencesData = batch.map((seq) => { + const { prompt, target, mask, absoluteIndex } = seq; + const fullSequence: number[] = [...(prompt ?? []), ...target]; + // If a global generator is not provided, derive a per-sample RNG from baseSeed and absoluteIndex + const rng = generator + ? generator + : new Random( + MersenneTwister19937.seed( + deriveToySampleSeed( + dataset.baseSeed, + dataset.datasetName, + absoluteIndex ?? 0, + 'toy-mask' + ) + ) + ); + + const maskedSequence = [...fullSequence]; + const labels = new Array(fullSequence.length).fill(-100); + + // Determine which tokens are eligible for masking + const promptLength = prompt?.length ?? 0; + + const eligibleForMasking = fullSequence.map((_, index) => { + if (maskPrompt) { + // Allow masking across both prompt and target (no specials in bidirectional) + return true; + } else { + // Only target tokens eligible (after prompt) + const targetStart = promptLength; + const targetEnd = fullSequence.length; + if (index < targetStart || index >= targetEnd) return false; + // If dataset provided a mask array, restrict eligible positions to those marked true + if (mask && mask[index - targetStart] === false) return false; + return true; + } + }); + + // Apply random masking and track which positions got masked + const maskedPositions: boolean[] = new Array(fullSequence.length).fill(false); + eligibleForMasking.forEach((eligible, index) => { + if (eligible && rng.real(0, 1) < maskRatio) { + maskedSequence[index] = maskToken; + maskedPositions[index] = true; + labels[index] = fullSequence[index]; // compute loss against original token + } + }); + + // Ensure at least one token is masked per example + if (!maskedPositions.some((v) => v)) { + // Prefer among eligible positions + const candidates: number[] = eligibleForMasking + .map((eligible, idx) => (eligible ? idx : -1)) + .filter((idx) => idx >= 0); + if (candidates.length > 0) { + const forcedIdx = candidates[rng.integer(0, candidates.length - 1)]; + maskedSequence[forcedIdx] = maskToken; + maskedPositions[forcedIdx] = true; + labels[forcedIdx] = fullSequence[forcedIdx]; + } else { + throw new Error('No tokens eligible for masking'); + } + } + + return { + maskedSequence, + labels, + rawData: { + fullSequence: maskedSequence, + prompt: [...(prompt ?? [])], + target: [...target], + ignored: labels.map((l) => l === -100) + } + }; + }); + + const inputSequences = sequencesData.map(({ maskedSequence }) => maskedSequence); + const labelSequences = sequencesData.map(({ labels }) => labels); + const attentionMaskSequences = sequencesData.map(({ maskedSequence }) => + maskedSequence.map(() => 1) + ); + + const input = wrapFunction ? wrapFunction(inputSequences) : inputSequences; + const labels = wrapFunction ? wrapFunction(labelSequences) : labelSequences; + const attentionMask = wrapFunction + ? wrapFunction(attentionMaskSequences) + : attentionMaskSequences; + + return { + tensors: [input as T, labels as T, attentionMask as T], + raw: sequencesData.map(({ rawData }) => rawData), + samples: batch + }; +} + +export function toyDatasetEncoderDecoderCollate( + batch: ToySequence[], + dataset: ToyDatasetLike, + options: EncoderDecoderCollateOptions = {} +): ToyEncoderDecoderBatch { + if (dataset.bosId === null) { + throw new Error( + 'Encoder-decoder collation requires a BOS token, but none was configured in the dataset' + ); + } + + const encoderData = batch.map(({ prompt }) => { + // Build encoder input WITHOUT BOS/EOS + const encoderSequence: number[] = [...(prompt ?? [])]; + + return { + encoderSequence, + rawData: { + fullSequence: encoderSequence, + prompt: encoderSequence, // encoder sequence is the prompt without specials + target: [], // No target for encoder + ignored: [] + } + }; + }); + + const decoderData = batch.map(({ target, mask }) => { + // Apply mask to target array first + let maskedTarget = [...target]; + if (mask) { + maskedTarget = maskedTarget.map((token, i) => (mask[i] === false ? -100 : token)); + } + + // Build decoder target with EOS if available + const decoderTarget: number[] = [...maskedTarget]; + if (dataset.eosId !== null) { + decoderTarget.push(dataset.eosId); + } + + // Decoder input: BOS + target (for teacher forcing) + // Note: dataset.bosId is guaranteed to be non-null because we checked earlier + // If EOS is enabled, we feed BOS + full target so model predicts target then EOS. + // If EOS is disabled, feed BOS + target[:-1] so input/target lengths match and + // the loss is computed only over generated tokens. + const decoderInput: number[] = + dataset.eosId !== null + ? [dataset.bosId!, ...target] + : [dataset.bosId!, ...target.slice(0, -1)]; + + // Create visible target with mask tokens for rawData + const visibleTarget = [...target]; + if (mask && dataset.maskId !== null) { + for (let i = 0; i < visibleTarget.length; i++) { + if (mask[i] === false) { + visibleTarget[i] = dataset.maskId; + } + } + } + + return { + decoderInput, + decoderTarget, + rawData: { + fullSequence: decoderInput, + prompt: [], // decoder has no prompt concept + target: withSpecials(visibleTarget, { bosId: null, eosId: dataset.eosId }), + ignored: decoderTarget.map((t) => t === -100) + } + }; + }); + + const encoderSequences = encoderData.map(({ encoderSequence }) => encoderSequence); + const decoderInputSequences = decoderData.map(({ decoderInput }) => decoderInput); + const decoderTargetSequences = decoderData.map(({ decoderTarget }) => decoderTarget); + + const encoderInput = options.wrapFunction + ? options.wrapFunction(encoderSequences) + : encoderSequences; + const decoderInput = options.wrapFunction + ? options.wrapFunction(decoderInputSequences) + : decoderInputSequences; + const decoderTarget = options.wrapFunction + ? options.wrapFunction(decoderTargetSequences) + : decoderTargetSequences; + + // Combine encoder and decoder raw data into single sequences + const combinedRaw = batch.map((_, index) => { + const encoderRaw = encoderData[index].rawData; + const decoderRaw = decoderData[index].rawData; + + return { + fullSequence: [...encoderRaw.fullSequence, ...decoderRaw.fullSequence], + prompt: encoderRaw.prompt, // encoder provides the prompt + target: decoderRaw.target, // decoder provides the target + ignored: [...encoderRaw.ignored, ...decoderRaw.ignored] + }; + }); + + return { + tensors: [encoderInput as T, decoderInput as T, decoderTarget as T], + raw: combinedRaw, + samples: batch + }; +} diff --git a/examples/piston-train-toy/src/lib/train/data/toy/config.ts b/examples/piston-train-toy/src/lib/train/data/toy/config.ts new file mode 100644 index 00000000..6f455682 --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/data/toy/config.ts @@ -0,0 +1,116 @@ +// Shared configuration parameter interface +export interface ConfigParameter { + name: string; + description?: string; + type: 'number' | 'boolean'; + min?: number; + max?: number; + step?: number; + default: number | boolean; +} + +// Import all dataset configurations +import { ADDITION_CONFIG_DEFAULTS, ADDITION_CONFIG_METADATA } from './addition'; +import { COPY_MEMORY_CONFIG_DEFAULTS, COPY_MEMORY_CONFIG_METADATA } from './copyMemory'; +import { DYCK_CONFIG_DEFAULTS, DYCK_CONFIG_METADATA } from './dyck'; +import { ELMAN_CONFIG_DEFAULTS, ELMAN_CONFIG_METADATA } from './elman'; +import { MARKED_ADDITION_CONFIG_DEFAULTS, MARKED_ADDITION_CONFIG_METADATA } from './markedAddition'; +import { + MODULAR_ADDITION_CONFIG_DEFAULTS, + MODULAR_ADDITION_CONFIG_METADATA +} from './modularAddition'; +import { PARITY_CONFIG_DEFAULTS, PARITY_CONFIG_METADATA } from './parity'; +import { RANDOM_CONFIG_DEFAULTS, RANDOM_CONFIG_METADATA } from './random'; +import { REPEAT_CONFIG_DEFAULTS, REPEAT_CONFIG_METADATA } from './repeat'; +import { REVERSE_CONFIG_DEFAULTS, REVERSE_CONFIG_METADATA } from './reverse'; +import { SLAPJACK_CONFIG_DEFAULTS, SLAPJACK_CONFIG_METADATA } from './slapjack'; +import { SORT_CONFIG_DEFAULTS, SORT_CONFIG_METADATA } from './sort'; +import { TEMPORAL_ORDER_CONFIG_DEFAULTS, TEMPORAL_ORDER_CONFIG_METADATA } from './temporalOrder'; +import { TWO_SUM_CONFIG_DEFAULTS, TWO_SUM_CONFIG_METADATA } from './twoSum'; +import { ZEROS_CONFIG_DEFAULTS, ZEROS_CONFIG_METADATA } from './zeros'; + +// Re-export types and constants +export type { AdditionConfig } from './addition'; +export { ADDITION_CONFIG_DEFAULTS, ADDITION_CONFIG_METADATA } from './addition'; + +export type { CopyMemoryConfig } from './copyMemory'; +export { COPY_MEMORY_CONFIG_DEFAULTS, COPY_MEMORY_CONFIG_METADATA } from './copyMemory'; + +export type { DyckConfig } from './dyck'; +export { DYCK_CONFIG_DEFAULTS, DYCK_CONFIG_METADATA } from './dyck'; + +export { ELMAN_CONFIG_DEFAULTS, ELMAN_CONFIG_METADATA } from './elman'; + +export type { MarkedAdditionConfig } from './markedAddition'; +export { MARKED_ADDITION_CONFIG_DEFAULTS, MARKED_ADDITION_CONFIG_METADATA } from './markedAddition'; + +export type { ModularAdditionConfig } from './modularAddition'; +export { + MODULAR_ADDITION_CONFIG_DEFAULTS, + MODULAR_ADDITION_CONFIG_METADATA +} from './modularAddition'; + +export type { ParityConfig } from './parity'; +export { PARITY_CONFIG_DEFAULTS, PARITY_CONFIG_METADATA } from './parity'; + +export type { RandomConfig } from './random'; +export { RANDOM_CONFIG_DEFAULTS, RANDOM_CONFIG_METADATA } from './random'; + +export type { RepeatConfig } from './repeat'; +export { REPEAT_CONFIG_DEFAULTS, REPEAT_CONFIG_METADATA } from './repeat'; + +export type { ReverseConfig } from './reverse'; +export { REVERSE_CONFIG_DEFAULTS, REVERSE_CONFIG_METADATA } from './reverse'; + +export type { SlapjackConfig } from './slapjack'; +export { SLAPJACK_CONFIG_DEFAULTS, SLAPJACK_CONFIG_METADATA } from './slapjack'; + +export type { SortConfig } from './sort'; +export { SORT_CONFIG_DEFAULTS, SORT_CONFIG_METADATA } from './sort'; + +export type { TemporalOrderConfig } from './temporalOrder'; +export { TEMPORAL_ORDER_CONFIG_DEFAULTS, TEMPORAL_ORDER_CONFIG_METADATA } from './temporalOrder'; + +export type { TwoSumConfig } from './twoSum'; +export { TWO_SUM_CONFIG_DEFAULTS, TWO_SUM_CONFIG_METADATA } from './twoSum'; + +export type { ZerosConfig } from './zeros'; +export { ZEROS_CONFIG_DEFAULTS, ZEROS_CONFIG_METADATA } from './zeros'; + +// Aggregate metadata for all datasets +export const TOY_DATASET_CONFIG_METADATA = { + addition: ADDITION_CONFIG_METADATA, + 'copy-memory': COPY_MEMORY_CONFIG_METADATA, + dyck: DYCK_CONFIG_METADATA, + elman: ELMAN_CONFIG_METADATA, + 'marked-addition': MARKED_ADDITION_CONFIG_METADATA, + 'modular-addition': MODULAR_ADDITION_CONFIG_METADATA, + parity: PARITY_CONFIG_METADATA, + random: RANDOM_CONFIG_METADATA, + repeat: REPEAT_CONFIG_METADATA, + reverse: REVERSE_CONFIG_METADATA, + slapjack: SLAPJACK_CONFIG_METADATA, + sort: SORT_CONFIG_METADATA, + 'temporal-order': TEMPORAL_ORDER_CONFIG_METADATA, + 'two-sum': TWO_SUM_CONFIG_METADATA, + zeros: ZEROS_CONFIG_METADATA +} as const; + +// Aggregate defaults for all datasets +export const TOY_DATASET_CONFIG_DEFAULTS = { + addition: ADDITION_CONFIG_DEFAULTS, + 'copy-memory': COPY_MEMORY_CONFIG_DEFAULTS, + dyck: DYCK_CONFIG_DEFAULTS, + elman: ELMAN_CONFIG_DEFAULTS, + 'marked-addition': MARKED_ADDITION_CONFIG_DEFAULTS, + 'modular-addition': MODULAR_ADDITION_CONFIG_DEFAULTS, + parity: PARITY_CONFIG_DEFAULTS, + random: RANDOM_CONFIG_DEFAULTS, + repeat: REPEAT_CONFIG_DEFAULTS, + reverse: REVERSE_CONFIG_DEFAULTS, + slapjack: SLAPJACK_CONFIG_DEFAULTS, + sort: SORT_CONFIG_DEFAULTS, + 'temporal-order': TEMPORAL_ORDER_CONFIG_DEFAULTS, + 'two-sum': TWO_SUM_CONFIG_DEFAULTS, + zeros: ZEROS_CONFIG_DEFAULTS +} as const; diff --git a/examples/piston-train-toy/src/lib/train/data/toy/copyMemory.ts b/examples/piston-train-toy/src/lib/train/data/toy/copyMemory.ts new file mode 100644 index 00000000..33c4b59b --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/data/toy/copyMemory.ts @@ -0,0 +1,137 @@ +import ToyDataset, { type ToySequence } from './dataset'; + +export interface CopyMemoryConfig { + prefixLength: number; // length of the subsequence to memorize (A,B,C) + distractorLength: number; // length of distractor stream between prefix and recall signal + vocabSize: number; // vocabulary for prefix and distractors (A..) + recallTokenEnabled: boolean; // include '?' token to signal recall time + includeSeparators: boolean; // optional commas between tokens +} + +export const COPY_MEMORY_CONFIG_METADATA = { + name: 'Copy Memory with Distractors', + description: 'Memorize a subsequence and reproduce it at the end after distractors.', + citations: { + entries: [ + { + name: 'Hochreiter & Schmidhuber, 1997', + url: 'https://ieeexplore.ieee.org/abstract/document/6795963' + } + ] + }, + supportsModelTypes: ['encoder', 'encoder-decoder', 'decoder'], + parameters: { + prefixLength: { + name: 'Prefix Length', + description: 'Length of subsequence to memorize', + type: 'number' as const, + min: 1, + max: 32, + step: 1, + default: 3 + }, + distractorLength: { + name: 'Distractor Length', + description: 'Number of distractor tokens before recall', + type: 'number' as const, + min: 0, + max: 512, + step: 1, + default: 12 + }, + vocabSize: { + name: 'Vocab Size', + description: 'Number of token types used for prefix/distractors', + type: 'number' as const, + min: 2, + max: 128, + step: 1, + default: 10 + }, + recallTokenEnabled: { + name: 'Include Recall Token (?)', + description: 'Append a ? token to signal recall time', + type: 'boolean' as const, + default: true + }, + includeSeparators: { + name: 'Include Separators', + description: 'Insert commas between tokens', + type: 'boolean' as const, + default: false + } + } +} as const; + +export const COPY_MEMORY_CONFIG_DEFAULTS: CopyMemoryConfig = { + prefixLength: COPY_MEMORY_CONFIG_METADATA.parameters.prefixLength.default, + distractorLength: COPY_MEMORY_CONFIG_METADATA.parameters.distractorLength.default, + vocabSize: COPY_MEMORY_CONFIG_METADATA.parameters.vocabSize.default, + recallTokenEnabled: COPY_MEMORY_CONFIG_METADATA.parameters.recallTokenEnabled.default, + includeSeparators: COPY_MEMORY_CONFIG_METADATA.parameters.includeSeparators.default +}; + +export class CopyMemoryDataset extends ToyDataset { + /** + * Vocab: + * - letters L0..L{vocabSize-1} + * - optional comma separator + * - optional recall token '?' + */ + protected buildVocab(): string[] { + const { vocabSize, recallTokenEnabled, includeSeparators } = this.config; + const padWidth = vocabSize.toString().length; + const vocab = Array.from( + { length: vocabSize + 1 }, + (_, i) => `L${i.toString().padStart(padWidth, '0')}` + ); + if (includeSeparators) vocab.push(','); + if (recallTokenEnabled) vocab.push('?'); + return vocab; + } + + /** + * Prompt layout: P0 P1 ... P{prefix-1} [distractors...] [?] + * Target: P0 P1 ... P{prefix-1} + */ + public generateSequence(): ToySequence { + const { prefixLength, distractorLength, vocabSize, recallTokenEnabled, includeSeparators } = + this.config; + const prompt: number[] = []; + + // Build prefix + const prefix: number[] = []; + for (let i = 0; i < prefixLength; i++) { + prefix.push(this.generator.integer(0, vocabSize - 1)); + } + + const comma = this.tokenizer.vocab[',']; + + // Push prefix into prompt + for (let i = 0; i < prefix.length; i++) { + prompt.push(prefix[i]); + if (includeSeparators) { + if (i < prefix.length - 1 || distractorLength > 0 || recallTokenEnabled) { + prompt.push(comma); + } + } + } + + // Add distractors + for (let i = 0; i < distractorLength; i++) { + prompt.push(this.generator.integer(0, vocabSize - 1)); + if (includeSeparators && (i < distractorLength - 1 || recallTokenEnabled)) { + prompt.push(comma); + } + } + + // Add recall token + if (recallTokenEnabled) { + prompt.push(this.tokenizer.vocab['?']); + } + + // Target is the original prefix + const target = [...prefix]; + return { prompt, target }; + } +} diff --git a/examples/piston-train-toy/src/lib/train/data/toy/dataset.ts b/examples/piston-train-toy/src/lib/train/data/toy/dataset.ts new file mode 100644 index 00000000..55059d4f --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/data/toy/dataset.ts @@ -0,0 +1,217 @@ +import { IterableDataset } from '@piston-ml/piston-web'; +import { MersenneTwister19937, Random } from 'random-js'; + +import type { ToyTokenizer } from './types'; + +export const BOS = ''; +export const EOS = ''; +export const MASK = ''; + +export interface SpecialTokensConfig { + includeBos: boolean; + includeEos: boolean; + includeMask: boolean; +} + +export type SpecialTokenSet = { + bos: string; + eos?: string; + mask?: string; +}; + +export interface ToySequence { + prompt?: number[]; + target: number[]; + mask?: boolean[]; + // Absolute sample index within this run (for deterministic per-sample behavior) + absoluteIndex?: number; +} + +// Raw format interfaces that preserve prompt/target distinction and show mask tokens +export interface CollatedRawSequence { + // The full sequence with all tokens visible (including mask tokens, BOS, EOS) + fullSequence: number[]; + // Original prompt tokens (if any) + prompt?: number[]; + // Original target tokens + target?: number[]; + // Which tokens are masked out (-100 in labels) + ignored?: boolean[]; +} + +export interface ToyAutoregressiveBatch { + // Tensor outputs for training + tensors: [T, T]; // [input, target] + // Raw format for visualization/debugging + raw: CollatedRawSequence[]; + // Original uncollated samples + samples: ToySequence[]; +} + +export interface ToyBidirectionalBatch { + tensors: [T, T, T]; // [input, labels, attentionMask] + raw: CollatedRawSequence[]; + samples: ToySequence[]; +} + +export interface ToyEncoderDecoderBatch { + tensors: [T, T, T]; // [encoderInput, decoderInput, decoderTarget] + raw: CollatedRawSequence[]; + samples: ToySequence[]; +} + +export interface ToyDatasetLike extends IterableDataset { + readonly config: DatasetConfig; + readonly tokenizer: ToyTokenizer; + readonly generator: Random; + readonly bosId: number | null; + readonly eosId: number | null; + readonly maskId: number | null; + readonly hasCanonicalTargets?: boolean; + readonly disableValidation?: boolean; + baseSeed: number; + readonly datasetName: string; + generateSequence(): ToySequence; + generateSequenceAt(index: number): ToySequence; +} + +function hashString32(input: string): number { + // Simple 32-bit FNV-1a hash + let hash = 0x811c9dc5; + for (let i = 0; i < input.length; i++) { + hash ^= input.charCodeAt(i); + hash = Math.imul(hash, 0x01000193); + } + return hash >>> 0; +} + +function mix32(x: number): number { + x = Math.imul(x ^ (x >>> 16), 0x7feb352d); + x = Math.imul(x ^ (x >>> 15), 0x846ca68b); + x = x ^ (x >>> 16); + return x >>> 0; +} + +export function deriveToySampleSeed( + baseSeed: number, + datasetName: string, + index: number, + scope: string = 'sample' +): number { + const nameHash = hashString32(datasetName); + const scopeHash = hashString32(scope); + const mixed = mix32(baseSeed ^ mix32(nameHash) ^ mix32(index) ^ mix32(scopeHash)); + // Ensure non-zero seed for MT engine + return mixed >>> 0 || 0x1; +} + +abstract class ToyDataset + extends IterableDataset + implements ToyDatasetLike +{ + readonly config: DatasetConfig; + readonly tokenizer: ToyTokenizer; + generator: Random; + public readonly bosId: number | null; + public readonly eosId: number | null; + public readonly maskId: number | null; + public readonly hasCanonicalTargets: boolean = true; + baseSeed: number; + public readonly datasetName: string; + public cursor: number = 0; + + private readonly _originalGenerateSequence: () => ToySequence; + + constructor( + config: DatasetConfig, + generator: Random, + specialTokensConfig: SpecialTokensConfig, + datasetName: string, + baseSeed: number + ) { + super(); + this.config = config; + + // Build vocabulary with special tokens + const coreVocab = this.buildVocab(); + const specialTokens: string[] = []; + + if (specialTokensConfig.includeBos) specialTokens.push(BOS); + if (specialTokensConfig.includeEos) specialTokens.push(EOS); + if (specialTokensConfig.includeMask) specialTokens.push(MASK); + + const fullVocab = [...coreVocab, ...specialTokens]; + + this.tokenizer = this.tokenizerFromVocab(fullVocab); + this.bosId = specialTokensConfig.includeBos ? this.tokenizer.vocab[BOS] : null; + this.eosId = specialTokensConfig.includeEos ? this.tokenizer.vocab[EOS] : null; + this.maskId = specialTokensConfig.includeMask ? this.tokenizer.vocab[MASK] : null; + this.generator = generator; + this.datasetName = datasetName; + this.baseSeed = baseSeed; + + // Wrap subclass-defined generateSequence with index-based deterministic generation + this._originalGenerateSequence = this.generateSequence.bind(this); + // Replace generateSequence to advance cursor and delegate to generateSequenceAt + this.generateSequence = () => this.generateSequenceAt(this.cursor++); + } + + protected abstract buildVocab(): string[]; + public abstract generateSequence(): ToySequence; + + private tokenizerFromVocab(vocab: string[]): ToyTokenizer { + const vocabMap = vocab.reduce( + (acc, token, index) => { + acc[token] = index; + return acc; + }, + {} as Record + ); + const ids = vocab.reduce( + (acc, token, index) => { + acc[index] = token; + return acc; + }, + {} as Record + ); + const tokenizer: ToyTokenizer = { + vocab: vocabMap, + ids, + lastToken: vocab.length - 1, + decode: (tokens) => tokens.map((token) => ids[token] ?? '').join(' ') + }; + + return tokenizer; + } + + public getItem(_index: number): ToySequence { + return this.generateSequence(); + } + + public [Symbol.iterator](): Iterator { + return { + next: () => { + const sequence = this.generateSequence(); + return { value: sequence, done: false }; + } + }; + } + + /** + * Generate a deterministic sequence at an absolute index without affecting cursor or RNG state elsewhere. + */ + public generateSequenceAt(index: number): ToySequence { + const seed = deriveToySampleSeed(this.baseSeed, this.datasetName, index, 'toy-sample'); + const rng = new Random(MersenneTwister19937.seed(seed)); + const previous = this.generator; + try { + this.generator = rng; + const seq = this._originalGenerateSequence(); + return { ...seq, absoluteIndex: index }; + } finally { + this.generator = previous; + } + } +} + +export default ToyDataset; diff --git a/examples/piston-train-toy/src/lib/train/data/toy/dyck.ts b/examples/piston-train-toy/src/lib/train/data/toy/dyck.ts new file mode 100644 index 00000000..0be28bc5 --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/data/toy/dyck.ts @@ -0,0 +1,157 @@ +import ToyDataset, { type ToySequence } from './dataset'; + +export interface DyckConfig { + sequenceLength: number; + order: number; + onlyTrainOnClosingBrackets: boolean; +} + +export const DYCK_CONFIG_METADATA = { + name: 'Dyck Language', + description: 'Generate balanced bracket sequences (Dyck words)', + supportsModelTypes: ['encoder'], + parameters: { + sequenceLength: { + name: 'Sequence Length', + description: 'Length of the sequence (must be even)', + type: 'number' as const, + min: 2, + max: 100, + step: 2, + default: 10 + }, + order: { + name: 'Order (Dyck-n)', + description: 'Number of bracket types (Dyck-n)', + type: 'number' as const, + min: 1, + max: 10, + default: 2 + }, + onlyTrainOnClosingBrackets: { + name: 'Only Train on Closing Brackets', + description: 'Only train on closing brackets', + type: 'boolean' as const, + default: false + } + } +} as const; + +export const DYCK_CONFIG_DEFAULTS: DyckConfig = { + sequenceLength: DYCK_CONFIG_METADATA.parameters.sequenceLength.default, + order: DYCK_CONFIG_METADATA.parameters.order.default, + onlyTrainOnClosingBrackets: DYCK_CONFIG_METADATA.parameters.onlyTrainOnClosingBrackets.default +}; + +export class DyckDataset extends ToyDataset { + /** + * For Dyck dataset: + * - Generate bracket pairs based on order + * - For order > 3, use regular number notation (e.g., (2, [2, {2) + */ + protected buildVocab(): string[] { + const { order } = this.config; + const vocab: string[] = []; + + // Base bracket types + const baseBrackets = ['()', '[]', '{}']; + + // Generate bracket pairs based on order + for (let i = 0; i < order; i++) { + const baseIndex = i % 3; + const level = Math.floor(i / 3); + + let openBracket = baseBrackets[baseIndex][0]; + let closeBracket = baseBrackets[baseIndex][1]; + + // Add number notation for levels > 0 + if (order > 3) { + const numberSuffix = (level + 1).toString(); + openBracket += numberSuffix; + closeBracket += numberSuffix; + } + + vocab.push(openBracket); + vocab.push(closeBracket); + } + + return vocab; + } + + /** + * Generate a random Dyck word (balanced brackets) of exact length. + * The algorithm uses a stack to track unclosed openings and ensures + * the result is properly balanced. + */ + public generateSequence(): ToySequence { + const { sequenceLength, order, onlyTrainOnClosingBrackets } = this.config; + + // Ensure length is even + const length = sequenceLength % 2 === 0 ? sequenceLength : sequenceLength - 1; + + // Generate bracket pairs + const pairs: [string, string][] = []; + const vocab = this.buildVocab(); + + for (let i = 0; i < order; i++) { + const openBracket = vocab[i * 2]; + const closeBracket = vocab[i * 2 + 1]; + pairs.push([openBracket, closeBracket]); + } + + let openLeft = length / 2; // how many openings remain + let closeLeft = length / 2; // how many closings remain + const stack: [string, string][] = []; // track unclosed openings + const result: string[] = []; // output characters + + while (openLeft > 0 || closeLeft > 0) { + if (openLeft === 0) { + // must close + const pair = stack.pop()!; + result.push(pair[1]); + closeLeft -= 1; + } else if (closeLeft === 0) { + // must open + const pair = pairs[this.generator.integer(0, pairs.length - 1)]; + stack.push(pair); + result.push(pair[0]); + openLeft -= 1; + } else { + // we can choose to open or close (if stack not empty) + if (stack.length > 0 && this.generator.real(0, 1) < 0.5) { + // close + const pair = stack.pop()!; + result.push(pair[1]); + closeLeft -= 1; + } else { + // open + const pair = pairs[this.generator.integer(0, pairs.length - 1)]; + stack.push(pair); + result.push(pair[0]); + openLeft -= 1; + } + } + } + + // Convert to tokens + const tokens = result.map((char) => this.tokenizer.vocab[char]); + + // Only apply mask if onlyTrainOnClosingBrackets is true + if (onlyTrainOnClosingBrackets) { + // Create a set of closing bracket characters for easy lookup + const closingBrackets = new Set(); + for (let i = 0; i < order; i++) { + const closeBracket = vocab[i * 2 + 1]; // Closing brackets are at odd indices + closingBrackets.add(closeBracket); + } + + // Create boolean mask - true for closing brackets, false for opening brackets + const mask = result.map((char) => closingBrackets.has(char)); + + return { target: tokens, mask }; + } else { + // Return tokens without masking + return { target: tokens }; + } + } +} diff --git a/examples/piston-train-toy/src/lib/train/data/toy/elman.ts b/examples/piston-train-toy/src/lib/train/data/toy/elman.ts new file mode 100644 index 00000000..cf6b5e25 --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/data/toy/elman.ts @@ -0,0 +1,88 @@ +import ToyDataset, { type ToySequence } from './dataset'; + +export const ELMAN_CONFIG_METADATA = { + name: 'Elman Grammar', + description: 'Generate simplified sentences based on Elman (1990) grammar.', + citations: { + entries: [ + { + name: 'Elman, 1990', + url: 'https://onlinelibrary.wiley.com/doi/abs/10.1207/s15516709cog1402_1' + } + ] + }, + supportsModelTypes: ['encoder', 'encoder-decoder', 'decoder'], + parameters: {} +} as const; + +export const ELMAN_CONFIG_DEFAULTS = {} as const; +export type ElmanConfig = object; + +export class ElmanDataset extends ToyDataset { + public readonly hasCanonicalTargets = false; + // Compact placeholder dictionary + private static categories: Record = { + NOUN_HUM: ['man', 'woman', 'boy', 'girl'], + NOUN_ANIM: ['cat', 'mouse', 'dog'], + NOUN_INANIM: ['book', 'rock', 'pencil'], + NOUN_AGRESS: ['dragon', 'monster'], + NOUN_FRAG: ['glass', 'plate'], + NOUN_FOOD: ['apple', 'banana', 'orange', 'pie', 'pizza', 'sandwich'], + VERB_INTRAN: ['think', 'sleep'], + VERB_TRAN: ['see', 'chase'], + VERB_AGPAT: ['move', 'break'], + VERB_PERCEPT: ['smell', 'see'], + VERB_DESTROY: ['break', 'smash'], + VERB_EAT: ['eat', 'consume'] + }; + + // Precompiled templates with exactly 3 positions; null means PAD + private static templates: (string[] | null)[][] = [ + ['NOUN_HUM', 'VERB_EAT', 'NOUN_FOOD'], + ['NOUN_HUM', 'VERB_PERCEPT', 'NOUN_INANIM'], + ['NOUN_HUM', 'VERB_DESTROY', 'NOUN_FRAG'], + ['NOUN_HUM', 'VERB_INTRAN', 'PAD'], + ['NOUN_HUM', 'VERB_TRAN', 'NOUN_HUM'], + ['NOUN_HUM', 'VERB_AGPAT', 'NOUN_INANIM'], + ['NOUN_HUM', 'VERB_AGPAT', 'PAD'], + ['NOUN_ANIM', 'VERB_EAT', 'NOUN_FOOD'], + ['NOUN_ANIM', 'VERB_TRAN', 'NOUN_ANIM'], + ['NOUN_ANIM', 'VERB_AGPAT', 'NOUN_INANIM'], + ['NOUN_ANIM', 'VERB_AGPAT', 'PAD'], + ['NOUN_INANIM', 'VERB_AGPAT', 'PAD'], + ['NOUN_AGRESS', 'VERB_DESTROY', 'NOUN_FRAG'], + ['NOUN_AGRESS', 'VERB_EAT', 'NOUN_HUM'], + ['NOUN_AGRESS', 'VERB_EAT', 'NOUN_ANIM'], + ['NOUN_AGRESS', 'VERB_EAT', 'NOUN_FOOD'] + ].map((tpl) => tpl.map((slot) => (slot === 'PAD' ? null : ElmanDataset.categories[slot]))); + + protected buildVocab(): string[] { + // Union of all words used in generation, plus 'who' + const vocabSet = new Set(); + // console.log(ElmanDataset.categories); + for (const list of Object.values(ElmanDataset.categories)) { + for (const w of list) { + vocabSet.add(w); + } + } + vocabSet.add('PAD'); + return Array.from(vocabSet); + } + + public generateSequence(): ToySequence { + // Choose a precompiled template + const template = + ElmanDataset.templates[this.generator.integer(0, ElmanDataset.templates.length - 1)]; + + // Replace placeholders with dictionary lookup (null => PAD) + const sentenceWords: string[] = template.map((slot) => { + if (slot === null) return 'PAD'; + const idx = this.generator.integer(0, slot.length - 1); + return slot[idx] as string; + }); + + // Map words to token IDs + const target = sentenceWords.map((w) => this.tokenizer.vocab[w]); + return { target }; + } +} diff --git a/examples/piston-train-toy/src/lib/train/data/toy/index.ts b/examples/piston-train-toy/src/lib/train/data/toy/index.ts new file mode 100644 index 00000000..47307735 --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/data/toy/index.ts @@ -0,0 +1,139 @@ +import type { Config } from '$lib/workspace/config'; +import type { Random } from 'random-js'; + +import type ToyDataset from './dataset'; +import type { SpecialTokensConfig } from './dataset'; + +import { type AdditionConfig, AdditionDataset } from './addition'; +import { type CopyMemoryConfig, CopyMemoryDataset } from './copyMemory'; +import { type DyckConfig, DyckDataset } from './dyck'; +import { type ElmanConfig, ElmanDataset } from './elman'; +import { type MarkedAdditionConfig, MarkedAdditionDataset } from './markedAddition'; +import { type ModularAdditionConfig, ModularAdditionDataset } from './modularAddition'; +import { type ParityConfig, ParityDataset } from './parity'; +import { type RandomConfig, RandomDataset } from './random'; +import { type RepeatConfig, RepeatDataset } from './repeat'; +import { type ReverseConfig, ReverseDataset } from './reverse'; +import { type SlapjackConfig, SlapjackDataset } from './slapjack'; +import { type SortConfig, SortDataset } from './sort'; +import { type TemporalOrderConfig, TemporalOrderDataset } from './temporalOrder'; +import { type TwoSumConfig, TwoSumDataset } from './twoSum'; +import { type ZerosConfig, ZerosDataset } from './zeros'; +export { type AdditionConfig, AdditionDataset } from './addition'; +export { type CopyMemoryConfig, CopyMemoryDataset } from './copyMemory'; +export { default as ToyDataset } from './dataset'; +export { type DyckConfig, DyckDataset } from './dyck'; +export { type ElmanConfig, ElmanDataset } from './elman'; +export { type MarkedAdditionConfig, MarkedAdditionDataset } from './markedAddition'; +export { type ModularAdditionConfig, ModularAdditionDataset } from './modularAddition'; +export { type RepeatConfig, RepeatDataset } from './repeat'; +export { type ReverseConfig, ReverseDataset } from './reverse'; +export { type SlapjackConfig, SlapjackDataset } from './slapjack'; +export { type SortConfig, SortDataset } from './sort'; +export { type TemporalOrderConfig, TemporalOrderDataset } from './temporalOrder'; +export { type TwoSumConfig, TwoSumDataset } from './twoSum'; + +export interface DatasetConfigs { + 'two-sum': TwoSumConfig; + sort: SortConfig; + repeat: RepeatConfig; + reverse: ReverseConfig; + addition: AdditionConfig; + 'modular-addition': ModularAdditionConfig; + zeros: ZerosConfig; + slapjack: SlapjackConfig; + dyck: DyckConfig; + parity: ParityConfig; + random: RandomConfig; + 'marked-addition': MarkedAdditionConfig; + 'copy-memory': CopyMemoryConfig; + 'temporal-order': TemporalOrderConfig; + elman: ElmanConfig; +} + +export function buildToyDataset(config: Config, generator: Random): ToyDataset { + const datasetName = config.data.dataset; + // Derive a baseSeed for deterministic per-sample generation; if the run is seeded, + // the provided generator will be seeded deterministically. If not, we still persist + // baseSeed in checkpoints for resumption. + const baseSeed = generator.int32() >>> 0; + + const isEncoderOnly = config.model.topology === 'encoder'; + const specialTokensConfig: SpecialTokensConfig = { + includeBos: !isEncoderOnly, + includeEos: !isEncoderOnly && config.data.specialTokens.includeEos, + includeMask: isEncoderOnly + }; + + if (datasetName === 'sort') { + const sortConfig = config.data.datasets.sort; + return new SortDataset(sortConfig, generator, specialTokensConfig, datasetName, baseSeed); + } else if (datasetName === 'repeat') { + const repeatConfig = config.data.datasets.repeat; + return new RepeatDataset(repeatConfig, generator, specialTokensConfig, datasetName, baseSeed); + } else if (datasetName === 'reverse') { + const reverseConfig = config.data.datasets.reverse; + return new ReverseDataset(reverseConfig, generator, specialTokensConfig, datasetName, baseSeed); + } else if (datasetName === 'addition') { + const additionConfig = config.data.datasets.addition; + return new AdditionDataset( + additionConfig, + generator, + specialTokensConfig, + datasetName, + baseSeed + ); + } else if (datasetName === 'modular-addition') { + const modularAdditionConfig = config.data.datasets['modular-addition']; + return new ModularAdditionDataset( + modularAdditionConfig, + generator, + specialTokensConfig, + datasetName, + baseSeed + ); + } else if (datasetName === 'two-sum') { + const twoSumConfig = config.data.datasets['two-sum']; + return new TwoSumDataset(twoSumConfig, generator, specialTokensConfig, datasetName, baseSeed); + } else if (datasetName === 'zeros') { + const zerosConfig = config.data.datasets.zeros; + return new ZerosDataset(zerosConfig, generator, specialTokensConfig, datasetName, baseSeed); + } else if (datasetName === 'slapjack') { + const slapjackConfig = config.data.datasets.slapjack; + return new SlapjackDataset( + slapjackConfig, + generator, + specialTokensConfig, + datasetName, + baseSeed + ); + } else if (datasetName === 'dyck') { + const dyckConfig = config.data.datasets.dyck; + return new DyckDataset(dyckConfig, generator, specialTokensConfig, datasetName, baseSeed); + } else if (datasetName === 'parity') { + const parityConfig = config.data.datasets.parity; + return new ParityDataset(parityConfig, generator, specialTokensConfig, datasetName, baseSeed); + } else if (datasetName === 'random') { + const randomConfig = config.data.datasets.random; + return new RandomDataset(randomConfig, generator, specialTokensConfig, datasetName, baseSeed); + } else if (datasetName === 'marked-addition') { + const markedAdditionConfig = config.data.datasets['marked-addition']; + return new MarkedAdditionDataset( + markedAdditionConfig, + generator, + specialTokensConfig, + datasetName, + baseSeed + ); + } else if (datasetName === 'temporal-order') { + const cfg = config.data.datasets['temporal-order']; + return new TemporalOrderDataset(cfg, generator, specialTokensConfig, datasetName, baseSeed); + } else if (datasetName === 'copy-memory') { + const cfg = config.data.datasets['copy-memory']; + return new CopyMemoryDataset(cfg, generator, specialTokensConfig, datasetName, baseSeed); + } else if (datasetName === 'elman') { + return new ElmanDataset(null, generator, specialTokensConfig, datasetName, baseSeed); + } else { + throw new Error(`Unknown dataset: ${datasetName}`); + } +} diff --git a/examples/piston-train-toy/src/lib/train/data/toy/markedAddition.ts b/examples/piston-train-toy/src/lib/train/data/toy/markedAddition.ts new file mode 100644 index 00000000..bc23a71b --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/data/toy/markedAddition.ts @@ -0,0 +1,127 @@ +import ToyDataset, { type ToySequence } from './dataset'; + +export interface MarkedAdditionConfig { + sequenceLength: number; // number of (value, marker) pairs + maxInputValue: number; // largest value sampled per element in the sequence + maxSumValue: number; // largest representable sum (numeric vocab range) + includeEqualsToken: boolean; // include '=' delimiter at end of prompt +} + +export const MARKED_ADDITION_CONFIG_METADATA = { + name: 'Marked Addition', + description: + 'Sum values whose associated marker equals 1. Not real-valued, unlike the original task.', + citations: { + entries: [ + { + name: 'Hochreiter & Schmidhuber, 1997', + url: 'https://ieeexplore.ieee.org/abstract/document/6795963' + } + ] + }, + supportsModelTypes: ['encoder', 'encoder-decoder', 'decoder'], + parameters: { + sequenceLength: { + name: 'Sequence Length', + description: 'Number of (value, marker) pairs', + type: 'number' as const, + min: 2, + max: 64, + step: 1, + default: 8 + }, + maxSumValue: { + name: 'Max Sum Value', + description: 'Largest representable sum (controls numeric vocab range)', + type: 'number' as const, + min: 1, + max: 10000, + step: 1, + default: 100 + }, + maxInputValue: { + name: 'Max Input Value', + description: 'Largest value sampled per element in the sequence', + type: 'number' as const, + min: 1, + max: 1000, + step: 1, + default: 10 + }, + includeEqualsToken: { + name: 'Include Equals Token', + description: 'Append an = token after the sequence', + type: 'boolean' as const, + default: true + } + } +} as const; + +export const MARKED_ADDITION_CONFIG_DEFAULTS: MarkedAdditionConfig = { + sequenceLength: MARKED_ADDITION_CONFIG_METADATA.parameters.sequenceLength.default, + maxInputValue: MARKED_ADDITION_CONFIG_METADATA.parameters.maxInputValue.default, + maxSumValue: MARKED_ADDITION_CONFIG_METADATA.parameters.maxSumValue.default, + includeEqualsToken: MARKED_ADDITION_CONFIG_METADATA.parameters.includeEqualsToken.default +}; + +export class MarkedAdditionDataset extends ToyDataset { + /** + * Vocab design: + * - values are discretized based on precision: for precision p, there are (10^p + 1) buckets + * 0.0..1.0. + * We emit tokens V0..V{numBuckets-1} representing these buckets. + * - markers are tokens 'M0' and 'M1'. + * - optionally BOS/EOS/mask are appended by ToyDataset constructor based on specialTokensConfig. + */ + protected buildVocab(): string[] { + const { maxSumValue, includeEqualsToken } = this.config; + const padWidth = maxSumValue.toString().length; + const vocab = Array.from({ length: maxSumValue + 1 }, (_, i) => + i.toString().padStart(padWidth, '0') + ); + vocab.push('M0', 'M1'); + if (includeEqualsToken) { + vocab.push('='); + } + return vocab; + } + + /** + * Generate a sequence of (value, marker) pairs and target the sum of values with marker==1. + * Representation: + * - Discretize each value in [0,1] to bucket index b in [0, buckets-1]. Token is Vb. + * - Marker token is M0 or M1. + * - Prompt layout: Vb0 Mx0 Vb1 Mx1 ... Vb{n-1} Mx{n-1} '=' + * - Target: a single token VbSum where bSum is the discretized sum clamped to [0, 1]. + */ + public generateSequence(): ToySequence { + const { sequenceLength, maxInputValue, maxSumValue, includeEqualsToken } = this.config; + + const prompt: number[] = []; + let sum = 0; + const padWidth = maxSumValue.toString().length; + + for (let i = 0; i < sequenceLength; i++) { + const value = this.generator.integer(0, maxInputValue); + const marker = this.generator.integer(0, 1); + + prompt.push(value); + prompt.push(this.tokenizer.vocab[`M${marker}`]); + + if (marker === 1) sum += value; + } + + if (includeEqualsToken) { + prompt.push(this.tokenizer.vocab['=']); + } + + if (sum > maxSumValue) { + sum = maxSumValue; // clamp to representable range + } + + const targetTokenStr = sum.toString().padStart(padWidth, '0'); + const target = [this.tokenizer.vocab[targetTokenStr]]; + + return { prompt, target }; + } +} diff --git a/examples/piston-train-toy/src/lib/train/data/toy/modularAddition.ts b/examples/piston-train-toy/src/lib/train/data/toy/modularAddition.ts new file mode 100644 index 00000000..6e95cbb1 --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/data/toy/modularAddition.ts @@ -0,0 +1,95 @@ +import ToyDataset, { type ToySequence } from './dataset'; + +export interface ModularAdditionConfig { + maxNumber: number; + modulo: number; + includeExpressionTokens: boolean; +} + +export const MODULAR_ADDITION_CONFIG_METADATA = { + name: 'Modular Addition', + description: 'Add two numbers with modulo', + supportsModelTypes: ['encoder', 'encoder-decoder', 'decoder'], + parameters: { + maxNumber: { + name: 'Max Number', + type: 'number' as const, + min: 5, + max: 1000, + default: 500 + }, + modulo: { + name: 'Modulo', + type: 'number' as const, + min: 2, + max: 113, + default: 113 + }, + includeExpressionTokens: { + name: 'Include Expression Tokens (+, =)', + type: 'boolean' as const, + default: true + } + } +} as const; + +export const MODULAR_ADDITION_CONFIG_DEFAULTS: ModularAdditionConfig = { + maxNumber: MODULAR_ADDITION_CONFIG_METADATA.parameters.maxNumber.default, + modulo: MODULAR_ADDITION_CONFIG_METADATA.parameters.modulo.default, + includeExpressionTokens: + MODULAR_ADDITION_CONFIG_METADATA.parameters.includeExpressionTokens.default +}; + +export class ModularAdditionDataset extends ToyDataset { + /** + * Tokenizer for modular addition. + * - tokens 0 .. maxNum represent the numbers 0 … maxNum + * (When converting to a string, token id i <= maxNum becomes `<${i}>` with zero-padding.) + * - token maxNum+1 -> "+" + * - token maxNum+2 -> "=" + */ + protected buildVocab(): string[] { + const { maxNumber, includeExpressionTokens } = this.config; + const vocab: string[] = []; + const padWidth = maxNumber.toString().length; + // 1. Add tokens for numbers + vocab.push( + ...Array.from({ length: maxNumber + 1 }, (_, i) => i.toString().padStart(padWidth, '0')) + ); + // 2. Add tokens for + and = + if (includeExpressionTokens) { + vocab.push('+', '='); + } + return vocab; + } + + /** + * Modular Addition: generate two numbers (in [0, maxNum)) and compute (num1+num2)%maxNum. + * Example: <15>+<03>=<05> (if maxNum is 113 and 15+3=18, 18%113=18, but if 15+100=115, 115%113=2) + */ + public generateSequence(): ToySequence { + const { maxNumber, modulo, includeExpressionTokens } = this.config; + + // 1. Pick numbers and compute sum. Include maxNumber in the range. + const sum = this.generator.integer(0, maxNumber); + const num1 = sum > 0 ? this.generator.integer(0, sum - 1) : 0; + const num2 = sum - num1; + const sumModulo = sum % modulo; + + // 2. Convert to token IDs: + // - 0..maxNum-1 are numbers + // - maxNum is '+' + // - maxNum+1 is '=' + let prompt; + if (includeExpressionTokens) { + prompt = [num1, maxNumber + 1, num2, maxNumber + 2]; + } else { + prompt = [num1, num2]; + } + + // 3. Target is a single token corresponding to the modular sum + const target = [sumModulo]; + + return { prompt, target }; + } +} diff --git a/examples/piston-train-toy/src/lib/train/data/toy/parity.ts b/examples/piston-train-toy/src/lib/train/data/toy/parity.ts new file mode 100644 index 00000000..737f6215 --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/data/toy/parity.ts @@ -0,0 +1,96 @@ +import ToyDataset, { type ToySequence } from './dataset'; + +export interface ParityConfig { + sequenceLength: number; + includeColon: boolean; +} + +export const PARITY_CONFIG_METADATA = { + name: 'Parity', + description: 'Determine if a bit string has even or odd number of 1s (parity)', + citations: { + entries: [ + { name: 'Minsky & Papert, 1969', url: 'https://psycnet.apa.org/record/1969-35017-000' } + ] + }, + supportsModelTypes: ['encoder', 'encoder-decoder', 'decoder'], + parameters: { + sequenceLength: { + name: 'Sequence Length', + description: 'Number of bits in the string', + type: 'number' as const, + min: 1, + max: 20, + default: 4 + }, + includeColon: { + name: 'Include Colon', + type: 'boolean' as const, + default: true + } + } +} as const; + +export const PARITY_CONFIG_DEFAULTS: ParityConfig = { + sequenceLength: PARITY_CONFIG_METADATA.parameters.sequenceLength.default, + includeColon: PARITY_CONFIG_METADATA.parameters.includeColon.default +}; + +export class ParityDataset extends ToyDataset { + /** + * For parity dataset: + * - token 0 -> '0' + * - token 1 -> '1' + * - token 2 -> ':' (if includeColon is true) + * - token 3 -> 'even' + * - token 4 -> 'odd' + */ + protected buildVocab(): string[] { + const { includeColon } = this.config; + const vocab: string[] = ['0', '1']; + + if (includeColon) { + vocab.push(':'); + } + + vocab.push('even', 'odd'); + return vocab; + } + + /** + * Parity: generate a bit string and determine its parity: + * - prompt: the bit string with optional colon at the end + * - target: "even" if even number of 1s, "odd" if odd number of 1s + * Example: 10110:odd (3 ones = odd parity) + */ + public generateSequence(): ToySequence { + const { sequenceLength, includeColon } = this.config; + const bits = Array.from({ length: sequenceLength }, () => this.generator.integer(0, 1)); + + const prompt: number[] = []; + + // Add the bit string to the prompt + for (const bit of bits) { + prompt.push(bit); + } + + // Add colon if enabled + if (includeColon) { + const colon = this.tokenizer.vocab[':']; + prompt.push(colon); + } + + // Calculate parity (count number of 1s) + const onesCount = bits.filter((bit) => bit === 1).length; + const isEven = onesCount % 2 === 0; + + const target: number[] = []; + if (isEven) { + target.push(this.tokenizer.vocab['even']); + } else { + target.push(this.tokenizer.vocab['odd']); + } + + return { prompt, target }; + } +} diff --git a/examples/piston-train-toy/src/lib/train/data/toy/random.ts b/examples/piston-train-toy/src/lib/train/data/toy/random.ts new file mode 100644 index 00000000..442cd7db --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/data/toy/random.ts @@ -0,0 +1,82 @@ +import ToyDataset, { type ToySequence } from './dataset'; + +export interface RandomConfig { + sequenceLength: number; // length of the prompt + responseLength: number; // length of the response + vocabSize: number; // number of non-special tokens +} + +export const RANDOM_CONFIG_METADATA = { + name: 'Random Token Prediction', + description: + 'Prompt is random tokens and target is an independent random token. This is a good test case; there is no learnable mapping, so any model should struggle to learn this task.', + supportsModelTypes: ['encoder', 'encoder-decoder', 'decoder'], + parameters: { + sequenceLength: { + name: 'Prompt Length', + description: 'Number of tokens in the prompt', + type: 'number' as const, + min: 0, + max: 512, + step: 1, + default: 5 + }, + responseLength: { + name: 'Response Length', + description: 'Number of tokens in the response', + type: 'number' as const, + min: 0, + max: 512, + step: 1, + default: 1 + }, + vocabSize: { + name: 'Vocab Size', + description: 'Number of distinct tokens (excluding special tokens)', + type: 'number' as const, + min: 2, + max: 1000, + step: 1, + default: 10 + } + } +} as const; + +export const RANDOM_CONFIG_DEFAULTS: RandomConfig = { + sequenceLength: RANDOM_CONFIG_METADATA.parameters.sequenceLength.default, + responseLength: RANDOM_CONFIG_METADATA.parameters.responseLength.default, + vocabSize: RANDOM_CONFIG_METADATA.parameters.vocabSize.default +}; + +export class RandomDataset extends ToyDataset { + /** + * For the random dataset: + * - Create token strings V0..V{vocabSize-1} + */ + protected buildVocab(): string[] { + const { vocabSize } = this.config; + const vocab: string[] = []; + for (let i = 0; i < vocabSize; i++) { + vocab.push(`V${i}`); + } + return vocab; + } + + /** + * Generate a random prompt of length sequenceLength, and a single independent random target token. + * The target is sampled independently of the prompt, making the task unlearnable beyond chance. + */ + public generateSequence(): ToySequence { + const { sequenceLength, responseLength, vocabSize } = this.config; + const prompt: number[] = []; + for (let i = 0; i < sequenceLength; i++) { + prompt.push(this.generator.integer(0, vocabSize - 1)); + } + // placing predictable before unpredictable yields plausible loss + const target: number[] = []; + for (let i = 0; i < responseLength; i++) { + target.push(this.generator.integer(0, vocabSize - 1)); + } + return { prompt, target }; + } +} diff --git a/examples/piston-train-toy/src/lib/train/data/toy/repeat.ts b/examples/piston-train-toy/src/lib/train/data/toy/repeat.ts new file mode 100644 index 00000000..42716ce1 --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/data/toy/repeat.ts @@ -0,0 +1,110 @@ +import ToyDataset, { type ToySequence } from './dataset'; + +export interface RepeatConfig { + sequenceLength: number; + maxNumber: number; + includeCommas: boolean; + includeColon: boolean; +} + +export const REPEAT_CONFIG_METADATA = { + name: 'Repeat', + description: 'Repeat a sequence of characters', + supportsModelTypes: ['encoder', 'encoder-decoder', 'decoder'], + parameters: { + sequenceLength: { + name: 'Sequence Length', + description: 'Number of items to repeat', + type: 'number' as const, + min: 1, + max: 10, + default: 3 + }, + maxNumber: { + name: 'Max Alphabet Character', + description: 'Maximum alphabet character', + type: 'number' as const, + min: 5, + max: 26, + default: 5 + }, + includeCommas: { + name: 'Include Commas', + type: 'boolean' as const, + default: false + }, + includeColon: { + name: 'Include Colon', + type: 'boolean' as const, + default: true + } + } +} as const; + +export const REPEAT_CONFIG_DEFAULTS: RepeatConfig = { + sequenceLength: REPEAT_CONFIG_METADATA.parameters.sequenceLength.default, + maxNumber: REPEAT_CONFIG_METADATA.parameters.maxNumber.default, + includeCommas: REPEAT_CONFIG_METADATA.parameters.includeCommas.default, + includeColon: REPEAT_CONFIG_METADATA.parameters.includeColon.default +}; + +export class RepeatDataset extends ToyDataset { + /** + * For repeat dataset: + * - tokens 0 .. maxNum represent the alphabet characters corresponding to the numbers 0 .. maxNum. + * - token maxNum+1 -> ':' + * - token maxNum+2 -> ',' + */ + protected buildVocab(): string[] { + const { maxNumber, includeColon, includeCommas } = this.config; + const vocab: string[] = []; + const aCharCode = 'A'.charCodeAt(0); + for (let n = 0; n < maxNumber; n++) { + const token = String.fromCharCode(aCharCode + n); + vocab.push(token); + } + if (includeColon) { + vocab.push(':'); + } + if (includeCommas) { + vocab.push(','); + } + return vocab; + } + + /** + * Repeat: generate a sequence of characters and return: + * - prompt: the sequence with commas (token 1) between numbers and a colon (token 0) at the end. + * - target: the same sequence with commas between numbers. + * Example: BBCA:BBCA + */ + public generateSequence(): ToySequence { + const { sequenceLength, maxNumber, includeCommas, includeColon } = this.config; + const nums = Array.from({ length: sequenceLength }, () => + this.generator.integer(0, maxNumber - 1) + ); + const prompt: number[] = []; + + const comma = this.tokenizer.vocab[',']; + const colon = this.tokenizer.vocab[':']; + + for (let i = 0; i < nums.length; i++) { + prompt.push(nums[i]); + if (includeCommas && i < nums.length - 1) { + prompt.push(comma); + } + } + if (includeColon) { + prompt.push(colon); + } + + const target: number[] = []; + for (let i = 0; i < nums.length; i++) { + target.push(nums[i]); + if (includeCommas && i < nums.length - 1) { + target.push(comma); + } + } + return { prompt, target }; + } +} diff --git a/examples/piston-train-toy/src/lib/train/data/toy/reverse.ts b/examples/piston-train-toy/src/lib/train/data/toy/reverse.ts new file mode 100644 index 00000000..01df0994 --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/data/toy/reverse.ts @@ -0,0 +1,111 @@ +import ToyDataset, { type ToySequence } from './dataset'; + +export interface ReverseConfig { + sequenceLength: number; + maxNumber: number; + includeCommas: boolean; + includeColon: boolean; +} + +export const REVERSE_CONFIG_METADATA = { + name: 'Reverse', + description: 'Reverse a sequence of characters', + supportsModelTypes: ['encoder', 'encoder-decoder', 'decoder'], + parameters: { + sequenceLength: { + name: 'Sequence Length', + description: 'Number of items to reverse', + type: 'number' as const, + min: 1, + max: 10, + default: 3 + }, + maxNumber: { + name: 'Max Alphabet Character', + description: 'Maximum alphabet character', + type: 'number' as const, + min: 5, + max: 26, + default: 5 + }, + includeCommas: { + name: 'Include Commas', + type: 'boolean' as const, + default: false + }, + includeColon: { + name: 'Include Colon', + type: 'boolean' as const, + default: true + } + } +} as const; + +export const REVERSE_CONFIG_DEFAULTS: ReverseConfig = { + sequenceLength: REVERSE_CONFIG_METADATA.parameters.sequenceLength.default, + maxNumber: REVERSE_CONFIG_METADATA.parameters.maxNumber.default, + includeCommas: REVERSE_CONFIG_METADATA.parameters.includeCommas.default, + includeColon: REVERSE_CONFIG_METADATA.parameters.includeColon.default +}; + +export class ReverseDataset extends ToyDataset { + /** + * For reverse dataset: + * - tokens 0 .. maxNum represent the alphabet characters corresponding to the numbers 0 .. maxNum. + * - token maxNum+1 -> ':' + * - token maxNum+2 -> ',' + */ + protected buildVocab(): string[] { + const { maxNumber, includeColon, includeCommas } = this.config; + const vocab: string[] = []; + const aCharCode = 'A'.charCodeAt(0); + for (let n = 0; n < maxNumber; n++) { + const token = String.fromCharCode(aCharCode + n); + vocab.push(token); + } + if (includeColon) { + vocab.push(':'); + } + if (includeCommas) { + vocab.push(','); + } + return vocab; + } + + /** + * Reverse: generate a sequence of characters and return: + * - prompt: the sequence with commas (token 1) between numbers and a colon (token 0) at the end. + * - target: the reversed sequence with commas between numbers. + * Example: BBCA:ACBB + */ + public generateSequence(): ToySequence { + const { sequenceLength, maxNumber, includeCommas, includeColon } = this.config; + const nums = Array.from({ length: sequenceLength }, () => + this.generator.integer(0, maxNumber - 1) + ); + const prompt: number[] = []; + + const comma = this.tokenizer.vocab[',']; + const colon = this.tokenizer.vocab[':']; + + for (let i = 0; i < nums.length; i++) { + prompt.push(nums[i]); + if (includeCommas && i < nums.length - 1) { + prompt.push(comma); + } + } + if (includeColon) { + prompt.push(colon); + } + + const target: number[] = []; + const reversedNums = nums.reverse(); + for (let i = 0; i < reversedNums.length; i++) { + target.push(reversedNums[i]); + if (includeCommas && i < reversedNums.length - 1) { + target.push(comma); + } + } + return { prompt, target }; + } +} diff --git a/examples/piston-train-toy/src/lib/train/data/toy/slapjack.ts b/examples/piston-train-toy/src/lib/train/data/toy/slapjack.ts new file mode 100644 index 00000000..a6017237 --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/data/toy/slapjack.ts @@ -0,0 +1,113 @@ +import ToyDataset, { type ToySequence } from './dataset'; + +export interface SlapjackConfig { + sequenceLength: number; + slapOnDoubles: boolean; + slapOnSandwiches: boolean; + includeColon: boolean; + // onlyTrainOnSlaps: boolean; +} + +export const SLAPJACK_CONFIG_METADATA = { + name: 'Slapjack', + description: + '"Slap" (🖐️) when the card is a Jack, a double (same card twice in a row), or a sandwich (same card with one card in between). Otherwise, take no action (❌).', + supportsModelTypes: ['encoder', 'encoder-decoder', 'decoder'], + parameters: { + sequenceLength: { + name: 'Sequence Length', + description: 'Total length of the sequence', + type: 'number' as const, + min: 5, + max: 200, + default: 5 + }, + slapOnDoubles: { + name: 'Slap on Doubles', + description: 'Slap on doubles (same card twice in a row)', + type: 'boolean' as const, + default: true + }, + slapOnSandwiches: { + name: 'Slap on Sandwiches', + description: 'Slap on sandwiches (same card with one card in between)', + type: 'boolean' as const, + default: true + }, + includeColon: { + name: 'Include Colon', + type: 'boolean' as const, + default: true + } + // onlyTrainOnSlaps: { + // name: 'Only Train on Slaps', + // description: 'Only train on slaps (not on cards)', + // type: 'boolean' as const, + // default: true + // } + } +} as const; + +export const SLAPJACK_CONFIG_DEFAULTS: SlapjackConfig = { + sequenceLength: SLAPJACK_CONFIG_METADATA.parameters.sequenceLength.default, + slapOnDoubles: SLAPJACK_CONFIG_METADATA.parameters.slapOnDoubles.default, + slapOnSandwiches: SLAPJACK_CONFIG_METADATA.parameters.slapOnSandwiches.default, + includeColon: SLAPJACK_CONFIG_METADATA.parameters.includeColon.default + // onlyTrainOnSlaps: SLAPJACK_CONFIG_METADATA.parameters.onlyTrainOnSlaps.default, +}; + +const CARDS = 'A23456789JQK'; + +export class SlapjackDataset extends ToyDataset { + /** + * Vocabulary contains card tokens and action tokens (🖐️ slap, ❌ no slap). + */ + protected buildVocab(): string[] { + const { includeColon } = this.config; + const vocab = [...CARDS.split('')]; + if (includeColon) vocab.push(':'); + vocab.push('🖐️', '❌'); + return vocab; + } + + /** + * Slapjack: + * - Input (prompt): sequence of cards of length sequenceLength + * - Target: action at each position (🖐️ if slap, ❌ otherwise) + * Slap rules: + * - Slap on Jack (J) + * - Slap on doubles (same card twice in a row) if enabled + * - Slap on sandwiches (same card with one card in between) if enabled + */ + public generateSequence(): ToySequence { + const { sequenceLength, slapOnDoubles, slapOnSandwiches, includeColon } = this.config; + + // Build card sequence + const cardSeq = Array.from( + { length: sequenceLength }, + () => CARDS[this.generator.integer(0, CARDS.length - 1)] + ); + + // Map to token ids for prompt + const prompt: number[] = cardSeq.map((c) => this.tokenizer.vocab[c]); + if (includeColon) { + const colon = this.tokenizer.vocab[':']; + prompt.push(colon); + } + + // Compute action at each position + const slapId = this.tokenizer.vocab['🖐️']; + const noId = this.tokenizer.vocab['❌']; + const target: number[] = []; + for (let i = 0; i < cardSeq.length; i++) { + const c = cardSeq[i]; + const isJack = c === 'J'; + const isDouble = slapOnDoubles && i > 0 && c === cardSeq[i - 1]; + const isSandwich = slapOnSandwiches && i > 1 && c === cardSeq[i - 2]; + const shouldSlap = isJack || isDouble || isSandwich; + target.push(shouldSlap ? slapId : noId); + } + + return { prompt, target }; + } +} diff --git a/examples/piston-train-toy/src/lib/train/data/toy/sort.ts b/examples/piston-train-toy/src/lib/train/data/toy/sort.ts new file mode 100644 index 00000000..a3511419 --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/data/toy/sort.ts @@ -0,0 +1,109 @@ +import ToyDataset, { type ToySequence } from './dataset'; + +export interface SortConfig { + sequenceLength: number; + maxNumber: number; + includeCommas: boolean; + includeColon: boolean; +} + +export const SORT_CONFIG_METADATA = { + name: 'Alphabetical Sorting', + description: 'Sort a sequence of alphabet characters', + supportsModelTypes: ['encoder', 'encoder-decoder', 'decoder'], + parameters: { + sequenceLength: { + name: 'Number of Items', + type: 'number' as const, + min: 2, + max: 10, + default: 5 + }, + maxNumber: { + name: 'Max Alphabet Character', + type: 'number' as const, + min: 3, + max: 26, + default: 26 + }, + includeCommas: { + name: 'Include Commas', + type: 'boolean' as const, + default: false + }, + includeColon: { + name: 'Include Colon', + type: 'boolean' as const, + default: true + } + } +} as const; + +export const SORT_CONFIG_DEFAULTS: SortConfig = { + sequenceLength: SORT_CONFIG_METADATA.parameters.sequenceLength.default, + maxNumber: SORT_CONFIG_METADATA.parameters.maxNumber.default, + includeCommas: SORT_CONFIG_METADATA.parameters.includeCommas.default, + includeColon: SORT_CONFIG_METADATA.parameters.includeColon.default +}; + +export class SortDataset extends ToyDataset { + /** + * For sort dataset: + * - tokens 0 .. maxNum represent the alphabet characters corresponding to the numbers 0 .. maxNum. + * - token maxNum+1 -> ':' + * - token maxNum+2 -> ',' + */ + protected buildVocab(): string[] { + const { maxNumber, includeColon, includeCommas } = this.config; + const vocab: string[] = []; + const aCharCode = 'A'.charCodeAt(0); + for (let n = 0; n < maxNumber; n++) { + const token = String.fromCharCode(aCharCode + n); + vocab.push(token); + } + if (includeColon) { + vocab.push(':'); + } + if (includeCommas) { + vocab.push(','); + } + return vocab; + } + + /** + * Sorting: generate a sequence of characters and return: + * - prompt: the unsorted sequence with commas (token 1) between numbers and a colon (token 0) at the end. + * - target: the sorted sequence with commas between numbers. + * Example: BBCA:ABCBB + */ + public generateSequence(): ToySequence { + const { sequenceLength, maxNumber, includeCommas, includeColon } = this.config; + const nums = Array.from({ length: sequenceLength }, () => + this.generator.integer(0, maxNumber - 1) + ); + const sorted = [...nums].sort((a, b) => a - b); + const prompt: number[] = []; + + const comma = this.tokenizer.vocab[',']; + const colon = this.tokenizer.vocab[':']; + + for (let i = 0; i < nums.length; i++) { + prompt.push(nums[i]); + if (includeCommas && i < nums.length - 1) { + prompt.push(comma); + } + } + if (includeColon) { + prompt.push(colon); + } + + const target: number[] = []; + for (let i = 0; i < sorted.length; i++) { + target.push(sorted[i]); + if (includeCommas && i < sorted.length - 1) { + target.push(comma); + } + } + return { prompt, target }; + } +} diff --git a/examples/piston-train-toy/src/lib/train/data/toy/temporalOrder.ts b/examples/piston-train-toy/src/lib/train/data/toy/temporalOrder.ts new file mode 100644 index 00000000..2cde0423 --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/data/toy/temporalOrder.ts @@ -0,0 +1,115 @@ +import ToyDataset, { type ToySequence } from './dataset'; + +export interface TemporalOrderConfig { + sequenceLength: number; // total length including distractors and special symbols X/Y + vocabSize: number; // number of distractor letters (A..) + includeSeparators: boolean; // whether to include comma separators between tokens +} + +export const TEMPORAL_ORDER_CONFIG_METADATA = { + name: 'Temporal Order Recognition', + description: 'Classify whether X appears before Y (XY) or after Y (YX).', + supportsModelTypes: ['encoder', 'encoder-decoder', 'decoder'], + citations: { + entries: [ + { + name: 'Hochreiter & Schmidhuber, 1997', + url: 'https://ieeexplore.ieee.org/abstract/document/6795963' + } + ] + }, + parameters: { + sequenceLength: { + name: 'Sequence Length', + description: 'Total number of tokens (including X and Y)', + type: 'number' as const, + min: 5, + max: 256, + step: 1, + default: 13 + }, + vocabSize: { + name: 'Distractor Vocab Size', + description: 'Number of distractor letters (A..)', + type: 'number' as const, + min: 2, + max: 100, + step: 1, + default: 10 + }, + includeSeparators: { + name: 'Include Separators', + description: 'Insert commas between tokens', + type: 'boolean' as const, + default: false + } + } +} as const; + +export const TEMPORAL_ORDER_CONFIG_DEFAULTS: TemporalOrderConfig = { + sequenceLength: TEMPORAL_ORDER_CONFIG_METADATA.parameters.sequenceLength.default, + vocabSize: TEMPORAL_ORDER_CONFIG_METADATA.parameters.vocabSize.default, + includeSeparators: TEMPORAL_ORDER_CONFIG_METADATA.parameters.includeSeparators.default +}; + +export class TemporalOrderDataset extends ToyDataset { + /** + * Vocab: + * - distractor letters D0..D{vocabSize-1} + * - special symbols 'X', 'Y' + * - classes 'XY', 'YX' + * - optional ',' separator + */ + protected buildVocab(): string[] { + const { vocabSize, includeSeparators } = this.config; + const padWidth = vocabSize.toString().length; + // 1. Add tokens for numbers + const vocab = Array.from( + { length: vocabSize + 1 }, + (_, i) => `D${i.toString().padStart(padWidth, '0')}` + ); + vocab.push('X', 'Y', 'XY', 'YX'); + if (includeSeparators) vocab.push(','); + return vocab; + } + + /** + * Build a sequence with exactly one X and one Y inserted among distractors. + * Prompt: sequence of tokens (with optional commas). Target: single class token. + */ + public generateSequence(): ToySequence { + const { sequenceLength, vocabSize, includeSeparators } = this.config; + if (sequenceLength < 2) { + throw new Error('sequenceLength must be at least 2 to place X and Y'); + } + const prompt: number[] = []; + + // Choose distinct positions for X and Y + const posX = this.generator.integer(0, sequenceLength - 1); + let posY = this.generator.integer(0, sequenceLength - 1); + while (posY === posX) posY = this.generator.integer(0, sequenceLength - 1); + + const comma = this.tokenizer.vocab[',']; + const x = this.tokenizer.vocab['X']; + const y = this.tokenizer.vocab['Y']; + + for (let i = 0; i < sequenceLength; i++) { + let tokenId: number; + if (i === posX) { + tokenId = x; + } else if (i === posY) { + tokenId = y; + } else { + tokenId = this.generator.integer(0, vocabSize - 1); + } + prompt.push(tokenId); + if (includeSeparators && i < sequenceLength - 1) { + prompt.push(comma); + } + } + + const classToken = posX < posY ? 'XY' : 'YX'; + const target = [this.tokenizer.vocab[classToken]]; + return { prompt, target }; + } +} diff --git a/examples/piston-train-toy/src/lib/train/data/toy/twoSum.ts b/examples/piston-train-toy/src/lib/train/data/toy/twoSum.ts new file mode 100644 index 00000000..9affb227 --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/data/toy/twoSum.ts @@ -0,0 +1,178 @@ +import ToyDataset, { type ToySequence } from './dataset'; + +export interface TwoSumConfig { + sequenceLength: number; + maxNumber: number; + includeCommas: boolean; + includeExpressionTokens: boolean; +} + +export const TWO_SUM_CONFIG_METADATA = { + name: 'Two Sum', + description: 'Find two numbers in a sequence that sum to a target', + supportsModelTypes: ['encoder', 'encoder-decoder', 'decoder'], + parameters: { + sequenceLength: { + name: 'Sequence Length', + description: 'Length of the sequence', + type: 'number' as const, + min: 2, + max: 10, + default: 4 + }, + maxNumber: { + name: 'Max Number', + description: 'Maximum value in the sequence', + type: 'number' as const, + min: 10, + max: 100, + default: 15 + }, + includeCommas: { + name: 'Include Commas', + type: 'boolean' as const, + default: false + }, + includeExpressionTokens: { + name: 'Include Expression Tokens', + type: 'boolean' as const, + default: true + } + } +} as const; + +export const TWO_SUM_CONFIG_DEFAULTS: TwoSumConfig = { + sequenceLength: TWO_SUM_CONFIG_METADATA.parameters.sequenceLength.default, + maxNumber: TWO_SUM_CONFIG_METADATA.parameters.maxNumber.default, + includeCommas: TWO_SUM_CONFIG_METADATA.parameters.includeCommas.default, + includeExpressionTokens: TWO_SUM_CONFIG_METADATA.parameters.includeExpressionTokens.default +}; + +export class TwoSumDataset extends ToyDataset { + /** + * For Two-Sum dataset: + * - tokens 0 .. maxNum represent the numbers 0 .. maxNum. + * - token maxNum+1 -> ':' + * - token maxNum+2 -> '=' + * - token maxNum+3 -> ',' + */ + protected buildVocab(): string[] { + const { maxNumber, includeCommas, includeExpressionTokens } = this.config; + const vocab: string[] = []; + const padWidth = maxNumber.toString().length; + // 1. Add tokens for numbers + vocab.push( + ...Array.from({ length: maxNumber + 1 }, (_, i) => i.toString().padStart(padWidth, '0')) + ); + // 2. Add tokens for operators + if (includeExpressionTokens) { + vocab.push(':'); + vocab.push('='); + } + if (includeCommas) { + vocab.push(','); + } + return vocab; + } + + /** + * Two-Sum: generate a sequence of numbers (each in [0, maxNum]) and two random indices. + * The prompt consists of: + * - the sequence (with commas if enabled) + * - a colon, the sum and an equals sign. + * The target is the two numbers (from the chosen indices) separated by a comma. + * Example: ABC:D=AB or ABC:D=A,B + */ + public generateSequence(): ToySequence { + const { sequenceLength, maxNumber, includeCommas, includeExpressionTokens } = this.config; + // Generate a two-sum instance with a unique solution using a target-first constructive method. + const MAX_RETRIES = 10; + const maxElement = Math.floor(maxNumber / 2); + for (let attempt = 0; attempt < MAX_RETRIES; attempt++) { + // 1) Choose two distinct indices for the special pair + const i = this.generator.integer(0, sequenceLength - 1); + let j = this.generator.integer(0, sequenceLength - 1); + while (j === i) { + j = this.generator.integer(0, sequenceLength - 1); + } + // 2) Choose their values from the usable range so t stays in-vocab + const a = this.generator.integer(0, maxElement); + const b = this.generator.integer(0, maxElement); + const t = a + b; // target sum + + // 3) Fill the rest while avoiding any other pair summing to t + const nums = new Array(sequenceLength); + nums[i] = a; + nums[j] = b; + + const seenNonSpecial: number[] = []; + let feasible = true; + + for (let k = 0; k < sequenceLength; k++) { + if (k === i || k === j) continue; + + // Build the set of disallowed values for this position + const disallowed = new Set(); + disallowed.add(a); + disallowed.add(b); + for (const y of seenNonSpecial) { + const complement = t - y; + if (complement >= 0 && complement <= maxElement) { + disallowed.add(complement); + } + } + + // Enumerate allowed candidates in the usable range + const allowed: number[] = []; + for (let val = 0; val <= maxElement; val++) { + if (!disallowed.has(val)) { + allowed.push(val); + } + } + + if (allowed.length === 0) { + feasible = false; + break; + } + + const choice = allowed[this.generator.integer(0, allowed.length - 1)]; + nums[k] = choice; + seenNonSpecial.push(choice); + } + + if (!feasible) { + continue; + } + + // 4) Build prompt and target + const prompt: number[] = []; + const comma = this.tokenizer.vocab[',']; + + for (let k = 0; k < nums.length; k++) { + prompt.push(nums[k]); + if (includeCommas && k < nums.length - 1) { + prompt.push(comma); + } + } + + if (includeExpressionTokens) { + prompt.push(this.tokenizer.vocab[':']); + } + + prompt.push(t); + + if (includeExpressionTokens) { + prompt.push(this.tokenizer.vocab['=']); + } + + const target: number[] = [nums[i]]; + if (includeCommas) { + target.push(comma); + } + target.push(nums[j]); + + return { prompt, target }; + } + throw new Error('Failed to generate a unique-solution two-sum instance after 10 attempts'); + } +} diff --git a/examples/piston-train-toy/src/lib/train/data/toy/types.ts b/examples/piston-train-toy/src/lib/train/data/toy/types.ts new file mode 100644 index 00000000..231e3cdb --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/data/toy/types.ts @@ -0,0 +1,6 @@ +export interface ToyTokenizer { + vocab: Record; + ids: Record; + lastToken: number; + decode(tokens: number[]): string; +} diff --git a/examples/piston-train-toy/src/lib/train/data/toy/zeros.ts b/examples/piston-train-toy/src/lib/train/data/toy/zeros.ts new file mode 100644 index 00000000..a43e9124 --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/data/toy/zeros.ts @@ -0,0 +1,55 @@ +import ToyDataset, { type ToySequence } from './dataset'; + +export interface ZerosConfig { + sequenceLength: number; +} + +export const ZEROS_CONFIG_METADATA = { + name: 'Zeros', + description: + 'Output only zeros (useful test case; if a model fails to learn this, something is wrong)', + supportsModelTypes: ['encoder', 'encoder-decoder', 'decoder'], + disableValidation: true, + parameters: { + sequenceLength: { + name: 'Sequence Length', + description: 'Length of the sequence', + type: 'number' as const, + min: 2, + max: 10, + default: 5 + } + } +} as const; + +export const ZEROS_CONFIG_DEFAULTS: ZerosConfig = { + sequenceLength: ZEROS_CONFIG_METADATA.parameters.sequenceLength.default +}; + +export class ZerosDataset extends ToyDataset { + /** + * For zeros dataset (baseline/debug task): + */ + protected buildVocab(): string[] { + // We have at least two tokens so this is actually technically a classification task + return '01'.split(''); + } + + public readonly disableValidation = true; + + /** + * Zeros: (Baseline) returns a sequence of zeros. + * prompt: single '0' + * target: sequence of zeros (sequenceLength - 1) + * Example: 0:0000 + */ + public generateSequence(): ToySequence { + const { sequenceLength } = this.config; + const zeroToken = this.tokenizer.vocab['0']; + + // Target is sequenceLength zeros + const target = Array(sequenceLength).fill(zeroToken); + + return { target }; + } +} From 1d32c71702152fa354d60cd7b362180ca348a6ae Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 26 Oct 2025 17:47:00 -0600 Subject: [PATCH 530/590] Add effective train loop --- .../src/lib/train/moduleWorker.ts | 196 ++++++++++++ .../src/lib/train/protocol.ts | 45 +++ .../piston-train-toy/src/lib/train/session.ts | 302 ++++++++++++++++++ .../piston-train-toy/src/lib/train/types.ts | 42 +++ .../src/lib/train/utils/model.ts | 129 ++++++++ .../src/lib/train/utils/random.ts | 9 + 6 files changed, 723 insertions(+) create mode 100644 examples/piston-train-toy/src/lib/train/moduleWorker.ts create mode 100644 examples/piston-train-toy/src/lib/train/protocol.ts create mode 100644 examples/piston-train-toy/src/lib/train/session.ts create mode 100644 examples/piston-train-toy/src/lib/train/types.ts create mode 100644 examples/piston-train-toy/src/lib/train/utils/model.ts create mode 100644 examples/piston-train-toy/src/lib/train/utils/random.ts diff --git a/examples/piston-train-toy/src/lib/train/moduleWorker.ts b/examples/piston-train-toy/src/lib/train/moduleWorker.ts new file mode 100644 index 00000000..97417c0c --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/moduleWorker.ts @@ -0,0 +1,196 @@ +import * as piston from '@piston-ml/piston-web'; + +import type { WorkerCommand, WorkerEvent } from './protocol'; + +import { TrainingSession } from './session'; + +let session: TrainingSession | undefined; + +// Console Interception +const originalConsole = { + log: console.log.bind(console), + error: console.error.bind(console), + warn: console.warn.bind(console), + info: console.info.bind(console), + debug: console.debug.bind(console) +}; + +function formatArgs(args: unknown[]) { + return args + .map((arg) => { + if (typeof arg === 'object' && arg !== null) { + try { + return JSON.stringify(arg); + } catch (_: unknown) { + return '[Unserializable Object]'; + } + } + return String(arg); + }) + .join(' '); +} + +interface LogInfo { + level: string; + message: string; + source: string; + lineno?: number; + colno?: number; +} + +function sendLog(level: string, message: string, source: string = '[Worker]') { + // Check if this is a WASM log and parse it + const wasmLogRegex = /^\[WASM ([^:]+):(\d+)(?::(\d+))?\] (.*)$/; + const match = message.match(wasmLogRegex); + + if (match) { + // Handle WASM logs with parsed source info + const [, filepath, lineno, colno, actualMessage] = match; + const logInfo: LogInfo = { + level, + message: actualMessage, + source: `[WASM] ${filepath}`, + lineno: parseInt(lineno, 10), + ...(colno && { colno: parseInt(colno, 10) }) + }; + self.postMessage({ type: 'log', ...logInfo }); + } else { + // Handle regular logs + const logInfo: LogInfo = { + level, + message, + source + }; + self.postMessage({ type: 'log', ...logInfo }); + } +} + +// Wrap console methods before importing Piston to catch its logs +Object.keys(originalConsole).forEach((level) => { + (console as unknown as Record void>)[level] = ( + ...args: unknown[] + ) => { + const message = formatArgs(args); + + // Use sendLog which will handle WASM log parsing internally + sendLog(level, message, currentExecutionSource); + + // Also call original console for debugging + originalConsole[level as keyof typeof originalConsole](...args); + }; +}); + +// Global error handler - catches unhandled errors +self.addEventListener('error', (event) => { + const errorMessage = `Uncaught Error: ${event.message} at ${event.filename}:${event.lineno}:${event.colno}`; + sendLog('error', errorMessage); + if (event.error?.stack) { + sendLog('error', `${event.error.stack}`); + } +}); + +// Unhandled promise rejection handler - catches unhandled promise rejections +self.addEventListener('unhandledrejection', (event) => { + const errorMessage = `Unhandled Promise Rejection: ${event.reason}`; + sendLog('error', errorMessage); + if (event.reason?.stack) { + sendLog('error', `${event.reason.stack}`); + } + // Prevent the default browser behavior (logging to console) + event.preventDefault(); +}); + +// Intercept and override the default error reporting +const originalOnError = self.onerror; +self.onerror = (message, source, lineno, colno, error) => { + const errorMessage = `Global Error: ${message} at ${source}:${lineno}:${colno}`; + sendLog('error', errorMessage); + if (error?.stack) { + sendLog('error', `${error.stack}`); + } + // Call original handler if it exists + if (originalOnError) { + return originalOnError(message, source, lineno, colno, error); + } + // Prevent default browser error handling + return true; +}; + +// +// End Console Interception +// + +// Track current execution context for logging (will be reassigned during execution) +let currentExecutionSource = '[Worker]'; + +import type { Config } from '$lib/workspace/config'; + +function postEvent(e: WorkerEvent) { + self.postMessage(e); +} + +function startTraining() { + if (!session) return; + const runId = session.runId; + session.start().catch((error: unknown) => { + console.error('Training error:', error); + self.postMessage({ + type: 'error', + runId, + message: error instanceof Error ? error.message : String(error), + name: error instanceof Error ? error.name : undefined, + stack: error instanceof Error ? error.stack : undefined + }); + }); +} + +// Message handler for worker +self.addEventListener('message', async (event) => { + const raw = event.data as WorkerCommand | { type: string; data?: unknown }; + const type: string = (raw as { type: string }).type; + const data: unknown = (raw as { data?: unknown }).data; + + switch (type) { + case 'start': + try { + const { runId: runIdFromData, config } = data as { + runId: string; + config: Config; + }; + currentExecutionSource = `[Training:${runIdFromData}]`; + + console.info(`Starting training for run ${runIdFromData}`); + session = new TrainingSession(runIdFromData, config, postEvent); + startTraining(); + } catch (error: unknown) { + console.error('Training error:', error); + self.postMessage({ + type: 'error', + runId: (data as { runId?: string })?.runId, + message: error instanceof Error ? error.message : String(error), + name: error instanceof Error ? error.name : undefined, + stack: error instanceof Error ? error.stack : undefined + }); + } + break; + + default: + console.warn(`Unknown message type: ${type}`); + break; + } +}); + +// Initialize Piston, then signal that the worker is ready +piston + .init() + .then(() => { + console.info('Piston initialized'); + self.postMessage({ type: 'ready' }); + }) + .catch((error: unknown) => { + console.error('Error initializing Piston:', error); + self.postMessage({ + type: 'error', + message: error instanceof Error ? error.message : String(error) + }); + }); diff --git a/examples/piston-train-toy/src/lib/train/protocol.ts b/examples/piston-train-toy/src/lib/train/protocol.ts new file mode 100644 index 00000000..f3e5ca29 --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/protocol.ts @@ -0,0 +1,45 @@ +import type { Config } from '$lib/workspace/config'; + +type WithRunId = { runId: string }; + +export type WorkerCommand = + | { + type: 'start'; + data: { runId: string; config: Config }; + } + | { type: 'stop' } + | { type: 'inspectModel'; data: { requestId: string; config: Config } }; + +type ReadyWorkerEvent = { + type: 'ready'; +}; + +type LogWorkerEvent = { + type: 'log'; + level: 'debug' | 'info' | 'warn' | 'error'; + message: string; + source?: string; + lineno?: number; + colno?: number; +}; + +type ErrorWorkerEvent = { + type: 'error'; + name?: string; + message: string; + stack?: string; +}; + +export type RunWorkerEventWithoutRunId = + | { + type: 'metrics'; + data: { [metricName: string]: number }; + metadata?: { step?: number }; + } + | { type: 'complete' } + | LogWorkerEvent + | ErrorWorkerEvent; + +export type RunWorkerEvent = RunWorkerEventWithoutRunId & WithRunId; + +export type WorkerEvent = ReadyWorkerEvent | LogWorkerEvent | ErrorWorkerEvent | RunWorkerEvent; diff --git a/examples/piston-train-toy/src/lib/train/session.ts b/examples/piston-train-toy/src/lib/train/session.ts new file mode 100644 index 00000000..37de201e --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/session.ts @@ -0,0 +1,302 @@ +import type { Config } from '$lib/workspace/config'; + +import { Adam, AdamW, SGD, type Tensor } from '@piston-ml/piston-web'; +import * as piston from '@piston-ml/piston-web'; + +import type { BuiltData } from './data/pipeline'; +import type { + ToyAutoregressiveBatch, + ToyBidirectionalBatch, + ToyDatasetLike, + ToyEncoderDecoderBatch +} from './data/toy/dataset'; +import type { RunWorkerEvent, RunWorkerEventWithoutRunId, WorkerEvent } from './protocol'; +import type { GeneratableModel } from './types'; + +import { tensorWrap } from './data'; +import { buildDataPipeline } from './data/pipeline'; +import { buildToyDataset } from './data/toy'; +import { + DecoderTransformer, + EncoderDecoderTransformer, + EncoderTransformer +} from './model/transformer'; +import { + calculateBlockSize, + calculateVocabSize, + createDataloader, + createModel +} from './utils/model'; +import { seededRandom } from './utils/random'; + +// @ts-expect-error polyfill +Symbol.dispose ||= Symbol.for('Symbol.dispose'); + +export class TrainingSession { + readonly runId: string; + private config: Config; + private readonly post: (e: RunWorkerEventWithoutRunId) => void; + + private model!: GeneratableModel; + private optimizer!: piston.Optimizer; + private trainDataset!: ToyDatasetLike; + private blockSize!: number | { source: number; target: number }; + + private isSetup: boolean = false; + + private startTimeMs: number | null = null; + private lastLogTime: number | null = null; + private lastLogStep: number | null = null; + private stepCount: number = 0; + + private dataPipeline!: BuiltData; + + constructor(runId: string, config: Config, post: (e: WorkerEvent) => void) { + this.runId = runId; + this.config = config; + this.post = (e: RunWorkerEventWithoutRunId) => + // We only post the subset of events that have runId in the payload + (post as (e: RunWorkerEvent) => void)({ ...e, runId: this.runId }); + } + + private logMetrics(data: { [metricName: string]: number }, metadata?: { step?: number }) { + this.post({ type: 'metrics', data, metadata }); + } + + private async setup() { + if (this.isSetup) { + return; + } + + // Determine model type and create appropriate dataloader/model + const isEncoderOnly = this.config.model.topology === 'encoder'; + const isDecoderOnly = this.config.model.topology === 'decoder'; + const isEncoderDecoder = this.config.model.topology === 'encoder-decoder'; + + // Ensure shared-object allocation is enabled so buffer handles are stable across steps + piston.gpu.setSharedObjectAllocationEnabled(this.config.training.sharedObjectAllocation); + piston.gpu.setCachingEnabled(this.config.training.cachingEnabled); + piston.gpu.setInplaceSupport(this.config.training.inplaceSupport); + + // Set up dataset; we use two generators so we change validation parameters without affecting + // training + const trainGenerator = seededRandom(); + + this.trainDataset = buildToyDataset(this.config, trainGenerator); + + // Calculate vocab size using shared utility + const vocabSize = calculateVocabSize(this.trainDataset); + const [trainDataloaderForSizing] = createDataloader( + this.config, + this.trainDataset, + trainGenerator, + tensorWrap + ); + const blockSize = + this.blockSize !== undefined + ? this.blockSize + : calculateBlockSize(this.config, trainDataloaderForSizing); + this.blockSize = blockSize; + + const datasetName = this.config.data.dataset; + + console.debug( + `Created dataset ${datasetName} with vocab size ${vocabSize} and block size ${blockSize}` + ); + + if (!isEncoderOnly && !isDecoderOnly && !isEncoderDecoder) { + throw new Error( + `Unsupported model type: ${this.config.model.topology}. Only 'encoder', 'decoder', and` + + ` 'encoder-decoder' are currently supported.` + ); + } + + // Create model + this.model = createModel(this.config, vocabSize, blockSize); + + // Build and store the training data pipeline (iterator bound to current dataset/collate) + this.dataPipeline = await buildDataPipeline(this.config, trainGenerator, this.trainDataset); + + const optimizerConfig = this.config.optimizer; + const effectiveWeightDecay = optimizerConfig.weightDecay.present + ? optimizerConfig.weightDecay.value + : 0.0; + if (optimizerConfig.type === 'AdamW' || optimizerConfig.type === 'Adam') { + this.optimizer = new (optimizerConfig.type === 'AdamW' ? AdamW : Adam)( + this.model.parameters(), + piston.gpu, + { + lr: optimizerConfig.lr, + betas: [optimizerConfig.adam.beta1, optimizerConfig.adam.beta2], + eps: optimizerConfig.adam.eps, + weightDecay: effectiveWeightDecay, + amsgrad: optimizerConfig.adam.amsgrad + } + ); + } else if (optimizerConfig.type === 'SGD') { + this.optimizer = new SGD(this.model.parameters(), piston.gpu, { + lr: optimizerConfig.lr, + momentum: optimizerConfig.sgd.momentum, + dampening: optimizerConfig.sgd.dampening, + weightDecay: effectiveWeightDecay, + nesterov: optimizerConfig.sgd.nesterov + }); + } + + this.model.train(); + + this.isSetup = true; + } + + async step({ manual = false }: { manual?: boolean } = {}): Promise< + IteratorResult + > { + if (this.startTimeMs == null) { + this.startTimeMs = Date.now(); + } + if (this.lastLogStep == null) { + this.lastLogStep = this.stepCount; + } + try { + const iterNext = await this.dataPipeline.iterator.next(); + if (iterNext.done) { + return { done: true, value: 'completed' }; + } + const batch = iterNext.value; + performance.mark('stepStart'); + // Reset peak GPU memory tracking at the start of the step + piston.gpu.markUsageBytesStep(); + + const loggingStep = manual || this.stepCount % this.config.training.logSteps === 0; + + let loss: Tensor; + if (this.config.model.topology === 'encoder') { + // For BERT: batch contains [inputIds, labels, attentionMask] + const { tensors } = batch as ToyBidirectionalBatch; + const [inputIds, bertLabels, attentionMask] = tensors; + + let computedLoss: Tensor | null = null; + [, , , computedLoss] = (this.model as EncoderTransformer).forward( + await inputIds.to('gpu'), + { + attentionMask: await attentionMask.to('gpu'), + targets: await bertLabels.to('gpu') + } + ); + + if (!computedLoss) { + throw new Error('No loss tensor returned from encoder-only model'); + } + + loss = computedLoss; + } else if (this.config.model.topology === 'encoder-decoder') { + // For Transformer: batch contains [encoderInputs, decoderInputs, decoderTargets] + const { tensors } = batch as ToyEncoderDecoderBatch; + const [encoderInputs, decoderInputs, decoderTargets] = tensors; + const [, computedLoss] = (this.model as EncoderDecoderTransformer).forward( + await encoderInputs.to('gpu'), + await decoderInputs.to('gpu'), + { targets: await decoderTargets.to('gpu') } + ); + + if (!computedLoss) { + throw new Error('No loss tensor returned from encoder-decoder model'); + } + + loss = computedLoss; + } else { + // For GPT: batch contains [inputs, targets] + const { tensors } = batch as ToyAutoregressiveBatch; + const [inputs, gptTargets] = tensors; + const [, computedLoss] = (this.model as DecoderTransformer).forward( + await inputs.to('gpu'), + { + targets: await gptTargets.to('gpu') + } + ); + + if (!computedLoss) { + throw new Error('No loss tensor returned from decoder-only model'); + } + + loss = computedLoss; + } + + loss.backward(); + + await this.optimizer.step(); + + this.optimizer.zeroGrad(true); + + if (loggingStep) { + const currentTime = Date.now(); + const totalElapsedSeconds = (currentTime - this.startTimeMs!) / 1000; + + // Calculate delta time and steps since last log + const deltaTime = (currentTime - this.lastLogTime!) / 1000; + const deltaSteps = this.stepCount - this.lastLogStep!; + + // Calculate steps per second and words per second based on delta + const stepsPerSecond = deltaSteps > 0 ? deltaSteps / deltaTime : 0; + + // Calculate words per second (tokens per second) + // Get sequence length from the first tensor in the batch + let sequenceLength = 0; + + // Encoder-decoder will have three tensors in its batch, but we can just use the first one + const [inputs] = batch.tensors; + sequenceLength = inputs.shape[1]; // [batch_size, seq_len] + + const tokensPerStep = this.config.training.batchSize * sequenceLength; + const tokensPerSecond = deltaSteps > 0 ? (deltaSteps * tokensPerStep) / deltaTime : 0; + + let lossItem: number | null = null; + + const lossCpu = await loss.to('cpu'); + lossItem = await lossCpu.item(); + + if (lossItem === null) { + throw new Error('Loss item is null?'); + } + + const logData: Record = { + 'train/loss': lossItem, + 'speed/steps_per_second': stepsPerSecond, + 'speed/step': this.stepCount, + 'speed/tokens_per_second': tokensPerSecond, + 'speed/wall_clock_seconds': totalElapsedSeconds + }; + + this.logMetrics(logData, { step: this.stepCount }); + + // Update last log time and step + this.lastLogTime = currentTime; + this.lastLogStep = this.stepCount; + } + + this.stepCount++; + + performance.mark('stepEnd'); + } catch (error) { + console.error(`Error during training: ${error}`); + throw error; + } + return { done: false, value: undefined }; + } + + async start(): Promise { + await this.setup(); + while (true) { + const { done, value } = await this.step(); + if (done) { + if (value === 'completed') { + this.post({ type: 'complete' }); + break; + } + if (value === 'restarted') { + continue; + } + } + } + } +} diff --git a/examples/piston-train-toy/src/lib/train/types.ts b/examples/piston-train-toy/src/lib/train/types.ts new file mode 100644 index 00000000..18b340ce --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/types.ts @@ -0,0 +1,42 @@ +import type { DataLoader } from '@piston-ml/piston-web'; + +import type { + ToyAutoregressiveBatch, + ToyBidirectionalBatch, + ToyEncoderDecoderBatch, + ToySequence +} from './data/toy/dataset'; +import type { + DecoderTransformer, + EncoderDecoderTransformer, + EncoderTransformer +} from './model/transformer'; + +type CollateFn = (batch: B) => T; + +type ToyCollateInput = ToySequence[]; + +export type ToyBatchType = + | ToyBidirectionalBatch + | ToyEncoderDecoderBatch + | ToyAutoregressiveBatch; + +export type EncoderDecoderBatchType = ToyEncoderDecoderBatch; + +export type ToyAutoregressiveCollateFnType = CollateFn< + ToyCollateInput, + ToyAutoregressiveBatch +>; +export type ToyBidirectionalCollateFnType = CollateFn>; +export type ToyEncoderDecoderCollateFnType = CollateFn< + ToyCollateInput, + ToyEncoderDecoderBatch +>; + +export type EncoderDecoderCollateFnType = CollateFn>; + +export type ToyCollateFnType = CollateFn>; + +export type ToyDataloaderType = DataLoader>; + +export type GeneratableModel = DecoderTransformer | EncoderTransformer | EncoderDecoderTransformer; diff --git a/examples/piston-train-toy/src/lib/train/utils/model.ts b/examples/piston-train-toy/src/lib/train/utils/model.ts new file mode 100644 index 00000000..d4526778 --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/utils/model.ts @@ -0,0 +1,129 @@ +import type { Random } from 'random-js'; + +import { DataLoader, Tensor } from '@piston-ml/piston-web'; + +import type { Config } from '../../workspace/config'; +import type { ToyBatchType, ToyCollateFnType, ToyDataloaderType } from '../types'; + +import { type CollateWrapFunction } from '../data'; +import { + toyDatasetAutoregressiveCollate, + toyDatasetBidirectionalCollate, + toyDatasetEncoderDecoderCollate +} from '../data/toy/collate'; +import { type ToyDatasetLike, type ToySequence } from '../data/toy/dataset'; +import { + DecoderTransformer, + EncoderDecoderTransformer, + EncoderTransformer +} from '../model/transformer'; + +type EncoderDecoderBlockSize = { source: number; target: number }; + +/** + * Calculate the vocabulary size based on the dataset and configuration + */ +export function calculateVocabSize(dataset: ToyDatasetLike): number { + return 'vocabSize' in dataset + ? (dataset.vocabSize as number) + : Object.keys(dataset.tokenizer.vocab).length; +} + +export function calculateBlockSize( + config: Config, + dataloader: ToyDataloaderType +): number | EncoderDecoderBlockSize { + // Infer lengths without advancing iteration using deterministic index-based generation + const toy = dataloader.dataset as ToyDatasetLike; + + const sample = toy.generateSequenceAt(0); + + const promptLen = sample.prompt?.length ?? 0; + const targetLen = sample.target.length; + const eosPlus = toy.eosId !== null ? 1 : 0; + const bosPlus = toy.bosId !== null ? 1 : 0; + + if (config.model.topology === 'encoder-decoder') { + // Encoder input uses prompt (no BOS/EOS). Decoder target includes EOS if enabled. + return { source: promptLen, target: targetLen + eosPlus }; + } + + if (config.model.topology === 'encoder') { + // Encoder-only sees prompt+target (no specials) + return promptLen + targetLen; + } + + // Decoder-only sees BOS + prompt + target + EOS in full sequence + return bosPlus + promptLen + targetLen + eosPlus; +} + +// Overloads for strong typing based on dataset kind +export function createCollateFn( + config: Config, + dataset: ToyDatasetLike, + maskGenerator: Random | null, + wrapFunction?: CollateWrapFunction | null +): ToyCollateFnType { + const isEncoderOnly = config.model.topology === 'encoder'; + const isEncoderDecoder = config.model.topology === 'encoder-decoder'; + const collateOptions = wrapFunction !== undefined ? { wrapFunction } : {}; + if (isEncoderOnly) { + return (batch: ToySequence[]) => + toyDatasetBidirectionalCollate(batch as ToySequence[], dataset, { + maskPrompt: config.data.trainOnPrompt, + maskRatio: config.data.maskRatio, + // Derive per-sample RNG from dataset/index internally (stateless) + ...collateOptions + }); + } else if (isEncoderDecoder) { + return (batch: ToySequence[]) => + toyDatasetEncoderDecoderCollate(batch as ToySequence[], dataset, { + ...collateOptions + }); + } else { + return (batch: ToySequence[]) => + toyDatasetAutoregressiveCollate(batch as ToySequence[], dataset, { + ignorePrompt: !config.data.trainOnPrompt, + ...collateOptions + }); + } +} + +export function createDataloader( + config: Config, + dataset: ToyDatasetLike, + generator: Random, + wrapFunction?: CollateWrapFunction | null +): [ToyDataloaderType, ToyCollateFnType] { + const toyDataset = dataset as ToyDatasetLike; + const collateFn = createCollateFn(config, toyDataset, generator, wrapFunction); + return [new DataLoader>(toyDataset, { collateFn }), collateFn]; +} + +/** + * Create a model instance based on the configuration + */ +export function createModel( + config: Config, + vocabSize: number, + blockSize: number | { source: number; target: number } +): DecoderTransformer | EncoderTransformer | EncoderDecoderTransformer { + const isEncoderOnly = config.model.topology === 'encoder'; + const isDecoderOnly = config.model.topology === 'decoder'; + const isEncoderDecoder = config.model.topology === 'encoder-decoder'; + + if (!isEncoderOnly && !isDecoderOnly && !isEncoderDecoder) { + throw new Error( + `Unsupported model type: ${config.model.topology}. Only 'encoder', 'decoder', and 'encoder-decoder' are currently supported.` + ); + } + + if (isEncoderOnly) { + return new EncoderTransformer(config, vocabSize, blockSize as number); + } else if (isEncoderDecoder) { + const { source, target } = blockSize as { source: number; target: number }; + return new EncoderDecoderTransformer(config, vocabSize, source, target); + } else { + return new DecoderTransformer(config, vocabSize, blockSize as number); + } +} diff --git a/examples/piston-train-toy/src/lib/train/utils/random.ts b/examples/piston-train-toy/src/lib/train/utils/random.ts new file mode 100644 index 00000000..286d3db3 --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/utils/random.ts @@ -0,0 +1,9 @@ +import { MersenneTwister19937, Random } from 'random-js'; + +export function seededRandom(): Random { + return new Random(MersenneTwister19937.autoSeed()); +} + +export function forkRandom(random: Random): Random { + return new Random(MersenneTwister19937.seed(random.int32())); +} From 2ef817b53ea41d6fb52b7cab3ab16dc5c6cbb3ef Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 26 Oct 2025 18:13:20 -0600 Subject: [PATCH 531/590] Add workspace state --- .../src/lib/workspace/config.svelte.ts | 429 ++++++++++++++++++ .../src/lib/workspace/localStorage.svelte.ts | 98 ++++ .../src/lib/workspace/runs.svelte.ts | 210 +++++++++ .../src/lib/workspace/ui.svelte.ts | 173 +++++++ .../src/lib/workspace/utils.ts | 39 ++ .../src/lib/workspace/workers.svelte.ts | 110 +++++ 6 files changed, 1059 insertions(+) create mode 100644 examples/piston-train-toy/src/lib/workspace/config.svelte.ts create mode 100644 examples/piston-train-toy/src/lib/workspace/localStorage.svelte.ts create mode 100644 examples/piston-train-toy/src/lib/workspace/runs.svelte.ts create mode 100644 examples/piston-train-toy/src/lib/workspace/ui.svelte.ts create mode 100644 examples/piston-train-toy/src/lib/workspace/utils.ts create mode 100644 examples/piston-train-toy/src/lib/workspace/workers.svelte.ts diff --git a/examples/piston-train-toy/src/lib/workspace/config.svelte.ts b/examples/piston-train-toy/src/lib/workspace/config.svelte.ts new file mode 100644 index 00000000..d23f394b --- /dev/null +++ b/examples/piston-train-toy/src/lib/workspace/config.svelte.ts @@ -0,0 +1,429 @@ +import type { Config, ModelType } from '$lib/workspace/config'; + +import { DATASET_CONFIG_METADATA } from '$lib/train/data'; +import { getCollatedSampleData } from '$lib/train/data/collate'; +import { buildToyDataset } from '$lib/train/data/toy'; +import { TOY_DATASET_CONFIG_DEFAULTS } from '$lib/train/data/toy/config'; +import { calculateBlockSize, calculateVocabSize, createDataloader } from '$lib/train/utils/model'; +import { seededRandom } from '$lib/train/utils/random'; +import { SvelteURL, SvelteURLSearchParams } from 'svelte/reactivity'; + +import { getCurrentRun, getLatestRun } from './runs.svelte'; + +export const MODEL_TYPES = [ + 'decoder', + 'encoder', + 'encoder-decoder' +] as const satisfies readonly ModelType[]; + +const CONFIG_DEFAULTS: Config = { + training: { + logSteps: 5, + batchSize: 32, + dropout: { + present: false, + embedding: 0.1, + transformer: { + attention: 0.1, + residual: 0.1 + } + }, + sharedObjectAllocation: false, + cachingEnabled: false, + inplaceSupport: true + }, + data: { + dataset: 'sort', + trainOnPrompt: false, + maskRatio: 0.15, + specialTokens: { + includeEos: true + }, + datasets: TOY_DATASET_CONFIG_DEFAULTS + }, + model: { + topology: 'decoder', + layers: 1, + encoderDecoder: { + encoderLayers: 1, + decoderLayers: 1 + }, + layerNormalization: { + type: 'rmsnorm', + eps: 1e-5, + transformer: { + present: true, + position: 'pre' + } + }, + transformer: { + headDim: 16, + attention: { + present: true, + nKeyValueHeads: 4 + }, + positionalEncoding: { + present: true + }, + mlp: { + present: true, + activation: 'relu2', + hiddenExpansionFactor: 4 + } + } + }, + optimizer: { + type: 'AdamW', + lr: 1e-3, + weightDecay: { + present: true, + value: 1e-2, + useWeightDecayGroups: true + }, + adam: { + beta1: 0.9, + beta2: 0.999, + eps: 1e-8, + amsgrad: false + }, + sgd: { + momentum: 0.9, + dampening: 0, + nesterov: false + } + }, + version: 1 +}; + +/** + * Parses a value based on the type of the default value in the config. This is not wildly general, + * but it seems to work for the current config. + * @param valueStr - The value to parse. + * @param defaultValue - The default value. + * @returns The parsed value. + */ +function parseValueBasedOnDefault(valueStr: string, defaultValue: unknown): unknown { + if (typeof defaultValue === 'boolean') { + return valueStr.toLowerCase() === 'true'; + } + if (typeof defaultValue === 'number') { + const num = parseFloat(valueStr); + return isNaN(num) ? defaultValue : num; + } + return valueStr; // Default to string if type is not boolean or number +} + +/** + * Builds a config from URL search params. + * @param params - The URL search params. + * @param defaults - The defaults to use if no URL search params are present. + * @returns The config. + */ +function buildConfigFromUrlParams(params: URLSearchParams, defaults: Config): Partial { + const configFromUrl: Record = {}; + + for (const [path, valueStr] of params) { + const keys = path.split('.'); + let currentLevel = configFromUrl; + let currentDefaultsLevel: unknown = defaults; + + try { + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + if ( + currentDefaultsLevel === undefined || + typeof currentDefaultsLevel !== 'object' || + currentDefaultsLevel === null + ) { + throw new Error(`Invalid config path from URL: ${path}`); + } + currentDefaultsLevel = (currentDefaultsLevel as Record)[key]; + + if (i < keys.length - 1) { + if ( + !(currentLevel as Record)[key] || + typeof (currentLevel as Record)[key] !== 'object' + ) { + (currentLevel as Record)[key] = {}; + } + currentLevel = (currentLevel as Record)[key] as Record; + } else { + (currentLevel as Record)[key] = parseValueBasedOnDefault( + valueStr, + currentDefaultsLevel + ); + } + } + } catch (e) { + console.warn((e as Error).message); + continue; // Skip this parameter if path is invalid or type mismatch + } + } + return configFromUrl as Partial; +} + +function mergeDeep(target: Record, source: Record) { + for (const key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + const sourceVal = source[key]; + let targetKeyAsObject = target[key] as Record; + + if (sourceVal && typeof sourceVal === 'object' && !Array.isArray(sourceVal)) { + if ( + !targetKeyAsObject || + typeof targetKeyAsObject !== 'object' || + Array.isArray(targetKeyAsObject) + ) { + targetKeyAsObject = {}; + target[key] = targetKeyAsObject; + } + mergeDeep(targetKeyAsObject, sourceVal as Record); + } else if (sourceVal !== undefined) { + target[key] = sourceVal; + } + } + } +} + +/** + * Gets the initial config from the URL search params, or the defaults if no URL search params are + * present. + * @returns The initial config. + */ +function getInitialConfig(): Config { + // Start with effective defaults + const base: Config = JSON.parse(JSON.stringify(CONFIG_DEFAULTS)); + if (typeof window !== 'undefined' && window.location && window.URLSearchParams) { + try { + const params = new URLSearchParams(window.location.search); + const configOverrides = buildConfigFromUrlParams(params, base); + const initial = JSON.parse(JSON.stringify(base)); + mergeDeep(initial, configOverrides); + return initial; + } catch (e) { + console.error('Error processing config from URL, using defaults:', e); + return JSON.parse(JSON.stringify(CONFIG_DEFAULTS)); + } + } + return base; +} + +export const config = $state(getInitialConfig()); + +/** + * Resets one or more config values to their defaults using dot-separated paths. + */ +export function resetConfigToDefaults(paths: string | string[]) { + const pathList = Array.isArray(paths) ? paths : [paths]; + + for (const path of pathList) { + const defaultValue = getValueAtPath( + CONFIG_DEFAULTS as unknown as Record, + path + ); + if (defaultValue === undefined) { + console.warn(`resetConfigToDefaults: Unknown config path "${path}"`); + continue; + } + // Deep clone to avoid mutating the CONFIG_DEFAULTS reference + const cloned = deepClone(defaultValue); + const ok = setValueAtPath(config as unknown as Record, path, cloned); + if (!ok) { + console.warn(`resetConfigToDefaults: Failed to set value for path "${path}"`); + } + } +} + +function deepClone(value: T): T { + return JSON.parse(JSON.stringify(value)) as T; +} + +export function getConfigDefaultValue(path: string): unknown { + const val = getValueAtPath(CONFIG_DEFAULTS as unknown as Record, path); + return deepClone(val); +} + +export function equalsConfigDefault(path: string): boolean { + const current = getValueAtPath(config as unknown as Record, path); + const def = getValueAtPath(CONFIG_DEFAULTS as unknown as Record, path); + return valuesDeepEqual(current, def); +} + +function valuesDeepEqual(a: unknown, b: unknown): boolean { + try { + return JSON.stringify(a) === JSON.stringify(b); + } catch { + return a === b; + } +} + +function getValueAtPath(obj: Record, path: string): unknown { + const keys = path.split('.'); + let current: unknown = obj; + for (const key of keys) { + if ( + current === null || + current === undefined || + typeof current !== 'object' || + !(key in (current as Record)) + ) { + return undefined; + } + current = (current as Record)[key]; + } + return current; +} + +function setValueAtPath(target: Record, path: string, value: unknown): boolean { + const keys = path.split('.'); + let current: Record = target; + for (let i = 0; i < keys.length - 1; i++) { + const key = keys[i]; + const next = current[key]; + if (next === undefined || next === null || typeof next !== 'object' || Array.isArray(next)) { + // Only create an object if we are not overwriting a non-object path + current[key] = {}; + } + current = current[key] as Record; + } + const lastKey = keys[keys.length - 1]; + current[lastKey] = value as unknown; + return true; +} + +/** + * Flattens only the non-default values from an object by comparing against a defaults object. + * Returns a map of dot-separated paths to stringified values. + */ +function flattenNonDefault( + obj: Record, + defaults: Record, + prefix: string = '' +): Record { + const params: Record = {}; + for (const key in obj) { + if (!Object.prototype.hasOwnProperty.call(obj, key)) continue; + const newPrefix = prefix ? `${prefix}.${key}` : key; + const value = obj[key]; + const defaultValue = (defaults ?? {})[key]; + + if (value !== null && typeof value === 'object' && !Array.isArray(value)) { + const defaultChild = + defaultValue !== null && typeof defaultValue === 'object' && !Array.isArray(defaultValue) + ? (defaultValue as Record) + : ({} as Record); + const nested = flattenNonDefault(value as Record, defaultChild, newPrefix); + Object.assign(params, nested); + } else if (value !== undefined) { + if (defaultValue === undefined || !valuesDeepEqual(value, defaultValue)) { + params[newPrefix] = String(value); + } + } + } + return params; +} + +export function initSharedConfigUrlSync() { + if (typeof window !== 'undefined' && window.history && window.URL) { + $effect(() => { + const configSnapshot = $state.snapshot(config); + const flatParams = flattenNonDefault( + configSnapshot, + CONFIG_DEFAULTS as unknown as Record + ); + + const searchParamsString = new SvelteURLSearchParams(flatParams).toString(); + + const currentUrl = new SvelteURL(window.location.href); + currentUrl.search = searchParamsString; // This replaces the entire search string + + // Only call replaceState if the URL actually changed to avoid flooding history + if (window.location.href !== currentUrl.href) { + window.history.replaceState({}, '', currentUrl.toString()); + } + }); + } +} + +function ensureDatasetSupportsModelType() { + const datasetKey = config.data.dataset as keyof typeof DATASET_CONFIG_METADATA; + const meta = DATASET_CONFIG_METADATA[datasetKey]; + const allModels = MODEL_TYPES; + const supported = [ + ...('supportsModelTypes' in meta ? meta.supportsModelTypes : allModels) + ].toSorted(); + const isModelSupported = [...supported].includes(config.model.topology); + if (!isModelSupported) { + console.debug(`ensureDatasetSupportsModelType: setting model topology to ${supported[0]}`); + config.model.topology = supported[0] as ModelType; + } +} + +function datasetFromConfig(config: Config) { + ensureDatasetSupportsModelType(); + const generator = seededRandom(); + const dataset = buildToyDataset(config, generator); + const [dataloader, collateFn] = createDataloader(config, dataset, generator, null); + const blockSize = calculateBlockSize(config, dataloader); + const vocabSize = calculateVocabSize(dataset); + + const collatedData = getCollatedSampleData(dataset, collateFn, 4); + + const firstSample = collatedData.collated[0]; + + return { + dataset, + vocabSize, + blockSize, + tokenizer: dataset.tokenizer, + sampleData: { + hasPrompt: 'prompt' in firstSample && (firstSample.prompt?.length ?? 0) > 0, + samples: collatedData.samples, + collated: collatedData.collated + } + }; +} + +const currentDataset = $derived(datasetFromConfig(config)); + +export function getCurrentDataset() { + return currentDataset; +} + +const currentRunDataset = $derived.by(() => { + const currentRun = getCurrentRun(); + return currentRun?.config ? datasetFromConfig(currentRun.config) : null; +}); + +export function getCurrentRunDataset() { + return currentRunDataset; +} + +const latestRunDataset = $derived.by(() => { + const latestRun = getLatestRun(); + return latestRun?.config ? datasetFromConfig(latestRun.config) : null; +}); + +export function getLatestRunDataset() { + return latestRunDataset; +} + +const transformerHiddenSize = $derived( + config.model.transformer.headDim * config.model.transformer.attention.nKeyValueHeads +); + +const mlpIntermediateSize = $derived( + transformerHiddenSize * config.model.transformer.mlp.hiddenExpansionFactor +); + +export function getHiddenSize() { + return transformerHiddenSize; +} + +export function getMlpIntermediateSize() { + return mlpIntermediateSize; +} + +export function validateConfig() { + ensureDatasetSupportsModelType(); +} diff --git a/examples/piston-train-toy/src/lib/workspace/localStorage.svelte.ts b/examples/piston-train-toy/src/lib/workspace/localStorage.svelte.ts new file mode 100644 index 00000000..5422c14f --- /dev/null +++ b/examples/piston-train-toy/src/lib/workspace/localStorage.svelte.ts @@ -0,0 +1,98 @@ +import { tick } from 'svelte'; + +export class LocalStorage { + #key: string; + #version = $state(0); + #listeners = 0; + #value: T | undefined; + + #handler = (e: StorageEvent) => { + if (e.storageArea !== localStorage) return; + if (e.key !== this.#key) return; + + this.#version += 1; + }; + + constructor(key: string, initial?: T) { + this.#key = key; + this.#value = initial; + + if (typeof localStorage !== 'undefined') { + if (localStorage.getItem(key) == null) { + localStorage.setItem(key, JSON.stringify(initial)); + } + } + } + + get current() { + // eslint-disable-next-line @typescript-eslint/no-unused-expressions + this.#version; + + const localStorageItem = + typeof localStorage !== 'undefined' ? localStorage.getItem(this.#key) : null; + const root = localStorageItem !== null ? JSON.parse(localStorageItem) : this.#value; + + const proxies = new WeakMap(); + + const proxy = (value: unknown) => { + if (typeof value !== 'object' || value === null) { + return value; + } + + let p = proxies.get(value); + + if (!p) { + p = new Proxy(value, { + get: (target, property) => { + // eslint-disable-next-line @typescript-eslint/no-unused-expressions + this.#version; + return proxy(Reflect.get(target, property)); + }, + set: (target, property, value) => { + this.#version += 1; + Reflect.set(target, property, value); + + if (typeof localStorage !== 'undefined') { + localStorage.setItem(this.#key, JSON.stringify(root)); + } + + return true; + } + }); + + proxies.set(value, p); + } + + return p; + }; + + if ($effect.tracking()) { + $effect(() => { + if (this.#listeners === 0) { + window.addEventListener('storage', this.#handler); + } + + this.#listeners += 1; + + return () => { + tick().then(() => { + this.#listeners -= 1; + if (this.#listeners === 0) { + window.removeEventListener('storage', this.#handler); + } + }); + }; + }); + } + + return proxy(root); + } + + set current(value) { + if (typeof localStorage !== 'undefined') { + localStorage.setItem(this.#key, JSON.stringify(value)); + } + + this.#version += 1; + } +} diff --git a/examples/piston-train-toy/src/lib/workspace/runs.svelte.ts b/examples/piston-train-toy/src/lib/workspace/runs.svelte.ts new file mode 100644 index 00000000..ad87913e --- /dev/null +++ b/examples/piston-train-toy/src/lib/workspace/runs.svelte.ts @@ -0,0 +1,210 @@ +import { generateMemorableName } from '$lib/workspace/utils'; +import { SvelteMap, SvelteSet } from 'svelte/reactivity'; + +import { type Config } from './config'; + +export type BaseStepData = { step: number }; + +export type Point = BaseStepData & { y: number }; + +export type StepData = Point; + +export type MetricData = { + metricName: string; + data: StepData[]; +}; + +export type RunData = { + runId: string; + color: string; + config: Config; + metrics: SvelteMap; + step: number; + lastUpdated: number; + createdAt: number; +}; + +export type RunMeta = { + runId: string | null; + config: Config | null; +}; + +// A palette of distinct colors for runs +const RUN_COLORS = ['#f5493b', '#dfa300', '#3bbc4a', '#00b7c0', '#4475f6', '#cc5cc5']; + +export const runsMap = new SvelteMap(); +export const runCounter = $state({ current: 0 }); +export const currentRun = $state<{ current: RunMeta | null }>({ + current: null +}); + +export function getRuns(): ReadonlyArray { + return [...runsMap.values()].sort((a, b) => a.runId.localeCompare(b.runId)); +} + +export function getAllMetricNames(): ReadonlyArray { + const names = new SvelteSet(); + for (const run of runsMap.values()) { + for (const metricName of run.metrics.keys()) { + names.add(metricName); + } + } + return [...names].sort(); +} + +/** + * Gets all metric names from the last n runs. + * @param n - The number of recent runs to consider + * @returns Array of unique metric names sorted alphabetically + */ +export function getMetricNamesFromLastNRuns(n: number): ReadonlyArray { + const names = new SvelteSet(); + const recentRuns = getLastNRuns(n); + for (const run of recentRuns) { + for (const metricName of run.metrics.keys()) { + names.add(metricName); + } + } + return [...names].sort(); +} + +export function getCurrentRun(): RunMeta | null { + return currentRun.current; +} + +export function getLatestRun(): RunMeta | null { + if (currentRun.current !== null) { + return currentRun.current; + } + + const allRuns = [...runsMap.values()]; + allRuns.sort((a, b) => b.lastUpdated - a.lastUpdated); + return allRuns[0] ?? null; +} + +/** + * Gets the last n runs sorted by most recently updated, ensuring the current run is always + * included. + */ +export function getLastNRuns(n: number): ReadonlyArray { + const allRuns = [...runsMap.values()]; + + // Sort by lastUpdated descending (most recent first) + allRuns.sort((a, b) => b.lastUpdated - a.lastUpdated); + + // If we have fewer runs than requested, return all of them + if (allRuns.length <= n) { + return allRuns; + } + + return allRuns.slice(0, n); +} + +export function newRun(config: Config, id?: string): RunData { + if (id && runsMap.has(id)) { + throw new Error(`Run with id ${id} already exists`); + } + + const runId = id ?? generateMemorableName(runCounter.current); + const color = RUN_COLORS[runCounter.current % RUN_COLORS.length]; + const now = Date.now(); + // Find baseline as immediately-previous-by-creation + const existingRuns = [...runsMap.values()]; + existingRuns.sort((a, b) => (a.createdAt ?? a.lastUpdated) - (b.createdAt ?? b.lastUpdated)); + + const run = { + runId: runId, + config, + color: color, + metrics: new SvelteMap(), + step: 0, + lastUpdated: now, + createdAt: now + }; + runsMap.set(runId, run); + runCounter.current += 1; + currentRun.current = { runId: runId, config: config }; + return run; +} + +export function endRun() { + currentRun.current = null; +} + +/** + * Logs metric data for a specific step in a run. + */ +export function log( + runId: string, + data: { [metricName: string]: number }, + { step }: { step?: number } = {} +): void { + const run = runsMap.get(runId); + const determinedStep = step !== undefined ? step : (runsMap.get(runId)?.step ?? 0); + + // Create run if it doesn't exist + if (!run) { + throw new Error(`Run with id ${runId} does not exist`); + } + + const currentStep = determinedStep; + + // Update metrics for the specified step + for (const [metricName, value] of Object.entries(data)) { + let metric = run.metrics.get(metricName); + if (!metric) { + metric = { + metricName, + data: [] + }; + run.metrics.set(metricName, metric); + } + + const stepData: StepData = { step: currentStep, y: value }; + + const updatedMetric = { + ...metric, + data: [...metric.data, stepData].sort((a: StepData, b: StepData) => a.step - b.step) + }; + + run.metrics.set(metricName, updatedMetric); + } + + // Update step counter + if (step === undefined) { + run.step += 1; + } else if (step > run.step) { + run.step = step; + } + + run.lastUpdated = Date.now(); + runsMap.set(runId, run); +} + +export type LogFn = typeof log; + +export function resetWorkspace(): void { + runsMap.clear(); + runCounter.current = 0; + currentRun.current = null; + console.debug('[workspaceState] Reset.'); +} + +// Group metrics by prefix (everything before the first '/') +export function getMetricGroups(metricNames: ReadonlyArray): Record { + const groups: Record = {}; + + const allMetricNames = metricNames; + + allMetricNames.forEach((metricName) => { + const parts = metricName.split('/'); + const groupName = parts.length > 1 ? parts[0] : 'default'; + + if (!groups[groupName]) { + groups[groupName] = []; + } + groups[groupName].push(metricName); + }); + + return groups; +} diff --git a/examples/piston-train-toy/src/lib/workspace/ui.svelte.ts b/examples/piston-train-toy/src/lib/workspace/ui.svelte.ts new file mode 100644 index 00000000..b37823ce --- /dev/null +++ b/examples/piston-train-toy/src/lib/workspace/ui.svelte.ts @@ -0,0 +1,173 @@ +import { config } from './config.svelte'; +import { LocalStorage } from './localStorage.svelte'; +import { newRun } from './runs.svelte'; +import { + trainingState, + workerReady, + workerStartTraining, + workerStopTraining +} from './workers.svelte'; + +export const isMobile = $state({ current: false }); +export const activeTab: { current: 'about' | 'metrics' } = $state({ + current: 'about' +}); +export const hasWebGPU = $state({ current: false }); +export const browserInfo: { + current: { + type: + | 'chrome' + | 'edge' + | 'brave' + | 'arc' + | 'opera' + | 'vivaldi' + | 'safari' + | 'firefox' + | 'unknown'; + platform: 'ios' | 'macos' | 'windows' | 'android' | 'linux' | 'other'; + }; +} = $state({ + current: { type: 'unknown', platform: 'other' } +}); + +export const setupUI = () => { + // Check for WebGPU support + hasWebGPU.current = 'gpu' in navigator; + + // Browser/platform detection (best-effort; UA-CH not universally available yet) + const ua = navigator.userAgent.toLowerCase(); + const vendor = navigator.vendor?.toLowerCase?.() ?? ''; + + // Platform + let platform: 'ios' | 'macos' | 'windows' | 'android' | 'linux' | 'other' = 'other'; + if (/iphone|ipad|ipod/.test(ua)) platform = 'ios'; + else if (/macintosh|mac os x/.test(ua)) platform = 'macos'; + else if (/windows nt/.test(ua)) platform = 'windows'; + else if (/android/.test(ua)) platform = 'android'; + else if (/linux/.test(ua)) platform = 'linux'; + + // Chromium-family checks + // Distinguish some popular Chromium variants before generic Chrome + let type: + | 'chrome' + | 'edge' + | 'brave' + | 'arc' + | 'opera' + | 'vivaldi' + | 'safari' + | 'firefox' + | 'unknown' = 'unknown'; + if (/edg\//.test(ua)) type = 'edge'; + else if (/vivaldi/.test(ua)) type = 'vivaldi'; + else if (/opr\//.test(ua)) type = 'opera'; + else if (/brave/.test(ua)) type = 'brave'; + else if (/arc\//.test(ua)) type = 'arc'; + else if (/firefox/.test(ua)) type = 'firefox'; + else if (/safari/.test(ua) && /apple/.test(vendor) && !/chrome|crios|android/.test(ua)) + type = 'safari'; + else if (/chrome|crios/.test(ua)) type = 'chrome'; + + browserInfo.current = { type, platform }; + + const mediaQuery = window.matchMedia('(min-width: 40rem)'); + isMobile.current = !mediaQuery.matches; + + // Set configOpen based on media query if not already set by user + if (configOpen.current === null) { + configOpen.current = mediaQuery.matches; + } + + // Listen for changes in screen size + const handleMediaChange = (e: MediaQueryListEvent) => { + isMobile.current = !e.matches; + + // If switching to mobile and config is open, close it and reset tab + if (isMobile.current && configOpen.current) { + configOpen.current = false; + activeTab.current = 'about'; + } + }; + + mediaQuery.addEventListener('change', handleMediaChange); + + return () => { + mediaQuery.removeEventListener('change', handleMediaChange); + }; +}; + +// Function to handle tab selection with mobile behavior +export function selectTab(tabName: 'about' | 'metrics') { + activeTab.current = tabName; + if (isMobile.current && configOpen.current) { + configOpen.current = false; + } +} + +const iconStrokeWidth = $derived(isMobile ? 2 : 2.5); + +export function getIconStrokeWidth() { + return iconStrokeWidth; +} + +// Initialize sectionsOpen from localStorage or use defaults +export const controlSectionsOpen = new LocalStorage('controlSectionsOpen', { + training: true, + task: true, + model: true, + optimizer: true, + advanced: false +}); + +export function toggleControlSection(sectionName: keyof typeof controlSectionsOpen.current) { + controlSectionsOpen.current[sectionName] = !controlSectionsOpen.current[sectionName]; +} + +export const metricsSectionsOpen = new LocalStorage('metricsSectionsOpen', {}); + +export function toggleMetricsSection(sectionName: string) { + metricsSectionsOpen.current[sectionName] = !(metricsSectionsOpen.current[sectionName] ?? true); +} + +// Visibility state for per-metric charts (user overrides only) +export const metricVisibility = new LocalStorage('metricVisibility', {}); + +// Initialize configOpen from localStorage with no default (null means use media query) +export const configOpen = new LocalStorage('configOpen', null); + +export function switchToMetrics() { + selectTab('metrics'); +} + +export function toggleConfig() { + configOpen.current = !configOpen.current; +} + +// Function to start training +export function startTraining() { + if (trainingState.current !== 'stopped' || !workerReady.current) return; + + trainingState.current = 'training'; + newRun(JSON.parse(JSON.stringify(config))); + + if (isMobile.current && configOpen.current) { + configOpen.current = false; + } + + if (activeTab.current === 'about') { + switchToMetrics(); + } + + workerStartTraining(); +} + +// Function to stop training +export async function stopTraining() { + await workerStopTraining(); +} + +export async function restartTraining() { + await stopTraining(); + startTraining(); +} diff --git a/examples/piston-train-toy/src/lib/workspace/utils.ts b/examples/piston-train-toy/src/lib/workspace/utils.ts new file mode 100644 index 00000000..9ad244c5 --- /dev/null +++ b/examples/piston-train-toy/src/lib/workspace/utils.ts @@ -0,0 +1,39 @@ +import { adjectives, animals, uniqueNamesGenerator } from 'unique-names-generator'; + +export function generateMemorableName(number: number) { + // by default uniqueNamesGenerator picks one word per dictionary + const twoWord = uniqueNamesGenerator({ + dictionaries: [adjectives, animals], + separator: '-', + style: 'lowerCase', + length: 2 + }); + return `${twoWord}-${number}`; +} + +/** + * Returns a new array sorted so that any items whose key is found in `priorityKeys` + * appear first in the specified order, followed by the remaining items sorted by + * their key alphabetically. + * + * Example: + * sortWithPriority(['b', 'a', 'c'], (x) => x, ['c']) -> ['c', 'a', 'b'] + */ +export function sortWithPriority( + items: ReadonlyArray, + getKey: (item: T) => string, + priorityKeys: ReadonlyArray +): T[] { + const priorityIndex = new Map(); + for (let i = 0; i < priorityKeys.length; i++) { + priorityIndex.set(priorityKeys[i], i); + } + return [...items].sort((a, b) => { + const ka = getKey(a); + const kb = getKey(b); + const ia = priorityIndex.has(ka) ? (priorityIndex.get(ka) as number) : Number.POSITIVE_INFINITY; + const ib = priorityIndex.has(kb) ? (priorityIndex.get(kb) as number) : Number.POSITIVE_INFINITY; + if (ia !== ib) return ia - ib; + return ka.localeCompare(kb); + }); +} diff --git a/examples/piston-train-toy/src/lib/workspace/workers.svelte.ts b/examples/piston-train-toy/src/lib/workspace/workers.svelte.ts new file mode 100644 index 00000000..80d0de77 --- /dev/null +++ b/examples/piston-train-toy/src/lib/workspace/workers.svelte.ts @@ -0,0 +1,110 @@ +import { currentRun, log } from './runs.svelte'; + +// Train state +let trainWorker: Worker | null = $state(null); +export const workerReady = $state({ current: false }); +export const workerVersion = $state({ current: 0 }); +export const trainingState = $state<{ current: 'training' | 'stopped' }>({ + current: 'stopped' +}); + +export async function initializeWorker() { + return new Promise((resolve, reject) => { + try { + // Create the dedicated module worker + // eslint-disable-next-line svelte/prefer-svelte-reactivity + trainWorker = new Worker(new URL('$lib/train/moduleWorker.ts', import.meta.url), { + type: 'module', + name: 'moduleWorker' + }); + + console.log('[Main] Module worker created successfully.'); + + trainWorker.onmessage = (event) => { + const { type, ...data } = event.data; + + switch (type) { + case 'ready': + console.log('[Main] Worker is ready'); + resolve(); + workerReady.current = true; + workerVersion.current += 1; + break; + + case 'metrics': { + // Handle training metric logs + if (!data.runId || !data.data) { + console.error('[Main] Invalid metrics data:', data); + return; + } + const step = data.metadata?.step as number | undefined; + const combinedMetrics: Record = {}; + for (const [metricName, value] of Object.entries(data.data)) { + combinedMetrics[metricName] = value as number; + } + log(data.runId, combinedMetrics, { step }); + break; + } + case 'complete': + console.log(`[Main] Training completed for run ${data.runId}`); + trainingState.current = 'stopped'; + currentRun.current = null; + break; + + case 'error': + console.error(`[Main] Training error for run ${data.runId}:`, data.message); + workerStopTraining(); + break; + } + }; + + trainWorker.onerror = (event) => { + console.error('[Main] Worker onerror:', event); + reject(new Error(event.error)); + }; + } catch (error) { + console.error('[Main] Failed to create worker:', error); + reject(error); + } + }); +} + +export function workerStartTraining() { + if (!trainWorker) { + throw new Error('Worker not initialized'); + } + + const run = currentRun.current; + + if (!run) { + throw new Error('No current run'); + } + + trainWorker.postMessage({ + type: 'start', + data: JSON.parse(JSON.stringify(run)) + }); + + trainingState.current = 'training'; +} + +export async function workerStopTraining() { + if (!trainWorker || trainingState.current === 'stopped') return; + + // For now, we'll just terminate and recreate the worker + // In a more sophisticated implementation, we'd send a stop message + trainWorker.terminate(); + workerReady.current = false; + trainingState.current = 'stopped'; + currentRun.current = null; + + // Recreate worker + await initializeWorker(); +} + +export function cleanupWorker() { + if (trainWorker) { + trainWorker.terminate(); + trainWorker = null; + } +} From 64546fbb92c59dfbc5d6403871cad58878872a7d Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 26 Oct 2025 19:23:39 -0600 Subject: [PATCH 532/590] Add initial UI --- .../src/lib/attachments/echarts.svelte.ts | 236 ++++++ .../src/lib/components/ActionButton.svelte | 46 ++ .../src/lib/components/ChevronIcon.svelte | 20 + .../src/lib/components/CuteLogo.svelte | 58 ++ .../src/lib/components/KatexBlock.svelte | 43 + .../src/lib/components/ToggleChips.svelte | 41 + .../lib/components/UserGuideTooltip.svelte | 36 + .../controls/ActivationPicker.svelte | 176 +++++ .../components/controls/BorderedGroup.svelte | 61 ++ .../lib/components/controls/Citations.svelte | 28 + .../controls/CollapsibleSection.svelte | 59 ++ .../lib/components/controls/Controls.svelte | 569 +++++++++++++ .../controls/ControlsStatistic.svelte | 32 + .../controls/DatasetControls.svelte | 110 +++ .../components/controls/DatasetSample.svelte | 86 ++ .../lib/components/controls/FormLabel.svelte | 17 + .../components/controls/NumberInput.svelte | 71 ++ .../controls/ResetValueButton.svelte | 22 + .../components/controls/SelectDataset.svelte | 119 +++ .../src/lib/components/controls/Slider.svelte | 745 ++++++++++++++++++ .../lib/components/controls/TextInput.svelte | 45 ++ .../components/controls/ToggleGroup.svelte | 56 ++ .../controls/checkbox/CheckboxIcon.svelte | 21 + .../controls/checkbox/CheckboxInput.svelte | 55 ++ .../controls/radio/RadioGroupInput.svelte | 64 ++ .../controls/radio/RadioIcon.svelte | 31 + .../controls/radio/RadioInput.svelte | 52 ++ .../controls/select/SelectInput.svelte | 367 +++++++++ .../select/SelectModelTopology.svelte | 133 ++++ .../select/SelectWithCitations.svelte | 66 ++ .../lib/components/footnotes/Footnote.svelte | 46 ++ .../footnotes/FootnotesProvider.svelte | 42 + .../components/metrics/MetricsSection.svelte | 65 ++ .../lib/components/metrics/RunChart.svelte | 323 ++++++++ .../src/lib/train/tokenizer.ts | 9 + .../piston-train-toy/src/routes/+page.svelte | 244 ++++++ .../src/routes/tabs/About.svelte | 310 ++++++++ .../src/routes/tabs/Metrics.svelte | 73 ++ 38 files changed, 4577 insertions(+) create mode 100644 examples/piston-train-toy/src/lib/attachments/echarts.svelte.ts create mode 100644 examples/piston-train-toy/src/lib/components/ActionButton.svelte create mode 100644 examples/piston-train-toy/src/lib/components/ChevronIcon.svelte create mode 100644 examples/piston-train-toy/src/lib/components/CuteLogo.svelte create mode 100644 examples/piston-train-toy/src/lib/components/KatexBlock.svelte create mode 100644 examples/piston-train-toy/src/lib/components/ToggleChips.svelte create mode 100644 examples/piston-train-toy/src/lib/components/UserGuideTooltip.svelte create mode 100644 examples/piston-train-toy/src/lib/components/controls/ActivationPicker.svelte create mode 100644 examples/piston-train-toy/src/lib/components/controls/BorderedGroup.svelte create mode 100644 examples/piston-train-toy/src/lib/components/controls/Citations.svelte create mode 100644 examples/piston-train-toy/src/lib/components/controls/CollapsibleSection.svelte create mode 100644 examples/piston-train-toy/src/lib/components/controls/Controls.svelte create mode 100644 examples/piston-train-toy/src/lib/components/controls/ControlsStatistic.svelte create mode 100644 examples/piston-train-toy/src/lib/components/controls/DatasetControls.svelte create mode 100644 examples/piston-train-toy/src/lib/components/controls/DatasetSample.svelte create mode 100644 examples/piston-train-toy/src/lib/components/controls/FormLabel.svelte create mode 100644 examples/piston-train-toy/src/lib/components/controls/NumberInput.svelte create mode 100644 examples/piston-train-toy/src/lib/components/controls/ResetValueButton.svelte create mode 100644 examples/piston-train-toy/src/lib/components/controls/SelectDataset.svelte create mode 100644 examples/piston-train-toy/src/lib/components/controls/Slider.svelte create mode 100644 examples/piston-train-toy/src/lib/components/controls/TextInput.svelte create mode 100644 examples/piston-train-toy/src/lib/components/controls/ToggleGroup.svelte create mode 100644 examples/piston-train-toy/src/lib/components/controls/checkbox/CheckboxIcon.svelte create mode 100644 examples/piston-train-toy/src/lib/components/controls/checkbox/CheckboxInput.svelte create mode 100644 examples/piston-train-toy/src/lib/components/controls/radio/RadioGroupInput.svelte create mode 100644 examples/piston-train-toy/src/lib/components/controls/radio/RadioIcon.svelte create mode 100644 examples/piston-train-toy/src/lib/components/controls/radio/RadioInput.svelte create mode 100644 examples/piston-train-toy/src/lib/components/controls/select/SelectInput.svelte create mode 100644 examples/piston-train-toy/src/lib/components/controls/select/SelectModelTopology.svelte create mode 100644 examples/piston-train-toy/src/lib/components/controls/select/SelectWithCitations.svelte create mode 100644 examples/piston-train-toy/src/lib/components/footnotes/Footnote.svelte create mode 100644 examples/piston-train-toy/src/lib/components/footnotes/FootnotesProvider.svelte create mode 100644 examples/piston-train-toy/src/lib/components/metrics/MetricsSection.svelte create mode 100644 examples/piston-train-toy/src/lib/components/metrics/RunChart.svelte create mode 100644 examples/piston-train-toy/src/lib/train/tokenizer.ts create mode 100644 examples/piston-train-toy/src/routes/+page.svelte create mode 100644 examples/piston-train-toy/src/routes/tabs/About.svelte create mode 100644 examples/piston-train-toy/src/routes/tabs/Metrics.svelte diff --git a/examples/piston-train-toy/src/lib/attachments/echarts.svelte.ts b/examples/piston-train-toy/src/lib/attachments/echarts.svelte.ts new file mode 100644 index 00000000..2ba10bd5 --- /dev/null +++ b/examples/piston-train-toy/src/lib/attachments/echarts.svelte.ts @@ -0,0 +1,236 @@ +import type { Attachment } from 'svelte/attachments'; + +import * as echarts from 'echarts/core'; + +type EChartsAttachmentParams = { + opts: () => echarts.EChartsCoreOption | null; + // Optional setup hook to programmatically configure the instance (e.g., event bridging) + // May return a cleanup function that will be called on detach. + setup?: (chart: echarts.ECharts, getPeers: undefined) => void | (() => void); +}; + +export function setupAxisSync(chart: echarts.ECharts, getPeers: () => echarts.ECharts[]) { + let isRelaying = false; + chart.on('updateAxisPointer', (evt) => { + if (isRelaying) return; + const e = evt as { axesInfo?: Array<{ axisDim: string; value: number }> }; + const axesInfo = e?.axesInfo; + if (!axesInfo || axesInfo.length === 0) return; + const xInfo = axesInfo.find((a) => a.axisDim === 'x'); + if (!xInfo) return; + + const catIndex = Math.max(0, Math.floor(xInfo.value ?? 0)); + const opt = chart.getOption?.() as + | { xAxis?: Array<{ data?: (number | string)[] }> } + | undefined; + const cats = opt?.xAxis?.[0]?.data ?? []; + const step = Number(cats[catIndex] ?? catIndex); + + isRelaying = true; + for (const peer of getPeers()) { + const hit = findPeerDataIndexByStep(peer, step); + peer.dispatchAction({ + type: 'updateAxisPointer', + seriesIndex: hit?.seriesIndex ?? 0, + dataIndex: hit?.dataIndex ?? catIndex + }); + } + Promise.resolve().then(() => { + isRelaying = false; + }); + }); +} + +// Binary-search utilities for sorted numeric step arrays +export function exactIndex(steps: number[], target: number): number { + let lo = 0, + hi = steps.length - 1; + while (lo <= hi) { + const mid = (lo + hi) >> 1; + const v = steps[mid]; + if (v === target) return mid; + if (v < target) lo = mid + 1; + else hi = mid - 1; + } + return -1; +} + +export function nearestIndex(steps: number[], target: number): number { + if (steps.length === 0) return -1; + const first = steps[0], + last = steps[steps.length - 1]; + if (target < first || target > last) return -1; + let lo = 0, + hi = steps.length; + while (lo < hi) { + const mid = (lo + hi) >> 1; + if (steps[mid] < target) lo = mid + 1; + else hi = mid; + } + if (lo === 0) return 0; + if (lo === steps.length) return steps.length - 1; + return target - steps[lo - 1] <= steps[lo] - target ? lo - 1 : lo; +} + +// Extract numeric x/step from an ECharts updateAxisPointer event +export function extractStepFromAxisPointerEvent(evt: unknown): number | null { + const e = evt as { axesInfo?: Array<{ axisDim: string; value: number }> } | undefined; + const xInfo = e?.axesInfo?.find((a) => a.axisDim === 'x'); + const step = Number(xInfo?.value); + return Number.isFinite(step) ? step : null; +} + +function extractXValue(datum: unknown): number | null { + if (Array.isArray(datum)) { + const x = Number(datum[0]); + return Number.isFinite(x) ? x : null; + } + if (datum && typeof datum === 'object') { + const obj = datum as { value?: unknown; x?: unknown; step?: unknown }; + if (Array.isArray(obj.value)) { + const x = Number(obj.value[0]); + return Number.isFinite(x) ? x : null; + } + const xCandidate = obj.x ?? obj.step; + if (typeof xCandidate === 'number') return xCandidate; + if (typeof xCandidate === 'string') { + const parsed = Number(xCandidate); + return Number.isFinite(parsed) ? parsed : null; + } + } + return null; +} + +function linearSearchByX(data: unknown[], targetStep: number): number { + for (let i = 0; i < data.length; i++) { + const x = extractXValue(data[i]); + if (x === targetStep) return i; + } + return -1; +} + +function binarySearchByX(data: unknown[], targetStep: number): number { + if (data.length === 0) return -1; + const first = extractXValue(data[0]); + const last = extractXValue(data[data.length - 1]); + if (first === null || last === null) return linearSearchByX(data, targetStep); + + const ascending = last >= first; + let lo = 0; + let hi = data.length - 1; + while (lo <= hi) { + const mid = lo + ((hi - lo) >> 1); + const x = extractXValue(data[mid]); + if (x === null) return linearSearchByX(data, targetStep); + if (x === targetStep) return mid; + if (ascending ? x < targetStep : x > targetStep) lo = mid + 1; + else hi = mid - 1; + } + return -1; +} + +function getSeriesArrayFromOption(opt: unknown): unknown[] { + const o = opt as { series?: unknown[] } | undefined; + return Array.isArray(o?.series) ? (o!.series as unknown[]) : []; +} + +function getDataArrayFromSeries(seriesItem: unknown): unknown[] { + const s = seriesItem as { data?: unknown[] } | undefined; + return Array.isArray(s?.data) ? (s!.data as unknown[]) : []; +} + +export function findPeerDataIndexByStep( + peer: echarts.ECharts, + step: number +): { seriesIndex: number; dataIndex: number } | null { + const opt = peer.getOption() as unknown; + const seriesArr = getSeriesArrayFromOption(opt); + if (seriesArr.length === 0) return null; + const seriesIndex = seriesArr.length - 1; // highest series index + const data = getDataArrayFromSeries(seriesArr[seriesIndex]); + if (data.length === 0) return null; + const dataIndex = binarySearchByX(data, step); + if (dataIndex < 0) return null; + return { seriesIndex, dataIndex }; +} + +export default function createEChartsAttachment(params: EChartsAttachmentParams): Attachment { + return (node: Element) => { + if (!(node instanceof HTMLDivElement)) { + throw new Error('ECharts attachment requires a div element'); + } + + const chart = echarts.init(node); + const getPeers = undefined; + + // Allow caller to set up custom behavior (e.g., axis-pointer mapping) + let setupCleanup: (() => void) | undefined; + const maybeCleanup = params.setup?.(chart, getPeers); + if (typeof maybeCleanup === 'function') setupCleanup = maybeCleanup; + + const resizeObserver = new ResizeObserver(() => { + chart.resize(); + }); + resizeObserver.observe(node); + + $effect(() => { + const options = params.opts?.(); + if (options) { + chart.setOption(options, { + notMerge: false, + replaceMerge: ['series'], + lazyUpdate: false + }); + } + }); + + return () => { + resizeObserver.unobserve(node); + setupCleanup?.(); + chart.dispose(); + }; + }; +} + +type MoveDetail = { + sourceId: string; + runId: string; + step: number; +}; + +type ClearDetail = { + sourceId: string; +}; + +const MOVE_EVENT = 'run-pointer:move'; +const CLEAR_EVENT = 'run-pointer:clear'; + +const bus: EventTarget = new EventTarget(); + +export function publishMove(detail: MoveDetail): void { + bus.dispatchEvent(new CustomEvent(MOVE_EVENT, { detail })); +} + +export function publishClear(detail: ClearDetail): void { + bus.dispatchEvent(new CustomEvent(CLEAR_EVENT, { detail })); +} + +export function subscribe( + onMove?: (detail: MoveDetail) => void, + onClear?: (detail: ClearDetail) => void +): () => void { + const moveListener = (e: Event) => { + const ce = e as CustomEvent; + if (onMove) onMove(ce.detail); + }; + const clearListener = (e: Event) => { + const ce = e as CustomEvent; + if (onClear) onClear(ce.detail); + }; + if (onMove) bus.addEventListener(MOVE_EVENT, moveListener as EventListener); + if (onClear) bus.addEventListener(CLEAR_EVENT, clearListener as EventListener); + return () => { + if (onMove) bus.removeEventListener(MOVE_EVENT, moveListener as EventListener); + if (onClear) bus.removeEventListener(CLEAR_EVENT, clearListener as EventListener); + }; +} diff --git a/examples/piston-train-toy/src/lib/components/ActionButton.svelte b/examples/piston-train-toy/src/lib/components/ActionButton.svelte new file mode 100644 index 00000000..0f711b67 --- /dev/null +++ b/examples/piston-train-toy/src/lib/components/ActionButton.svelte @@ -0,0 +1,46 @@ + + + diff --git a/examples/piston-train-toy/src/lib/components/ChevronIcon.svelte b/examples/piston-train-toy/src/lib/components/ChevronIcon.svelte new file mode 100644 index 00000000..5111db89 --- /dev/null +++ b/examples/piston-train-toy/src/lib/components/ChevronIcon.svelte @@ -0,0 +1,20 @@ + + + + + diff --git a/examples/piston-train-toy/src/lib/components/CuteLogo.svelte b/examples/piston-train-toy/src/lib/components/CuteLogo.svelte new file mode 100644 index 00000000..ec2a4261 --- /dev/null +++ b/examples/piston-train-toy/src/lib/components/CuteLogo.svelte @@ -0,0 +1,58 @@ + + + + + + + + + + + diff --git a/examples/piston-train-toy/src/lib/components/KatexBlock.svelte b/examples/piston-train-toy/src/lib/components/KatexBlock.svelte new file mode 100644 index 00000000..efdc1c51 --- /dev/null +++ b/examples/piston-train-toy/src/lib/components/KatexBlock.svelte @@ -0,0 +1,43 @@ + + +{#if processed.isHtml} + + {@html processed.output} +{:else} + {processed.output} +{/if} diff --git a/examples/piston-train-toy/src/lib/components/ToggleChips.svelte b/examples/piston-train-toy/src/lib/components/ToggleChips.svelte new file mode 100644 index 00000000..1d52745b --- /dev/null +++ b/examples/piston-train-toy/src/lib/components/ToggleChips.svelte @@ -0,0 +1,41 @@ + + +
    + {#each items as name (name)} + + {/each} +
    diff --git a/examples/piston-train-toy/src/lib/components/UserGuideTooltip.svelte b/examples/piston-train-toy/src/lib/components/UserGuideTooltip.svelte new file mode 100644 index 00000000..ae619b3a --- /dev/null +++ b/examples/piston-train-toy/src/lib/components/UserGuideTooltip.svelte @@ -0,0 +1,36 @@ + + +
    + + +
    diff --git a/examples/piston-train-toy/src/lib/components/controls/ActivationPicker.svelte b/examples/piston-train-toy/src/lib/components/controls/ActivationPicker.svelte new file mode 100644 index 00000000..3fdf518b --- /dev/null +++ b/examples/piston-train-toy/src/lib/components/controls/ActivationPicker.svelte @@ -0,0 +1,176 @@ + + +
    + +
    + +
    +
    + + + + + + + + +
    + {#if onReset} + + {/if} +
    +
    +
    diff --git a/examples/piston-train-toy/src/lib/components/controls/BorderedGroup.svelte b/examples/piston-train-toy/src/lib/components/controls/BorderedGroup.svelte new file mode 100644 index 00000000..78ff9098 --- /dev/null +++ b/examples/piston-train-toy/src/lib/components/controls/BorderedGroup.svelte @@ -0,0 +1,61 @@ + + +
    +
    + {#if title} +
    +

    + +

    + {#if citations} + + {/if} +
    + {:else} + {@render header?.()} + {/if} + {#if action} + {@render action?.()} + {/if} +
    + {#if contentClass} +
    + {@render children?.()} +
    + {:else} + {@render children?.()} + {/if} +
    diff --git a/examples/piston-train-toy/src/lib/components/controls/Citations.svelte b/examples/piston-train-toy/src/lib/components/controls/Citations.svelte new file mode 100644 index 00000000..3787ed24 --- /dev/null +++ b/examples/piston-train-toy/src/lib/components/controls/Citations.svelte @@ -0,0 +1,28 @@ + + + + {#each citations?.entries ?? [] as e, i (e.name)} + {#if i > 0}, + {/if} + {#if e.url} + + {e.name} + {:else} + {e.name} + {/if} + {/each}{#if citations?.extra}{citations.extra}{/if} + diff --git a/examples/piston-train-toy/src/lib/components/controls/CollapsibleSection.svelte b/examples/piston-train-toy/src/lib/components/controls/CollapsibleSection.svelte new file mode 100644 index 00000000..b97dff66 --- /dev/null +++ b/examples/piston-train-toy/src/lib/components/controls/CollapsibleSection.svelte @@ -0,0 +1,59 @@ + + +
    +
    { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + handleClick(); + } + }} + > +

    {title}

    + +
    + + {#if isOpen} +
    + {@render children?.()} +
    + {/if} +
    diff --git a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte new file mode 100644 index 00000000..3984f70c --- /dev/null +++ b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte @@ -0,0 +1,569 @@ + + +
    + + toggleControlSection('task')} + contentClass={collapsibleSectionClass} + > + + + + + toggleControlSection('model')} + contentClass={collapsibleSectionClass} + > + +
    + + {getHiddenSize()} + + + {currentDataset.vocabSize} + + {#if config.model.topology === 'encoder-decoder'} + {@const blockSize = currentDataset.blockSize as { source: number; target: number }} + + {blockSize.source} + + + {blockSize.target} + + {:else} + + {currentDataset.blockSize} + + {/if} +
    + + resetConfigToDefaults('model.topology')} + /> + + {#if config.model.topology !== 'encoder-decoder'} + resetConfigToDefaults('model.layers')} + /> + {:else} + resetConfigToDefaults('model.encoderDecoder.encoderLayers')} + /> + resetConfigToDefaults('model.encoderDecoder.decoderLayers')} + /> + {/if} + + resetConfigToDefaults('model.transformer.headDim')} + /> + + resetConfigToDefaults('model.transformer.positionalEncoding.present')} + /> + + + resetConfigToDefaults('model.transformer.attention.present')} + > + resetConfigToDefaults('model.transformer.attention.nKeyValueHeads')} + /> + + + + resetConfigToDefaults('model.transformer.mlp.present')} + > + + {getMlpIntermediateSize()} + + resetConfigToDefaults('model.transformer.mlp.activation')} + /> + resetConfigToDefaults('model.transformer.mlp.hiddenExpansionFactor')} + /> + + + + + resetConfigToDefaults('model.layerNormalization.transformer.present')} + > +
    + resetConfigToDefaults('model.layerNormalization.type')} + /> + resetConfigToDefaults('model.layerNormalization.transformer.position')} + /> + resetConfigToDefaults('model.layerNormalization.eps')} + /> +
    +
    +
    +
    + + + toggleControlSection('training')} + > + resetConfigToDefaults('training.logSteps')} + /> + + resetConfigToDefaults('training.batchSize')} + /> + + + + resetConfigToDefaults('training.dropout.present')} + > + resetConfigToDefaults('training.dropout.embedding')} + /> + resetConfigToDefaults('training.dropout.transformer.attention')} + /> + resetConfigToDefaults('training.dropout.transformer.residual')} + /> + + + + + + toggleControlSection('optimizer')} + contentClass={collapsibleSectionClass} + > + resetConfigToDefaults('optimizer.type')} + /> + + resetConfigToDefaults('optimizer.lr')} + /> + + resetConfigToDefaults('optimizer.weightDecay.present')} + > + resetConfigToDefaults('optimizer.weightDecay.value')} + /> + + resetConfigToDefaults('optimizer.weightDecay.useWeightDecayGroups')} + /> + + +
    + {#if config.optimizer.type === 'AdamW' || config.optimizer.type === 'Adam'} + {@const settingsName = config.optimizer.type === 'Adam' ? 'Adam' : 'AdamW'} + +
    + resetConfigToDefaults('optimizer.adam.beta1')} + /> + resetConfigToDefaults('optimizer.adam.beta2')} + /> + resetConfigToDefaults('optimizer.adam.eps')} + /> + resetConfigToDefaults('optimizer.adam.amsgrad')} + /> +
    +
    + {/if} + + {#if config.optimizer.type === 'SGD'} +
    + resetConfigToDefaults('optimizer.sgd.momentum')} + /> + resetConfigToDefaults('optimizer.sgd.dampening')} + /> + resetConfigToDefaults('optimizer.sgd.nesterov')} + /> +
    + {/if} +
    +
    + + + toggleControlSection('advanced')} + contentClass={collapsibleSectionClass} + > + resetConfigToDefaults('training.sharedObjectAllocation')} + /> + resetConfigToDefaults('training.inplaceSupport')} + /> + +
    + + diff --git a/examples/piston-train-toy/src/lib/components/controls/ControlsStatistic.svelte b/examples/piston-train-toy/src/lib/components/controls/ControlsStatistic.svelte new file mode 100644 index 00000000..e5c6ad44 --- /dev/null +++ b/examples/piston-train-toy/src/lib/components/controls/ControlsStatistic.svelte @@ -0,0 +1,32 @@ + + + +
    + + + + {@render children?.()} + + {#if funFact} + + {@render funFact?.()} + + {/if} + +
    diff --git a/examples/piston-train-toy/src/lib/components/controls/DatasetControls.svelte b/examples/piston-train-toy/src/lib/components/controls/DatasetControls.svelte new file mode 100644 index 00000000..a11fe70d --- /dev/null +++ b/examples/piston-train-toy/src/lib/components/controls/DatasetControls.svelte @@ -0,0 +1,110 @@ + + + resetConfigToDefaults('data.dataset')} +/> + +

    {datasetConfigMetadata?.description}

    + + + +
    + {#key datasetName} + {#if parameters && datasetConfig} +
    + {#each Object.entries(parameters) as [paramKey, paramMeta] (paramKey)} + {@const value = datasetConfig[paramKey as keyof typeof datasetConfig]} + {#if isSliderParameter(paramMeta)} + {@const sliderProps = getSliderProps(paramMeta)} + {#if sliderProps} + { + datasetConfig[paramKey as keyof typeof datasetConfig] = + paramMeta.default as never; + }} + /> + {/if} + {:else if isCheckboxParameter(paramMeta)} + { + datasetConfig[paramKey as keyof typeof datasetConfig] = paramMeta.default as never; + }} + /> + {/if} + {/each} +
    + {/if} + {/key} + {#if showDivider} +
    + {/if} + {#if showMaskRatio} + resetConfigToDefaults('data.maskRatio')} + /> + {/if} +
    diff --git a/examples/piston-train-toy/src/lib/components/controls/DatasetSample.svelte b/examples/piston-train-toy/src/lib/components/controls/DatasetSample.svelte new file mode 100644 index 00000000..a793a40a --- /dev/null +++ b/examples/piston-train-toy/src/lib/components/controls/DatasetSample.svelte @@ -0,0 +1,86 @@ + + +{#snippet token(value: number, dashed: boolean = false)} + + {decodeSingle(value, tokenizer)} + +{/snippet} + +{#snippet tokenSequence(values: number[], ignored?: boolean[])} +
    + {#each values as value, i (i)} + {@render token(value, ignored ? ignored[i] : false)} + {/each} +
    +{/snippet} + +
    + {#if collated.length > 0} +
    + + + + {#if hasPrompt} + + {/if} + + + + + {#each collated as { prompt, target, fullSequence } (Array.prototype.concat.call(fullSequence, prompt ?? [], target ?? []))} + {@const pLen = prompt?.length || 0} + {@const targetFlags = maskedFlagsForRange(fullSequence, pLen, target?.length ?? 0)} + + {#if hasPrompt} + {@const promptFlags = maskedFlagsForRange(fullSequence, 0, pLen)} + + {/if} + + + {/each} + + {#if hasPrompt} + + {/if} + + + +
    PromptTarget
    + {@render tokenSequence(prompt!, promptFlags)} + + {@render tokenSequence(target ?? fullSequence, targetFlags)} +
    ......
    +
    + {/if} +
    diff --git a/examples/piston-train-toy/src/lib/components/controls/FormLabel.svelte b/examples/piston-train-toy/src/lib/components/controls/FormLabel.svelte new file mode 100644 index 00000000..baf166ac --- /dev/null +++ b/examples/piston-train-toy/src/lib/components/controls/FormLabel.svelte @@ -0,0 +1,17 @@ + + + diff --git a/examples/piston-train-toy/src/lib/components/controls/NumberInput.svelte b/examples/piston-train-toy/src/lib/components/controls/NumberInput.svelte new file mode 100644 index 00000000..cf902162 --- /dev/null +++ b/examples/piston-train-toy/src/lib/components/controls/NumberInput.svelte @@ -0,0 +1,71 @@ + + +
    + {#if label} + + {/if} +
    + {#if unit} +
    + + + {unit} + +
    + {:else} + + {/if} + {#if onReset} +
    + +
    + {/if} +
    +
    diff --git a/examples/piston-train-toy/src/lib/components/controls/ResetValueButton.svelte b/examples/piston-train-toy/src/lib/components/controls/ResetValueButton.svelte new file mode 100644 index 00000000..19e33fd4 --- /dev/null +++ b/examples/piston-train-toy/src/lib/components/controls/ResetValueButton.svelte @@ -0,0 +1,22 @@ + + + diff --git a/examples/piston-train-toy/src/lib/components/controls/SelectDataset.svelte b/examples/piston-train-toy/src/lib/components/controls/SelectDataset.svelte new file mode 100644 index 00000000..645eda35 --- /dev/null +++ b/examples/piston-train-toy/src/lib/components/controls/SelectDataset.svelte @@ -0,0 +1,119 @@ + + +{#snippet nameAndBadges(_opt: DatasetOption)} + {@const opt = _opt as DatasetOption} + {@const meta = getMeta(opt.value as DatasetName)} + {@const name = opt.text ?? meta.name} + {@const citations = + 'citations' in meta + ? ((meta as Record).citations as CitationsType) + : undefined} + {@const supports = ( + 'supportsModelTypes' in meta ? meta.supportsModelTypes : MODEL_TYPES + ).toSorted()} + {@const currentModel = config.model.topology as ModelType} + {@const isSupported = supports.includes(currentModel)} + {@const autoModel = isSupported ? null : (supports[0] as ModelType)} +
    + + {name} + {#if autoModel} + + (will switch to + {MODEL_DISPLAY_NAMES[autoModel as keyof typeof MODEL_DISPLAY_NAMES]}) + + {/if} + + {#if citations} + + {/if} +
    + {#each supports as n (n)} + {MODEL_DISPLAY_NAMES[n as keyof typeof MODEL_DISPLAY_NAMES]} + {/each} +
    +
    +{/snippet} + +
    + + {#snippet option(_opt, _selected)} + {@const opt = _opt as DatasetOption} + {@render nameAndBadges(opt)} + {/snippet} + {#snippet trigger(_selected)} + {#if _selected} + {@const opt = _selected as DatasetOption} + {opt.text ?? opt.value} + {:else} + Select... + {/if} + {/snippet} + + {#if 'citations' in selectedMeta} +
    + +
    + {/if} +
    diff --git a/examples/piston-train-toy/src/lib/components/controls/Slider.svelte b/examples/piston-train-toy/src/lib/components/controls/Slider.svelte new file mode 100644 index 00000000..500670b8 --- /dev/null +++ b/examples/piston-train-toy/src/lib/components/controls/Slider.svelte @@ -0,0 +1,745 @@ + + +
    + {#if label} + + {/if} +
    +
    +
    +
    + +
    + + +
    + + + {#if showTicks && ticks.length > 0} +
    + + {#each ticks as tick (tick.key)} +
    + {/each} +
    + {/if} + + +
    { + let stepAmount = step > 0 ? step : useLog ? displayValue * 0.05 : (max - min) / 100; + if (stepAmount === 0 && max > min) stepAmount = (max - min) / 100; + if (stepAmount === 0) stepAmount = EPSILON; + + if (e.key === 'ArrowLeft' || e.key === 'ArrowDown') { + scheduleCommit(clampToRange(displayValue - stepAmount)); + } else if (e.key === 'ArrowRight' || e.key === 'ArrowUp') { + scheduleCommit(clampToRange(displayValue + stepAmount)); + } else if (e.key === 'Home') { + scheduleCommit(min); + } else if (e.key === 'End') { + scheduleCommit(max); + } + }} + >
    +
    +
    +
    + {tickFormatter + ? tickFormatter(min) + : useLog + ? formatExponential(min, EXPONENTIAL_PRECISION) + : min.toLocaleString()} +
    +
    + {tickFormatter + ? tickFormatter(max) + : useLog + ? formatExponential(max, EXPONENTIAL_PRECISION) + : max.toLocaleString()} +
    +
    +
    +
    + + +
    +
    + (isInputFocused = true)} + onblur={handleInputBlur} + onkeydown={handleInputKeyDown} + style:width={inputWidth} + class="bg-transparent text-controls-numeric text-right font-mono border-panel-border-base focus:border-neutral-500 border focus:outline-none h-full px-0.5" + {min} + {max} + /> + + {#if unit} + + {unit} + + {/if} +
    + + {#if onReset} + + {/if} +
    +
    +
    + + diff --git a/examples/piston-train-toy/src/lib/components/controls/TextInput.svelte b/examples/piston-train-toy/src/lib/components/controls/TextInput.svelte new file mode 100644 index 00000000..97d42747 --- /dev/null +++ b/examples/piston-train-toy/src/lib/components/controls/TextInput.svelte @@ -0,0 +1,45 @@ + + +
    + {#if label} + + {/if} +
    + + {#if onReset} + + {/if} +
    +
    diff --git a/examples/piston-train-toy/src/lib/components/controls/ToggleGroup.svelte b/examples/piston-train-toy/src/lib/components/controls/ToggleGroup.svelte new file mode 100644 index 00000000..80e2a46a --- /dev/null +++ b/examples/piston-train-toy/src/lib/components/controls/ToggleGroup.svelte @@ -0,0 +1,56 @@ + + + + {#snippet action()} + {#if showEnableToggle} + + {/if} + {/snippet} + {#if !showEnableToggle || (showEnableToggle && enabled)} +
    + {@render children?.()} +
    + {/if} +
    diff --git a/examples/piston-train-toy/src/lib/components/controls/checkbox/CheckboxIcon.svelte b/examples/piston-train-toy/src/lib/components/controls/checkbox/CheckboxIcon.svelte new file mode 100644 index 00000000..1a3a5293 --- /dev/null +++ b/examples/piston-train-toy/src/lib/components/controls/checkbox/CheckboxIcon.svelte @@ -0,0 +1,21 @@ + + + + + + + diff --git a/examples/piston-train-toy/src/lib/components/controls/checkbox/CheckboxInput.svelte b/examples/piston-train-toy/src/lib/components/controls/checkbox/CheckboxInput.svelte new file mode 100644 index 00000000..f85205b8 --- /dev/null +++ b/examples/piston-train-toy/src/lib/components/controls/checkbox/CheckboxInput.svelte @@ -0,0 +1,55 @@ + + + diff --git a/examples/piston-train-toy/src/lib/components/controls/radio/RadioGroupInput.svelte b/examples/piston-train-toy/src/lib/components/controls/radio/RadioGroupInput.svelte new file mode 100644 index 00000000..e06c1810 --- /dev/null +++ b/examples/piston-train-toy/src/lib/components/controls/radio/RadioGroupInput.svelte @@ -0,0 +1,64 @@ + + +
    + {#if label} + + {/if} +
    +
    + {#each options as opt (String(opt.value))} +
    + +
    + {/each} +
    + {#if onReset} +
    + +
    + {/if} +
    +
    diff --git a/examples/piston-train-toy/src/lib/components/controls/radio/RadioIcon.svelte b/examples/piston-train-toy/src/lib/components/controls/radio/RadioIcon.svelte new file mode 100644 index 00000000..2447c975 --- /dev/null +++ b/examples/piston-train-toy/src/lib/components/controls/radio/RadioIcon.svelte @@ -0,0 +1,31 @@ + + + + + diff --git a/examples/piston-train-toy/src/lib/components/controls/radio/RadioInput.svelte b/examples/piston-train-toy/src/lib/components/controls/radio/RadioInput.svelte new file mode 100644 index 00000000..1dca2d1d --- /dev/null +++ b/examples/piston-train-toy/src/lib/components/controls/radio/RadioInput.svelte @@ -0,0 +1,52 @@ + + + diff --git a/examples/piston-train-toy/src/lib/components/controls/select/SelectInput.svelte b/examples/piston-train-toy/src/lib/components/controls/select/SelectInput.svelte new file mode 100644 index 00000000..0ed160a7 --- /dev/null +++ b/examples/piston-train-toy/src/lib/components/controls/select/SelectInput.svelte @@ -0,0 +1,367 @@ + + +
    + {#if label} + + {/if} +
    +
    + +
    + +
    + + {#if isOpen} + +
      = 0 && + menuItems[highlightedIndex]?.kind === 'option' + ? `${id}-opt-${highlightedIndex}` + : undefined} + tabindex="-1" + onkeydown={onMenuKeydown} + class="fixed z-[9999] border border-neutral-300 bg-white max-h-120 overflow-auto text-base rounded-none shadow-lg" + style={menuStyle} + > + {#each menuItems as item, i (item.id)} + {#if item.kind === 'group'} +
    • 0} + class:border-b={i < menuItems.length - 1} + > + {item.label} +
    • + {:else} + {@const opt = item.option} + {@const isDisabled = isOptionDisabled(opt)} +
    • (!isDisabled ? (highlightedIndex = i) : undefined)} + > + +
    • + {/if} + {/each} +
    +
    + {/if} +
    + {#if onReset} + + {/if} +
    +
    diff --git a/examples/piston-train-toy/src/lib/components/controls/select/SelectModelTopology.svelte b/examples/piston-train-toy/src/lib/components/controls/select/SelectModelTopology.svelte new file mode 100644 index 00000000..45c75024 --- /dev/null +++ b/examples/piston-train-toy/src/lib/components/controls/select/SelectModelTopology.svelte @@ -0,0 +1,133 @@ + + +{#snippet optionView(_opt: unknown, _selected: boolean, _index: number)} + {@const opt = _opt as ModelOption} + {@const label = opt.title} + {@const citations = opt.citations} +
    +
    {label}
    + {#if citations} + + {/if} + {#if opt.disabled} + {@const datasetName = DATASET_CONFIG_METADATA[config.data.dataset].name} +
    Not supported by {datasetName}
    + {/if} +
    +{/snippet} + +
    + + {#snippet option(_opt, _selected, _i)} + {@render optionView(_opt, _selected, _i)} + {/snippet} + {#snippet trigger(_selected)} + {#if _selected} + {@const opt = _selected as ModelOption} + {opt.title} + {:else} + Select... + {/if} + {/snippet} + + {#if selectedOption?.citations} +
    + {/if} +
    diff --git a/examples/piston-train-toy/src/lib/components/controls/select/SelectWithCitations.svelte b/examples/piston-train-toy/src/lib/components/controls/select/SelectWithCitations.svelte new file mode 100644 index 00000000..f246a442 --- /dev/null +++ b/examples/piston-train-toy/src/lib/components/controls/select/SelectWithCitations.svelte @@ -0,0 +1,66 @@ + + +{#snippet citationView(_opt: CitationOption)} + {@const label = _opt.title} + {@const citations = _opt.citations} +
    +
    {label}
    + {#if citations} + + {/if} +
    +{/snippet} + +
    + + {#snippet option(_opt, _selected)} + {@const opt = _opt as CitationOption} + {@render citationView(opt)} + {/snippet} + {#snippet trigger(_selected)} + {#if _selected} + {@const opt = _selected as CitationOption} + {opt.title} + + + {:else} + Select... + {/if} + {/snippet} + + {#if selectedOption?.citations} +
    + {/if} +
    diff --git a/examples/piston-train-toy/src/lib/components/footnotes/Footnote.svelte b/examples/piston-train-toy/src/lib/components/footnotes/Footnote.svelte new file mode 100644 index 00000000..1fb49c46 --- /dev/null +++ b/examples/piston-train-toy/src/lib/components/footnotes/Footnote.svelte @@ -0,0 +1,46 @@ + + + + + {computedLabel ?? (label != null ? String(label) : '?')} + diff --git a/examples/piston-train-toy/src/lib/components/footnotes/FootnotesProvider.svelte b/examples/piston-train-toy/src/lib/components/footnotes/FootnotesProvider.svelte new file mode 100644 index 00000000..fbb0eba6 --- /dev/null +++ b/examples/piston-train-toy/src/lib/components/footnotes/FootnotesProvider.svelte @@ -0,0 +1,42 @@ + + +{@render children()} + +
    +

    Footnotes

    +
      + {#each entries as e (e.key)} +
    1. + {e.label + '.'} +
      + {@render e.content()} + ↩︎ +
      +
    2. + {/each} +
    +
    diff --git a/examples/piston-train-toy/src/lib/components/metrics/MetricsSection.svelte b/examples/piston-train-toy/src/lib/components/metrics/MetricsSection.svelte new file mode 100644 index 00000000..ab15269b --- /dev/null +++ b/examples/piston-train-toy/src/lib/components/metrics/MetricsSection.svelte @@ -0,0 +1,65 @@ + + +
    +
    +
    { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + handleClick(); + } + }} + > + +

    {title}

    +
    + {@render chips?.()} +
    + + {#if isOpen} +
    + {@render children?.()} +
    + {/if} +
    diff --git a/examples/piston-train-toy/src/lib/components/metrics/RunChart.svelte b/examples/piston-train-toy/src/lib/components/metrics/RunChart.svelte new file mode 100644 index 00000000..8ef57a5e --- /dev/null +++ b/examples/piston-train-toy/src/lib/components/metrics/RunChart.svelte @@ -0,0 +1,323 @@ + + +
    +
    chartOptions, + setup: (chart) => { + let isRelaying = false; + + const onUpdateAxisPointer = (evt: unknown) => { + if (isRelaying) return; + const s = extractStepFromAxisPointerEvent(evt); + if (s == null) return; + // Choose topmost series that contains this exact step + for (let idx = seriesIndexToRunId.length - 1; idx >= 0; idx--) { + const runId = seriesIndexToRunId[idx]; + if (!runId) continue; + const seriesInfo = runIdToSeries.get(runId); + if (!seriesInfo) continue; + if (exactIndex(seriesInfo.steps, s) !== -1) { + publishMove({ sourceId: chartId, runId, step: s }); + break; + } + } + }; + + const onGlobalOut = () => { + publishClear({ sourceId: chartId }); + }; + + chart.on('updateAxisPointer', onUpdateAxisPointer); + chart.on('globalout', onGlobalOut); + + const unsubscribe = subscribe( + ({ sourceId, runId, step }) => { + if (sourceId === chartId) return; + const info = runIdToSeries.get(runId); + if (!info) return; + const { seriesIndex, steps, first, last } = info; + if (step < first || step > last) return; + const dataIndex = nearestIndex(steps, step); + if (dataIndex < 0) return; + isRelaying = true; + try { + // ECharts accepts a second options argument with { silent: true } + chart.dispatchAction( + { type: 'updateAxisPointer', seriesIndex, dataIndex }, + { silent: true } + ); + } finally { + isRelaying = false; + } + }, + ({ sourceId }) => { + if (sourceId === chartId) return; + chart.dispatchAction({ type: 'hideTip' }, { silent: true }); + } + ); + + return () => { + unsubscribe(); + chart.off('updateAxisPointer', onUpdateAxisPointer); + chart.off('globalout', onGlobalOut); + }; + } + })} + >
    +
    diff --git a/examples/piston-train-toy/src/lib/train/tokenizer.ts b/examples/piston-train-toy/src/lib/train/tokenizer.ts new file mode 100644 index 00000000..487ec809 --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/tokenizer.ts @@ -0,0 +1,9 @@ +/** + * @fileoverview Simplified Tokenizer implementation adapted from huggingface/transformers.js + */ + +import type { ToyTokenizer } from './data/toy/types'; + +export function decodeSingle(value: number, tokenizer: ToyTokenizer | null): string { + return tokenizer?.ids?.[value] || `<${value}>`; +} diff --git a/examples/piston-train-toy/src/routes/+page.svelte b/examples/piston-train-toy/src/routes/+page.svelte new file mode 100644 index 00000000..f311fb33 --- /dev/null +++ b/examples/piston-train-toy/src/routes/+page.svelte @@ -0,0 +1,244 @@ + + +{#snippet tabButton( + title: string, + icon: typeof ChartLine, + hideBorder: boolean, + isActive: boolean, + disabled: boolean, + hasActivity: boolean, + onClick: () => void +)} + {@const Icon = icon} + +{/snippet} + +
    +
    + Sequence Toy +
    +
    + + + + {#if configOpen.current !== false} +
    +
    + {#if trainingState.current !== 'stopped'} +
    + {currentRun.current?.runId} +
    + {/if} +
    +
    + {#if trainingState.current === 'stopped'} + + + + Start Training + + + {:else} +
    + + + + + + + + + + {#if shouldSuggestRestart} + New Changes + {/if} + + +
    + {/if} +
    +
    +
    + +
    +
    +
    + {/if} + + + {#if shouldShowTabContent} +
    + {#if activeTab.current === 'metrics'} + + {:else if activeTab.current === 'about'} + + {/if} +
    + {/if} +
    +
    + + diff --git a/examples/piston-train-toy/src/routes/tabs/About.svelte b/examples/piston-train-toy/src/routes/tabs/About.svelte new file mode 100644 index 00000000..ac9ff6cf --- /dev/null +++ b/examples/piston-train-toy/src/routes/tabs/About.svelte @@ -0,0 +1,310 @@ + + +
    +
    +
    + + {#if !hasWebGPU.current} + {@const isBrowserUnknown = browserInfo.current.type === 'unknown'} + +
    +

    + Sequence Toy requires WebGPU support, and your browser doesn't support it yet. + {#if isBrowserUnknown} + You have a few options: + {/if} +

    +
    +
      + {#if isBrowserUnknown || (browserInfo.current.type !== 'firefox' && browserInfo.current.type !== 'safari')} +
    • + Use the latest version of Chrome, or a + Chromium-based browser (Edge, Arc, Brave, etc.) +
        +
      • + If that doesn't work, try Chrome Canary and ensure WebGPU is enabled. +
      • +
      +
    • + {/if} + {#if isBrowserUnknown || browserInfo.current.type === 'firefox'} +
    • + Use Firefox Nightly + +
    • + {/if} + {#if isBrowserUnknown || browserInfo.current.type === 'safari'} +
    • + Use the latest Safari Technology Preview or enable WebGPU via Feature + Flags. +
        + {#if isBrowserUnknown || browserInfo.current.platform === 'ios'} +
      • + iOS: System Settings > Apps > Safari > Advanced > Feature Flags > Enable + "WebGPU" +
      • + {/if} + {#if isBrowserUnknown || browserInfo.current.platform === 'macos'} +
      • + MacOS: Develop menu Feature Flags > Enable "WebGPU" +
      • + {/if} +
      +
    • + {/if} +
    +
    + {/if} +
    +
    + {#each Array.from({ length: 8 }) as _, i (i)} +
    + +
    + {/each} +
    + +

    + Sequence Toy +

    +
    + +

    + Train a language model in your browser with WebGPU +

    + +

    + Vin Howe + built + Piston, a WebGPU automatic differentiation engine with a JavaScript API modeled after PyTorch, + for this project. It started life as a fork of + RatchetA ratchet is a device that allows motion in only one direction—Ratchet is + intentionally forward-only. A piston reciprocates back and forth quickly—Piston + adds support for reverse-mode automatic differentiation. + by + Christopher Fleetwood. +

    + +
    +
    +

    + This project was inspired by many other open-source projects: +

    + +
    + +
    +

    Helpful resources when building this project:

    +
    + +
    +
    + +
    +
    +
    +

    + Thanks to Grant Pitt, + Christopher Fleetwood, and + Ben Gubler + for support, discussion, and feedback. 💜 +

    +
    +
    +
    +
    +
    +
    + +
    +
    diff --git a/examples/piston-train-toy/src/routes/tabs/Metrics.svelte b/examples/piston-train-toy/src/routes/tabs/Metrics.svelte new file mode 100644 index 00000000..b951483c --- /dev/null +++ b/examples/piston-train-toy/src/routes/tabs/Metrics.svelte @@ -0,0 +1,73 @@ + + +
    + {#if Object.keys(metricGroups).length === 0} +
    +

    No metrics available. Start training to see charts.

    +
    + {:else} +
    + {#each Object.entries(metricGroups) as [groupName, metrics] (groupName)} + {@const filteredMetrics = getFilteredMetrics(groupName, metrics)} + {@const hasMetrics = filteredMetrics.length > 0} + {@const sectionOpen = (metricsSectionsOpen.current[groupName] ?? true) && hasMetrics} + toggleMetricsSection(groupName) : undefined} + > + {#snippet chips()} + + {/snippet} + {#each filteredMetrics as metricName (metricName)} +
    + +
    + {/each} +
    + {/each} +
    + {/if} +
    From db69d01397ac2d887dabd412d319248a9c561674 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 26 Oct 2025 19:30:40 -0600 Subject: [PATCH 533/590] Update package.json --- examples/piston-train-toy/package.json | 52 ++++++++++++++------------ 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/examples/piston-train-toy/package.json b/examples/piston-train-toy/package.json index 7daac633..5509597a 100644 --- a/examples/piston-train-toy/package.json +++ b/examples/piston-train-toy/package.json @@ -15,31 +15,37 @@ }, "dependencies": { "@piston-ml/piston-web": "workspace:^", - "chart.js": "^4.4.7", - "mathjs": "^14.2.1" + "@types/katex": "^0.16.7", + "echarts": "^6.0.0", + "katex": "^0.16.23", + "lucide-svelte": "^0.548.0", + "random-js": "^2.1.0", + "svelte-portal": "^2.2.1", + "unique-names-generator": "^4.7.1" }, "devDependencies": { - "@eslint/compat": "^1.2.5", - "@eslint/js": "^9.18.0", - "@sveltejs/adapter-static": "^3.0.8", - "@sveltejs/kit": "^2.16.0", - "@sveltejs/vite-plugin-svelte": "^5.0.0", - "@tailwindcss/vite": "^4.0.0", - "@webgpu/types": "^0.1.60", - "eslint": "^9.18.0", - "eslint-config-prettier": "^10.0.1", + "@eslint/compat": "^1.4.0", + "@eslint/js": "^9.37.0", + "@sveltejs/adapter-static": "^3.0.10", + "@sveltejs/kit": "^2.46.4", + "@sveltejs/vite-plugin-svelte": "^6.2.1", + "@tailwindcss/vite": "^4.1.14", + "@types/glob": "^9.0.0", + "@webgpu/types": "^0.1.65", + "eslint": "^9.37.0", + "eslint-config-prettier": "^10.1.8", "eslint-plugin-perfectionist": "^4.15.1", - "eslint-plugin-svelte": "^2.46.1", - "globals": "^15.14.0", - "prettier": "^3.4.2", - "prettier-plugin-svelte": "^3.3.3", - "svelte": "^5.0.0", - "svelte-check": "^4.0.0", - "tailwindcss": "^4.0.0", - "typescript": "^5.8.2", - "typescript-eslint": "^8.20.0", - "vite": "^6.2.0", - "vite-plugin-top-level-await": "^1.4.4", - "vite-plugin-wasm": "^3.4.1" + "eslint-plugin-svelte": "^3.12.4", + "glob": "^11.0.3", + "globals": "^16.4.0", + "prettier": "^3.6.2", + "rollup": "^4.52.4", + "svelte": "^5.39.11", + "svelte-check": "^4.3.3", + "tailwindcss": "^4.1.14", + "typescript": "^5.9.3", + "typescript-eslint": "^8.46.0", + "vite": "^7.1.9", + "vite-plugin-wasm": "^3.5.0" } } From c2c634b5c240548f7bb280d050d590575535c5c1 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 26 Oct 2025 19:35:23 -0600 Subject: [PATCH 534/590] Update pnpm-lock.yaml --- pnpm-lock.yaml | 2564 ++++++++++++++++++++++++------------------------ 1 file changed, 1276 insertions(+), 1288 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 694b11a5..8c975b08 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -25,184 +25,106 @@ importers: examples/piston-train-toy: dependencies: - '@codemirror/lang-javascript': - specifier: ^6.2.4 - version: 6.2.4 - '@codemirror/language': - specifier: ^6.11.1 - version: 6.11.2 - '@codemirror/lint': - specifier: ^6.8.5 - version: 6.8.5 - '@codemirror/state': - specifier: ^6.5.2 - version: 6.5.2 - '@codemirror/view': - specifier: ^6.37.2 - version: 6.38.1 - '@lezer/highlight': - specifier: ^1.2.1 - version: 1.2.1 '@piston-ml/piston-web': specifier: workspace:^ version: link:../../packages/web '@types/katex': specifier: ^0.16.7 version: 0.16.7 - chart.js: - specifier: ^4.5.0 - version: 4.5.0 - codemirror: - specifier: ^6.0.1 - version: 6.0.2 echarts: - specifier: ^5.6.0 - version: 5.6.0 - eta: - specifier: ^3.5.0 - version: 3.5.0 + specifier: ^6.0.0 + version: 6.0.0 katex: - specifier: ^0.16.22 - version: 0.16.22 + specifier: ^0.16.23 + version: 0.16.23 lucide-svelte: - specifier: ^0.525.0 - version: 0.525.0(svelte@5.36.16) + specifier: ^0.548.0 + version: 0.548.0(svelte@5.39.11) random-js: specifier: ^2.1.0 version: 2.1.0 + svelte-portal: + specifier: ^2.2.1 + version: 2.2.1 unique-names-generator: specifier: ^4.7.1 version: 4.7.1 devDependencies: '@eslint/compat': - specifier: ^1.3.1 - version: 1.3.1(eslint@9.31.0(jiti@2.5.1)) + specifier: ^1.4.0 + version: 1.4.0(eslint@9.37.0(jiti@2.6.1)) '@eslint/js': - specifier: ^9.31.0 - version: 9.31.0 + specifier: ^9.37.0 + version: 9.37.0 '@sveltejs/adapter-static': - specifier: ^3.0.8 - version: 3.0.8(@sveltejs/kit@2.26.0(@sveltejs/vite-plugin-svelte@6.1.0(svelte@5.36.16)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)))(svelte@5.36.16)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0))) + specifier: ^3.0.10 + version: 3.0.10(@sveltejs/kit@2.46.4(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0)))(svelte@5.39.11)(vite@7.1.9(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0))) '@sveltejs/kit': - specifier: ^2.26.0 - version: 2.26.0(@sveltejs/vite-plugin-svelte@6.1.0(svelte@5.36.16)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)))(svelte@5.36.16)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)) + specifier: ^2.46.4 + version: 2.46.4(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0)))(svelte@5.39.11)(vite@7.1.9(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0)) '@sveltejs/vite-plugin-svelte': - specifier: ^6.1.0 - version: 6.1.0(svelte@5.36.16)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)) + specifier: ^6.2.1 + version: 6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0)) '@tailwindcss/vite': - specifier: ^4.1.11 - version: 4.1.11(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)) - '@testing-library/jest-dom': - specifier: ^6.6.3 - version: 6.6.3 - '@testing-library/svelte': - specifier: ^5.2.8 - version: 5.2.8(svelte@5.36.16)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0))(vitest@3.2.4) + specifier: ^4.1.14 + version: 4.1.14(vite@7.1.9(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0)) '@types/glob': - specifier: ^8.1.0 - version: 8.1.0 - '@vitest/ui': - specifier: ^3.2.4 - version: 3.2.4(vitest@3.2.4) + specifier: ^9.0.0 + version: 9.0.0 '@webgpu/types': - specifier: ^0.1.64 - version: 0.1.64 - default-passive-events: - specifier: ^2.0.0 - version: 2.0.0 + specifier: ^0.1.65 + version: 0.1.65 eslint: - specifier: ^9.31.0 - version: 9.31.0(jiti@2.5.1) + specifier: ^9.37.0 + version: 9.37.0(jiti@2.6.1) eslint-config-prettier: specifier: ^10.1.8 - version: 10.1.8(eslint@9.31.0(jiti@2.5.1)) + version: 10.1.8(eslint@9.37.0(jiti@2.6.1)) eslint-plugin-perfectionist: - specifier: ^4.15.0 - version: 4.15.0(eslint@9.31.0(jiti@2.5.1))(typescript@5.8.3) + specifier: ^4.15.1 + version: 4.15.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) eslint-plugin-svelte: - specifier: ^3.11.0 - version: 3.11.0(eslint@9.31.0(jiti@2.5.1))(svelte@5.36.16)(ts-node@10.9.2(@swc/core@1.13.2)(@swc/wasm@1.13.2)(@types/node@24.1.0)(typescript@5.8.3)) + specifier: ^3.12.4 + version: 3.12.4(eslint@9.37.0(jiti@2.6.1))(svelte@5.39.11)(ts-node@10.9.2(@swc/core@1.13.2)(@swc/wasm@1.13.2)(@types/node@24.1.0)(typescript@5.9.3)) glob: specifier: ^11.0.3 version: 11.0.3 globals: - specifier: ^15.15.0 - version: 15.15.0 - jsdom: - specifier: ^26.1.0 - version: 26.1.0 + specifier: ^16.4.0 + version: 16.4.0 prettier: specifier: ^3.6.2 version: 3.6.2 - prettier-plugin-svelte: - specifier: ^3.4.0 - version: 3.4.0(prettier@3.6.2)(svelte@5.36.16) rollup: - specifier: ^4.45.1 - version: 4.45.1 - rollup-plugin-copy: - specifier: ^3.5.0 - version: 3.5.0 + specifier: ^4.52.4 + version: 4.52.4 svelte: - specifier: ^5.36.16 - version: 5.36.16 + specifier: ^5.39.11 + version: 5.39.11 svelte-check: - specifier: ^4.3.0 - version: 4.3.0(picomatch@4.0.3)(svelte@5.36.16)(typescript@5.8.3) + specifier: ^4.3.3 + version: 4.3.3(picomatch@4.0.3)(svelte@5.39.11)(typescript@5.9.3) tailwindcss: - specifier: ^4.1.11 - version: 4.1.11 + specifier: ^4.1.14 + version: 4.1.14 typescript: - specifier: ^5.8.3 - version: 5.8.3 + specifier: ^5.9.3 + version: 5.9.3 typescript-eslint: - specifier: ^8.38.0 - version: 8.38.0(eslint@9.31.0(jiti@2.5.1))(typescript@5.8.3) + specifier: ^8.46.0 + version: 8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) vite: - specifier: ^6.3.5 - version: 6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) - vite-plugin-static-copy: - specifier: ^2.3.1 - version: 2.3.1(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)) - vite-plugin-top-level-await: - specifier: ^1.6.0 - version: 1.6.0(rollup@4.45.1)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)) + specifier: ^7.1.9 + version: 7.1.9(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0) vite-plugin-wasm: specifier: ^3.5.0 - version: 3.5.0(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)) - vite-tsconfig-paths: - specifier: ^5.1.4 - version: 5.1.4(typescript@5.8.3)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)) - vitest: - specifier: ^3.2.4 - version: 3.2.4(@types/node@24.1.0)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(jiti@2.5.1)(jsdom@26.1.0)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) + version: 3.5.0(vite@7.1.9(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0)) packages/web: dependencies: - '@codemirror/language': - specifier: ^6.11.1 - version: 6.11.2 - '@codemirror/lint': - specifier: ^6.8.5 - version: 6.8.5 - '@codemirror/state': - specifier: ^6.5.2 - version: 6.5.2 - '@codemirror/view': - specifier: ^6.37.2 - version: 6.38.1 - '@lezer/highlight': - specifier: ^1.2.1 - version: 1.2.1 - '@lezer/javascript': - specifier: ^1.5.1 - version: 1.5.1 '@piston-ml/piston-web-wasm': specifier: link:../../target/pkg/piston-web version: link:../../target/pkg/piston-web - codemirror: - specifier: ^6.0.1 - version: 6.0.2 random-js: specifier: ^2.1.0 version: 2.1.0 @@ -216,15 +138,6 @@ importers: '@eslint/js': specifier: ^9.31.0 version: 9.31.0 - '@lezer/common': - specifier: ^1.2.3 - version: 1.2.3 - '@lezer/generator': - specifier: ^1.7.3 - version: 1.8.0 - '@lezer/lr': - specifier: ^1.4.2 - version: 1.4.2 '@rollup/plugin-alias': specifier: ^5.1.1 version: 5.1.1(rollup@4.46.2) @@ -248,13 +161,13 @@ importers: version: 22.16.5 '@typescript-eslint/eslint-plugin': specifier: ^8.26.1 - version: 8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.31.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.31.0(jiti@2.5.1))(typescript@5.8.3) + version: 8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.3))(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.3) '@typescript-eslint/parser': specifier: ^8.26.1 - version: 8.38.0(eslint@9.31.0(jiti@2.5.1))(typescript@5.8.3) + version: 8.38.0(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.3) '@vitest/browser': specifier: ^3.1.4 - version: 3.2.4(playwright@1.55.0)(vite@6.3.5(@types/node@22.16.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0))(vitest@3.2.4) + version: 3.2.4(playwright@1.55.0)(vite@7.1.9(@types/node@22.16.5)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0))(vitest@3.2.4) '@vitest/ui': specifier: ^3.1.4 version: 3.2.4(vitest@3.2.4) @@ -272,16 +185,16 @@ importers: version: 3.3.0(@types/node@22.16.5)(typescript@5.8.3) eslint: specifier: ^9.22.0 - version: 9.31.0(jiti@2.5.1) + version: 9.31.0(jiti@2.6.1) eslint-config-prettier: specifier: ^10.1.1 - version: 10.1.8(eslint@9.31.0(jiti@2.5.1)) + version: 10.1.8(eslint@9.31.0(jiti@2.6.1)) eslint-plugin-perfectionist: specifier: ^4.10.1 - version: 4.15.0(eslint@9.31.0(jiti@2.5.1))(typescript@5.8.3) + version: 4.15.0(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.3) eslint-plugin-prettier: specifier: ^5.2.3 - version: 5.5.3(eslint-config-prettier@10.1.8(eslint@9.31.0(jiti@2.5.1)))(eslint@9.31.0(jiti@2.5.1))(prettier@3.6.2) + version: 5.5.3(eslint-config-prettier@10.1.8(eslint@9.31.0(jiti@2.6.1)))(eslint@9.31.0(jiti@2.6.1))(prettier@3.6.2) globals: specifier: ^15.14.0 version: 15.15.0 @@ -326,25 +239,28 @@ importers: version: 5.8.3 typescript-eslint: specifier: ^8.38.0 - version: 8.38.0(eslint@9.31.0(jiti@2.5.1))(typescript@5.8.3) + version: 8.38.0(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.3) vite-tsconfig-paths: specifier: ^5.1.4 - version: 5.1.4(typescript@5.8.3)(vite@6.3.5(@types/node@22.16.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)) + version: 5.1.4(typescript@5.8.3)(vite@7.1.9(@types/node@22.16.5)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0)) vitest: - specifier: ^3.1.4 - version: 3.2.4(@types/node@22.16.5)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(jiti@2.5.1)(jsdom@26.1.0)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) + specifier: ^3.2.4 + version: 3.2.4(@types/node@22.16.5)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0) packages: - '@adobe/css-tools@4.4.3': - resolution: {integrity: sha512-VQKMkwriZbaOgVCby1UDY/LDk5fIjhQicCvVPFqfe+69fWaPWydbWJ3wRt59/YzIwda1I81loas3oCoHxnqvdA==} - '@ampproject/remapping@2.3.0': resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@asamuzakjp/css-color@3.2.0': - resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} + '@asamuzakjp/css-color@4.0.5': + resolution: {integrity: sha512-lMrXidNhPGsDjytDy11Vwlb6OIGrT3CmLg3VWNFyWkLWtijKl7xjvForlh8vuj0SHGjgl4qZEQzUmYTeQA2JFQ==} + + '@asamuzakjp/dom-selector@6.7.3': + resolution: {integrity: sha512-kiGFeY+Hxf5KbPpjRLf+ffWbkos1aGo8MBfd91oxS3O57RgU3XhZrt/6UzoVF9VMpWbC3v87SRc9jxGrc9qHtQ==} + + '@asamuzakjp/nwsapi@2.3.9': + resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} '@babel/code-frame@7.27.1': resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} @@ -906,30 +822,6 @@ packages: resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==} engines: {node: '>=6.9.0'} - '@codemirror/autocomplete@6.18.6': - resolution: {integrity: sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==} - - '@codemirror/commands@6.8.1': - resolution: {integrity: sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw==} - - '@codemirror/lang-javascript@6.2.4': - resolution: {integrity: sha512-0WVmhp1QOqZ4Rt6GlVGwKJN3KW7Xh4H2q8ZZNGZaP6lRdxXJzmjm4FqvmOojVj6khWJHIb9sp7U/72W7xQgqAA==} - - '@codemirror/language@6.11.2': - resolution: {integrity: sha512-p44TsNArL4IVXDTbapUmEkAlvWs2CFQbcfc0ymDsis1kH2wh0gcY96AS29c/vp2d0y2Tquk1EDSaawpzilUiAw==} - - '@codemirror/lint@6.8.5': - resolution: {integrity: sha512-s3n3KisH7dx3vsoeGMxsbRAgKe4O1vbrnKBClm99PU0fWxmxsx5rR2PfqQgIt+2MMJBHbiJ5rfIdLYfB9NNvsA==} - - '@codemirror/search@6.5.11': - resolution: {integrity: sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA==} - - '@codemirror/state@6.5.2': - resolution: {integrity: sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==} - - '@codemirror/view@6.38.1': - resolution: {integrity: sha512-RmTOkE7hRU3OVREqFVITWHz6ocgBjv08GoePscAakgVQfciA3SGCEk7mb9IzwW61cKKmlTpHXG6DUE5Ubx+MGQ==} - '@commitlint/cli@19.8.1': resolution: {integrity: sha512-LXUdNIkspyxrlV6VDHWBmCZRtkEVRpBKxi2Gtw3J54cGWhLCTouVD/Q6ZSaSvd2YaDObWK8mDjrz3TIKtaQMAA==} engines: {node: '>=v18'} @@ -1003,8 +895,8 @@ packages: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} - '@csstools/color-helpers@5.0.2': - resolution: {integrity: sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==} + '@csstools/color-helpers@5.1.0': + resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} engines: {node: '>=18'} '@csstools/css-calc@2.1.4': @@ -1014,8 +906,8 @@ packages: '@csstools/css-parser-algorithms': ^3.0.5 '@csstools/css-tokenizer': ^3.0.4 - '@csstools/css-color-parser@3.0.10': - resolution: {integrity: sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==} + '@csstools/css-color-parser@3.1.0': + resolution: {integrity: sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==} engines: {node: '>=18'} peerDependencies: '@csstools/css-parser-algorithms': ^3.0.5 @@ -1027,162 +919,168 @@ packages: peerDependencies: '@csstools/css-tokenizer': ^3.0.4 + '@csstools/css-syntax-patches-for-csstree@1.0.14': + resolution: {integrity: sha512-zSlIxa20WvMojjpCSy8WrNpcZ61RqfTfX3XTaOeVlGJrt/8HF3YbzgFZa01yTbT4GWQLwfTcC3EB8i3XnB647Q==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + '@csstools/css-tokenizer@3.0.4': resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} engines: {node: '>=18'} - '@esbuild/aix-ppc64@0.25.8': - resolution: {integrity: sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==} + '@esbuild/aix-ppc64@0.25.10': + resolution: {integrity: sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.25.8': - resolution: {integrity: sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==} + '@esbuild/android-arm64@0.25.10': + resolution: {integrity: sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.25.8': - resolution: {integrity: sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==} + '@esbuild/android-arm@0.25.10': + resolution: {integrity: sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.25.8': - resolution: {integrity: sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==} + '@esbuild/android-x64@0.25.10': + resolution: {integrity: sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.25.8': - resolution: {integrity: sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==} + '@esbuild/darwin-arm64@0.25.10': + resolution: {integrity: sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.25.8': - resolution: {integrity: sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==} + '@esbuild/darwin-x64@0.25.10': + resolution: {integrity: sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.25.8': - resolution: {integrity: sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==} + '@esbuild/freebsd-arm64@0.25.10': + resolution: {integrity: sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.8': - resolution: {integrity: sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==} + '@esbuild/freebsd-x64@0.25.10': + resolution: {integrity: sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.25.8': - resolution: {integrity: sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==} + '@esbuild/linux-arm64@0.25.10': + resolution: {integrity: sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.25.8': - resolution: {integrity: sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==} + '@esbuild/linux-arm@0.25.10': + resolution: {integrity: sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.25.8': - resolution: {integrity: sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==} + '@esbuild/linux-ia32@0.25.10': + resolution: {integrity: sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.25.8': - resolution: {integrity: sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==} + '@esbuild/linux-loong64@0.25.10': + resolution: {integrity: sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.25.8': - resolution: {integrity: sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==} + '@esbuild/linux-mips64el@0.25.10': + resolution: {integrity: sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.25.8': - resolution: {integrity: sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==} + '@esbuild/linux-ppc64@0.25.10': + resolution: {integrity: sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.25.8': - resolution: {integrity: sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==} + '@esbuild/linux-riscv64@0.25.10': + resolution: {integrity: sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.25.8': - resolution: {integrity: sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==} + '@esbuild/linux-s390x@0.25.10': + resolution: {integrity: sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.25.8': - resolution: {integrity: sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==} + '@esbuild/linux-x64@0.25.10': + resolution: {integrity: sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.8': - resolution: {integrity: sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==} + '@esbuild/netbsd-arm64@0.25.10': + resolution: {integrity: sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.8': - resolution: {integrity: sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==} + '@esbuild/netbsd-x64@0.25.10': + resolution: {integrity: sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.8': - resolution: {integrity: sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==} + '@esbuild/openbsd-arm64@0.25.10': + resolution: {integrity: sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.8': - resolution: {integrity: sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==} + '@esbuild/openbsd-x64@0.25.10': + resolution: {integrity: sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.25.8': - resolution: {integrity: sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==} + '@esbuild/openharmony-arm64@0.25.10': + resolution: {integrity: sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.25.8': - resolution: {integrity: sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==} + '@esbuild/sunos-x64@0.25.10': + resolution: {integrity: sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.25.8': - resolution: {integrity: sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==} + '@esbuild/win32-arm64@0.25.10': + resolution: {integrity: sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.25.8': - resolution: {integrity: sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==} + '@esbuild/win32-ia32@0.25.10': + resolution: {integrity: sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.25.8': - resolution: {integrity: sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==} + '@esbuild/win32-x64@0.25.10': + resolution: {integrity: sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -1193,12 +1091,18 @@ packages: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/eslint-utils@4.9.0': + resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/regexpp@4.12.1': resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/compat@1.3.1': - resolution: {integrity: sha512-k8MHony59I5EPic6EQTCNOuPoVBnoYXkP+20xvwFjN7t0qI3ImyvyBgg+hIVPwC8JaxVjjUZld+cLfBLFDLucg==} + '@eslint/compat@1.4.0': + resolution: {integrity: sha512-DEzm5dKeDBPm3r08Ixli/0cmxr8LkRdwxMRUIJBlSCpAwSrvFEJpVBzV+66JhDxiaqKxnRzCXhtiMiczF7Hglg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.40 || 9 @@ -1214,10 +1118,18 @@ packages: resolution: {integrity: sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/config-helpers@0.4.0': + resolution: {integrity: sha512-WUFvV4WoIwW8Bv0KeKCIIEgdSiFOsulyN0xrMu+7z43q/hkOLXjvb5u7UC9jDxvRzcrbEmuZBX5yJZz1741jog==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/core@0.15.1': resolution: {integrity: sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/core@0.16.0': + resolution: {integrity: sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/eslintrc@3.3.1': resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1226,6 +1138,10 @@ packages: resolution: {integrity: sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/js@9.37.0': + resolution: {integrity: sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/object-schema@2.1.6': resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1234,6 +1150,10 @@ packages: resolution: {integrity: sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/plugin-kit@0.4.0': + resolution: {integrity: sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -1242,6 +1162,10 @@ packages: resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} engines: {node: '>=18.18.0'} + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + engines: {node: '>=18.18.0'} + '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} @@ -1391,8 +1315,11 @@ packages: resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} engines: {node: '>=18.0.0'} - '@jridgewell/gen-mapping@0.3.12': - resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==} + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} @@ -1401,11 +1328,11 @@ packages: '@jridgewell/source-map@0.3.10': resolution: {integrity: sha512-0pPkgz9dY+bijgistcTTJ5mR+ocqRXLuhXHYdzoMmmoJ2C9S46RCm2GMUbatPEUK9Yjy26IrAy8D/M00lLkv+Q==} - '@jridgewell/sourcemap-codec@1.5.4': - resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==} + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - '@jridgewell/trace-mapping@0.3.29': - resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==} + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} @@ -1414,28 +1341,6 @@ packages: resolution: {integrity: sha512-f5DRIOZf7wxogefH03RjMPMdBF7ADTWUMoOs9kaJo06EfwF+aFhMZMDZxHg/Xe12hptN9xoZjGso2fdjapBRIA==} engines: {node: '>=10'} - '@kurkle/color@0.3.4': - resolution: {integrity: sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==} - - '@lezer/common@1.2.3': - resolution: {integrity: sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==} - - '@lezer/generator@1.8.0': - resolution: {integrity: sha512-/SF4EDWowPqV1jOgoGSGTIFsE7Ezdr7ZYxyihl5eMKVO5tlnpIhFcDavgm1hHY5GEonoOAEnJ0CU0x+tvuAuUg==} - hasBin: true - - '@lezer/highlight@1.2.1': - resolution: {integrity: sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==} - - '@lezer/javascript@1.5.1': - resolution: {integrity: sha512-ATOImjeVJuvgm3JQ/bpo2Tmv55HSScE2MTPnKRMRIPx2cLhHGyX2VnqpHhtIV1tVzIjZDbcWQm+NCTF40ggZVw==} - - '@lezer/lr@1.4.2': - resolution: {integrity: sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==} - - '@marijn/find-cluster-break@1.0.2': - resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==} - '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -1606,15 +1511,6 @@ packages: tslib: optional: true - '@rollup/plugin-virtual@3.0.2': - resolution: {integrity: sha512-10monEYsBp3scM4/ND4LNH5Rxvh3e/cVeL3jWTgZ2SrQ+BmUoQcopVQvnaMcOnykb1VkxUFuDAN+0FnpTFRy2A==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true - '@rollup/plugin-wasm@6.2.2': resolution: {integrity: sha512-gpC4R1G9Ni92ZIRTexqbhX7U+9estZrbhP+9SRb0DW9xpB9g7j34r+J2hqrcW/lRI7dJaU84MxZM0Rt82tqYPQ==} engines: {node: '>=14.0.0'} @@ -1643,19 +1539,14 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.45.1': - resolution: {integrity: sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==} - cpu: [arm] - os: [android] - '@rollup/rollup-android-arm-eabi@4.46.2': resolution: {integrity: sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.45.1': - resolution: {integrity: sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==} - cpu: [arm64] + '@rollup/rollup-android-arm-eabi@4.52.4': + resolution: {integrity: sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==} + cpu: [arm] os: [android] '@rollup/rollup-android-arm64@4.46.2': @@ -1663,19 +1554,19 @@ packages: cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.45.1': - resolution: {integrity: sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==} + '@rollup/rollup-android-arm64@4.52.4': + resolution: {integrity: sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w==} cpu: [arm64] - os: [darwin] + os: [android] '@rollup/rollup-darwin-arm64@4.46.2': resolution: {integrity: sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.45.1': - resolution: {integrity: sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==} - cpu: [x64] + '@rollup/rollup-darwin-arm64@4.52.4': + resolution: {integrity: sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg==} + cpu: [arm64] os: [darwin] '@rollup/rollup-darwin-x64@4.46.2': @@ -1683,19 +1574,19 @@ packages: cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.45.1': - resolution: {integrity: sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==} - cpu: [arm64] - os: [freebsd] + '@rollup/rollup-darwin-x64@4.52.4': + resolution: {integrity: sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw==} + cpu: [x64] + os: [darwin] '@rollup/rollup-freebsd-arm64@4.46.2': resolution: {integrity: sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.45.1': - resolution: {integrity: sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==} - cpu: [x64] + '@rollup/rollup-freebsd-arm64@4.52.4': + resolution: {integrity: sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ==} + cpu: [arm64] os: [freebsd] '@rollup/rollup-freebsd-x64@4.46.2': @@ -1703,18 +1594,18 @@ packages: cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.45.1': - resolution: {integrity: sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==} - cpu: [arm] - os: [linux] + '@rollup/rollup-freebsd-x64@4.52.4': + resolution: {integrity: sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw==} + cpu: [x64] + os: [freebsd] '@rollup/rollup-linux-arm-gnueabihf@4.46.2': resolution: {integrity: sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.45.1': - resolution: {integrity: sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==} + '@rollup/rollup-linux-arm-gnueabihf@4.52.4': + resolution: {integrity: sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ==} cpu: [arm] os: [linux] @@ -1723,9 +1614,9 @@ packages: cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.45.1': - resolution: {integrity: sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==} - cpu: [arm64] + '@rollup/rollup-linux-arm-musleabihf@4.52.4': + resolution: {integrity: sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q==} + cpu: [arm] os: [linux] '@rollup/rollup-linux-arm64-gnu@4.46.2': @@ -1733,8 +1624,8 @@ packages: cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.45.1': - resolution: {integrity: sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==} + '@rollup/rollup-linux-arm64-gnu@4.52.4': + resolution: {integrity: sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg==} cpu: [arm64] os: [linux] @@ -1743,8 +1634,13 @@ packages: cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.45.1': - resolution: {integrity: sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==} + '@rollup/rollup-linux-arm64-musl@4.52.4': + resolution: {integrity: sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.52.4': + resolution: {integrity: sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ==} cpu: [loong64] os: [linux] @@ -1753,19 +1649,14 @@ packages: cpu: [loong64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.45.1': - resolution: {integrity: sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==} - cpu: [ppc64] - os: [linux] - '@rollup/rollup-linux-ppc64-gnu@4.46.2': resolution: {integrity: sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.45.1': - resolution: {integrity: sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==} - cpu: [riscv64] + '@rollup/rollup-linux-ppc64-gnu@4.52.4': + resolution: {integrity: sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g==} + cpu: [ppc64] os: [linux] '@rollup/rollup-linux-riscv64-gnu@4.46.2': @@ -1773,8 +1664,8 @@ packages: cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.45.1': - resolution: {integrity: sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==} + '@rollup/rollup-linux-riscv64-gnu@4.52.4': + resolution: {integrity: sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg==} cpu: [riscv64] os: [linux] @@ -1783,9 +1674,9 @@ packages: cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.45.1': - resolution: {integrity: sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==} - cpu: [s390x] + '@rollup/rollup-linux-riscv64-musl@4.52.4': + resolution: {integrity: sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA==} + cpu: [riscv64] os: [linux] '@rollup/rollup-linux-s390x-gnu@4.46.2': @@ -1793,9 +1684,9 @@ packages: cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.45.1': - resolution: {integrity: sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==} - cpu: [x64] + '@rollup/rollup-linux-s390x-gnu@4.52.4': + resolution: {integrity: sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA==} + cpu: [s390x] os: [linux] '@rollup/rollup-linux-x64-gnu@4.46.2': @@ -1803,8 +1694,8 @@ packages: cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.45.1': - resolution: {integrity: sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==} + '@rollup/rollup-linux-x64-gnu@4.52.4': + resolution: {integrity: sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg==} cpu: [x64] os: [linux] @@ -1813,19 +1704,24 @@ packages: cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.45.1': - resolution: {integrity: sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==} + '@rollup/rollup-linux-x64-musl@4.52.4': + resolution: {integrity: sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openharmony-arm64@4.52.4': + resolution: {integrity: sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA==} cpu: [arm64] - os: [win32] + os: [openharmony] '@rollup/rollup-win32-arm64-msvc@4.46.2': resolution: {integrity: sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.45.1': - resolution: {integrity: sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==} - cpu: [ia32] + '@rollup/rollup-win32-arm64-msvc@4.52.4': + resolution: {integrity: sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ==} + cpu: [arm64] os: [win32] '@rollup/rollup-win32-ia32-msvc@4.46.2': @@ -1833,8 +1729,13 @@ packages: cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.45.1': - resolution: {integrity: sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==} + '@rollup/rollup-win32-ia32-msvc@4.52.4': + resolution: {integrity: sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.52.4': + resolution: {integrity: sha512-UF9KfsH9yEam0UjTwAgdK0anlQ7c8/pWPU2yVjyWcF1I1thABt6WXE47cI71pGiZ8wGvxohBoLnxM04L/wj8mQ==} cpu: [x64] os: [win32] @@ -1843,6 +1744,11 @@ packages: cpu: [x64] os: [win32] + '@rollup/rollup-win32-x64-msvc@4.52.4': + resolution: {integrity: sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w==} + cpu: [x64] + os: [win32] + '@samverschueren/stream-to-observable@0.3.1': resolution: {integrity: sha512-c/qwwcHyafOQuVQJj0IlBjf5yYgBI7YPJ77k4fOJYesb41jio65eaJODRUmfYKhTOFBrIZ66kgvGPlNbjuoRdQ==} engines: {node: '>=6'} @@ -1859,38 +1765,45 @@ packages: resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} engines: {node: '>=18'} + '@standard-schema/spec@1.0.0': + resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + '@surma/rollup-plugin-off-main-thread@2.2.3': resolution: {integrity: sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==} - '@sveltejs/acorn-typescript@1.0.5': - resolution: {integrity: sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==} + '@sveltejs/acorn-typescript@1.0.6': + resolution: {integrity: sha512-4awhxtMh4cx9blePWl10HRHj8Iivtqj+2QdDCSMDzxG+XKa9+VCNupQuCuvzEhYPzZSrX+0gC+0lHA/0fFKKQQ==} peerDependencies: acorn: ^8.9.0 - '@sveltejs/adapter-static@3.0.8': - resolution: {integrity: sha512-YaDrquRpZwfcXbnlDsSrBQNCChVOT9MGuSg+dMAyfsAa1SmiAhrA5jUYUiIMC59G92kIbY/AaQOWcBdq+lh+zg==} + '@sveltejs/adapter-static@3.0.10': + resolution: {integrity: sha512-7D9lYFWJmB7zxZyTE/qxjksvMqzMuYrrsyh1f4AlZqeZeACPRySjbC3aFiY55wb1tWUaKOQG9PVbm74JcN2Iew==} peerDependencies: '@sveltejs/kit': ^2.0.0 - '@sveltejs/kit@2.26.0': - resolution: {integrity: sha512-TUxMYoK6Yim4uRIW0L7TXtlEtyLchy90PmInI7d1lPAPMchkBEvN3nVMkn5iTMUobxdLE5nR/YEU/4aYqezMuQ==} + '@sveltejs/kit@2.46.4': + resolution: {integrity: sha512-J1fd80WokLzIm6EAV7z7C2+/C02qVAX645LZomARARTRJkbbJSY1Jln3wtBZYibUB8c9/5Z6xqLAV39VdbtWCQ==} engines: {node: '>=18.13'} hasBin: true peerDependencies: + '@opentelemetry/api': ^1.0.0 '@sveltejs/vite-plugin-svelte': ^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 svelte: ^4.0.0 || ^5.0.0-next.0 vite: ^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true - '@sveltejs/vite-plugin-svelte-inspector@5.0.0': - resolution: {integrity: sha512-iwQ8Z4ET6ZFSt/gC+tVfcsSBHwsqc6RumSaiLUkAurW3BCpJam65cmHw0oOlDMTO0u+PZi9hilBRYN+LZNHTUQ==} + '@sveltejs/vite-plugin-svelte-inspector@5.0.1': + resolution: {integrity: sha512-ubWshlMk4bc8mkwWbg6vNvCeT7lGQojE3ijDh3QTR6Zr/R+GXxsGbyH4PExEPpiFmqPhYiVSVmHBjUcVc1JIrA==} engines: {node: ^20.19 || ^22.12 || >=24} peerDependencies: '@sveltejs/vite-plugin-svelte': ^6.0.0-next.0 svelte: ^5.0.0 vite: ^6.3.0 || ^7.0.0 - '@sveltejs/vite-plugin-svelte@6.1.0': - resolution: {integrity: sha512-+U6lz1wvGEG/BvQyL4z/flyNdQ9xDNv5vrh+vWBWTHaebqT0c9RNggpZTo/XSPoHsSCWBlYaTlRX8pZ9GATXCw==} + '@sveltejs/vite-plugin-svelte@6.2.1': + resolution: {integrity: sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ==} engines: {node: ^20.19 || ^22.12 || >=24} peerDependencies: svelte: ^5.0.0 @@ -1968,71 +1881,71 @@ packages: '@swc/counter@0.1.3': resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} - '@swc/types@0.1.23': - resolution: {integrity: sha512-u1iIVZV9Q0jxY+yM2vw/hZGDNudsN85bBpTqzAQ9rzkxW9D+e3aEM4Han+ow518gSewkXgjmEK0BD79ZcNVgPw==} + '@swc/types@0.1.25': + resolution: {integrity: sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==} '@swc/wasm@1.13.2': resolution: {integrity: sha512-iOOnBRjL4k8Yna/JTjOEOcn9bry0KiOnrOj8ztK/iUU1/SPWXnndX7l+Fa6ssGEOpnScLk5j9GthWX1kzMutTA==} - '@tailwindcss/node@4.1.11': - resolution: {integrity: sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==} + '@tailwindcss/node@4.1.14': + resolution: {integrity: sha512-hpz+8vFk3Ic2xssIA3e01R6jkmsAhvkQdXlEbRTk6S10xDAtiQiM3FyvZVGsucefq764euO/b8WUW9ysLdThHw==} - '@tailwindcss/oxide-android-arm64@4.1.11': - resolution: {integrity: sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==} + '@tailwindcss/oxide-android-arm64@4.1.14': + resolution: {integrity: sha512-a94ifZrGwMvbdeAxWoSuGcIl6/DOP5cdxagid7xJv6bwFp3oebp7y2ImYsnZBMTwjn5Ev5xESvS3FFYUGgPODQ==} engines: {node: '>= 10'} cpu: [arm64] os: [android] - '@tailwindcss/oxide-darwin-arm64@4.1.11': - resolution: {integrity: sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==} + '@tailwindcss/oxide-darwin-arm64@4.1.14': + resolution: {integrity: sha512-HkFP/CqfSh09xCnrPJA7jud7hij5ahKyWomrC3oiO2U9i0UjP17o9pJbxUN0IJ471GTQQmzwhp0DEcpbp4MZTA==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@tailwindcss/oxide-darwin-x64@4.1.11': - resolution: {integrity: sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==} + '@tailwindcss/oxide-darwin-x64@4.1.14': + resolution: {integrity: sha512-eVNaWmCgdLf5iv6Qd3s7JI5SEFBFRtfm6W0mphJYXgvnDEAZ5sZzqmI06bK6xo0IErDHdTA5/t7d4eTfWbWOFw==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@tailwindcss/oxide-freebsd-x64@4.1.11': - resolution: {integrity: sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==} + '@tailwindcss/oxide-freebsd-x64@4.1.14': + resolution: {integrity: sha512-QWLoRXNikEuqtNb0dhQN6wsSVVjX6dmUFzuuiL09ZeXju25dsei2uIPl71y2Ic6QbNBsB4scwBoFnlBfabHkEw==} engines: {node: '>= 10'} cpu: [x64] os: [freebsd] - '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11': - resolution: {integrity: sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==} + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.14': + resolution: {integrity: sha512-VB4gjQni9+F0VCASU+L8zSIyjrLLsy03sjcR3bM0V2g4SNamo0FakZFKyUQ96ZVwGK4CaJsc9zd/obQy74o0Fw==} engines: {node: '>= 10'} cpu: [arm] os: [linux] - '@tailwindcss/oxide-linux-arm64-gnu@4.1.11': - resolution: {integrity: sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==} + '@tailwindcss/oxide-linux-arm64-gnu@4.1.14': + resolution: {integrity: sha512-qaEy0dIZ6d9vyLnmeg24yzA8XuEAD9WjpM5nIM1sUgQ/Zv7cVkharPDQcmm/t/TvXoKo/0knI3me3AGfdx6w1w==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tailwindcss/oxide-linux-arm64-musl@4.1.11': - resolution: {integrity: sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==} + '@tailwindcss/oxide-linux-arm64-musl@4.1.14': + resolution: {integrity: sha512-ISZjT44s59O8xKsPEIesiIydMG/sCXoMBCqsphDm/WcbnuWLxxb+GcvSIIA5NjUw6F8Tex7s5/LM2yDy8RqYBQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tailwindcss/oxide-linux-x64-gnu@4.1.11': - resolution: {integrity: sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==} + '@tailwindcss/oxide-linux-x64-gnu@4.1.14': + resolution: {integrity: sha512-02c6JhLPJj10L2caH4U0zF8Hji4dOeahmuMl23stk0MU1wfd1OraE7rOloidSF8W5JTHkFdVo/O7uRUJJnUAJg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tailwindcss/oxide-linux-x64-musl@4.1.11': - resolution: {integrity: sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==} + '@tailwindcss/oxide-linux-x64-musl@4.1.14': + resolution: {integrity: sha512-TNGeLiN1XS66kQhxHG/7wMeQDOoL0S33x9BgmydbrWAb9Qw0KYdd8o1ifx4HOGDWhVmJ+Ul+JQ7lyknQFilO3Q==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tailwindcss/oxide-wasm32-wasi@4.1.11': - resolution: {integrity: sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==} + '@tailwindcss/oxide-wasm32-wasi@4.1.14': + resolution: {integrity: sha512-uZYAsaW/jS/IYkd6EWPJKW/NlPNSkWkBlaeVBi/WsFQNP05/bzkebUL8FH1pdsqx4f2fH/bWFcUABOM9nfiJkQ==} engines: {node: '>=14.0.0'} cpu: [wasm32] bundledDependencies: @@ -2043,24 +1956,24 @@ packages: - '@emnapi/wasi-threads' - tslib - '@tailwindcss/oxide-win32-arm64-msvc@4.1.11': - resolution: {integrity: sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==} + '@tailwindcss/oxide-win32-arm64-msvc@4.1.14': + resolution: {integrity: sha512-Az0RnnkcvRqsuoLH2Z4n3JfAef0wElgzHD5Aky/e+0tBUxUhIeIqFBTMNQvmMRSP15fWwmvjBxZ3Q8RhsDnxAA==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@tailwindcss/oxide-win32-x64-msvc@4.1.11': - resolution: {integrity: sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==} + '@tailwindcss/oxide-win32-x64-msvc@4.1.14': + resolution: {integrity: sha512-ttblVGHgf68kEE4om1n/n44I0yGPkCPbLsqzjvybhpwa6mKKtgFfAzy6btc3HRmuW7nHe0OOrSeNP9sQmmH9XA==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@tailwindcss/oxide@4.1.11': - resolution: {integrity: sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==} + '@tailwindcss/oxide@4.1.14': + resolution: {integrity: sha512-23yx+VUbBwCg2x5XWdB8+1lkPajzLmALEfMb51zZUBYaYVPDQvBSD/WYDqiVyBIo2BZFa3yw1Rpy3G2Jp+K0dw==} engines: {node: '>= 10'} - '@tailwindcss/vite@4.1.11': - resolution: {integrity: sha512-RHYhrR3hku0MJFRV+fN2gNbDNEh3dwKvY8XJvTxCSXeMOsCRSr+uKvDWQcbizrHgjML6ZmTE5OwMrl5wKcujCw==} + '@tailwindcss/vite@4.1.14': + resolution: {integrity: sha512-BoFUoU0XqgCUS1UXWhmDJroKKhNXeDzD7/XwabjkDIAbMnc4ULn5e2FuEuBbhZ6ENZoSYzKlzvZ44Yr6EUDUSA==} peerDependencies: vite: ^5.2.0 || ^6 || ^7 @@ -2068,23 +1981,6 @@ packages: resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==} engines: {node: '>=18'} - '@testing-library/jest-dom@6.6.3': - resolution: {integrity: sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==} - engines: {node: '>=14', npm: '>=6', yarn: '>=1'} - - '@testing-library/svelte@5.2.8': - resolution: {integrity: sha512-ucQOtGsJhtawOEtUmbR4rRh53e6RbM1KUluJIXRmh6D4UzxR847iIqqjRtg9mHNFmGQ8Vkam9yVcR5d1mhIHKA==} - engines: {node: '>= 10'} - peerDependencies: - svelte: ^3 || ^4 || ^5 || ^5.0.0-next.0 - vite: '*' - vitest: '*' - peerDependenciesMeta: - vite: - optional: true - vitest: - optional: true - '@testing-library/user-event@14.6.1': resolution: {integrity: sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==} engines: {node: '>=12', npm: '>=6'} @@ -2128,14 +2024,9 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - '@types/fs-extra@8.1.5': - resolution: {integrity: sha512-0dzKcwO+S8s2kuF5Z9oUWatQJj5Uq/iqphEtE3GQJVRRYm/tD1LglU2UnXi2A8jLq5umkGouOXOR9y0n613ZwQ==} - - '@types/glob@7.2.0': - resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} - - '@types/glob@8.1.0': - resolution: {integrity: sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==} + '@types/glob@9.0.0': + resolution: {integrity: sha512-00UxlRaIUvYm4R4W9WYkN8/J+kV8fmOQ7okeH6YFtGWFMt3odD45tpG5yA5wnL7HE6lLgjaTW5n14ju2hl2NNA==} + deprecated: This is a stub types definition. glob provides its own type definitions, so you do not need this installed. '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -2143,13 +2034,6 @@ packages: '@types/katex@0.16.7': resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==} - '@types/minimatch@5.1.2': - resolution: {integrity: sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==} - - '@types/minimatch@6.0.0': - resolution: {integrity: sha512-zmPitbQ8+6zNutpwgcQuLcsEpn/Cj54Kbn7L5pX0Os5kdWplB7xPgEh/g+SWOB/qmows2gpuCaPyduq8ZZRnxA==} - deprecated: This is a stub types definition. minimatch provides its own type definitions, so you do not need this installed. - '@types/node@22.16.5': resolution: {integrity: sha512-bJFoMATwIGaxxx8VJPeM8TonI8t579oRvgAuT8zFugJsJZgzqv0Fu8Mhp68iecjzG7cnN3mO2dJQ5uUM2EFrgQ==} @@ -2176,6 +2060,14 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/eslint-plugin@8.46.0': + resolution: {integrity: sha512-hA8gxBq4ukonVXPy0OKhiaUh/68D0E88GSmtC1iAEnGaieuDi38LhS7jdCHRLi6ErJBNDGCzvh5EnzdPwUc0DA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.46.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/parser@8.38.0': resolution: {integrity: sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2183,22 +2075,61 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/parser@8.46.0': + resolution: {integrity: sha512-n1H6IcDhmmUEG7TNVSspGmiHHutt7iVKtZwRppD7e04wha5MrkV1h3pti9xQLcCMt6YWsncpoT0HMjkH1FNwWQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/project-service@8.38.0': resolution: {integrity: sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/project-service@8.45.0': + resolution: {integrity: sha512-3pcVHwMG/iA8afdGLMuTibGR7pDsn9RjDev6CCB+naRsSYs2pns5QbinF4Xqw6YC/Sj3lMrm/Im0eMfaa61WUg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/project-service@8.46.0': + resolution: {integrity: sha512-OEhec0mH+U5Je2NZOeK1AbVCdm0ChyapAyTeXVIYTPXDJ3F07+cu87PPXcGoYqZ7M9YJVvFnfpGg1UmCIqM+QQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/scope-manager@8.38.0': resolution: {integrity: sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/scope-manager@8.45.0': + resolution: {integrity: sha512-clmm8XSNj/1dGvJeO6VGH7EUSeA0FMs+5au/u3lrA3KfG8iJ4u8ym9/j2tTEoacAffdW1TVUzXO30W1JTJS7dA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/scope-manager@8.46.0': + resolution: {integrity: sha512-lWETPa9XGcBes4jqAMYD9fW0j4n6hrPtTJwWDmtqgFO/4HF4jmdH/Q6wggTw5qIT5TXjKzbt7GsZUBnWoO3dqw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/tsconfig-utils@8.38.0': resolution: {integrity: sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/tsconfig-utils@8.45.0': + resolution: {integrity: sha512-aFdr+c37sc+jqNMGhH+ajxPXwjv9UtFZk79k8pLoJ6p4y0snmYpPA52GuWHgt2ZF4gRRW6odsEj41uZLojDt5w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/tsconfig-utils@8.46.0': + resolution: {integrity: sha512-WrYXKGAHY836/N7zoK/kzi6p8tXFhasHh8ocFL9VZSAkvH956gfeRfcnhs3xzRy8qQ/dq3q44v1jvQieMFg2cw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/type-utils@8.38.0': resolution: {integrity: sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2206,16 +2137,43 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/type-utils@8.46.0': + resolution: {integrity: sha512-hy+lvYV1lZpVs2jRaEYvgCblZxUoJiPyCemwbQZ+NGulWkQRy0HRPYAoef/CNSzaLt+MLvMptZsHXHlkEilaeg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/types@8.38.0': resolution: {integrity: sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/types@8.45.0': + resolution: {integrity: sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/types@8.46.0': + resolution: {integrity: sha512-bHGGJyVjSE4dJJIO5yyEWt/cHyNwga/zXGJbJJ8TiO01aVREK6gCTu3L+5wrkb1FbDkQ+TKjMNe9R/QQQP9+rA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/typescript-estree@8.38.0': resolution: {integrity: sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/typescript-estree@8.45.0': + resolution: {integrity: sha512-GfE1NfVbLam6XQ0LcERKwdTTPlLvHvXXhOeUGC1OXi4eQBoyy1iVsW+uzJ/J9jtCz6/7GCQ9MtrQ0fml/jWCnA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/typescript-estree@8.46.0': + resolution: {integrity: sha512-ekDCUfVpAKWJbRfm8T1YRrCot1KFxZn21oV76v5Fj4tr7ELyk84OS+ouvYdcDAwZL89WpEkEj2DKQ+qg//+ucg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/utils@8.38.0': resolution: {integrity: sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2223,10 +2181,32 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/utils@8.45.0': + resolution: {integrity: sha512-bxi1ht+tLYg4+XV2knz/F7RVhU0k6VrSMc9sb8DQ6fyCTrGQLHfo7lDtN0QJjZjKkLA2ThrKuCdHEvLReqtIGg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/utils@8.46.0': + resolution: {integrity: sha512-nD6yGWPj1xiOm4Gk0k6hLSZz2XkNXhuYmyIrOWcHoPuAhjT9i5bAG+xbWPgFeNR8HPHHtpNKdYUXJl/D3x7f5g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/visitor-keys@8.38.0': resolution: {integrity: sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/visitor-keys@8.45.0': + resolution: {integrity: sha512-qsaFBA3e09MIDAGFUrTk+dzqtfv1XPVz8t8d1f0ybTzrCY7BKiMC5cjrl1O/P7UmHsNyW90EYSkU/ZWpmXelag==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/visitor-keys@8.46.0': + resolution: {integrity: sha512-FrvMpAK+hTbFy7vH5j1+tMYHMSKLE6RzluFJlkFNKD0p9YsUT75JlBSmr5so3QRzvMwU5/bIEdeNrxm8du8l3Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@vitest/browser@3.2.4': resolution: {integrity: sha512-tJxiPrWmzH8a+w9nLKlQMzAKX/7VjFs50MWgcAj7p9XQ7AQ9/35fByFYptgPELyLw+0aixTnC4pUWV+APcZ/kw==} peerDependencies: @@ -2276,8 +2256,8 @@ packages: '@vitest/utils@3.2.4': resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} - '@webgpu/types@0.1.64': - resolution: {integrity: sha512-84kRIAGV46LJTlJZWxShiOrNL30A+9KokD7RB3dRCIqODFjodS5tCD5yyiZ8kIReGVZSDfA3XkkwyyOIF6K62A==} + '@webgpu/types@0.1.65': + resolution: {integrity: sha512-cYrHab4d6wuVvDW5tdsfI6/o6vcLMDe6w2Citd1oS51Xxu2ycLCnVo4fqwujfKWijrZMInTJIKcXxteoy21nVA==} JSONStream@1.3.5: resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} @@ -2507,6 +2487,9 @@ packages: before-after-hook@2.2.3: resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==} + bidi-js@1.0.3: + resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} + binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} @@ -2514,6 +2497,7 @@ packages: binary-install@1.1.0: resolution: {integrity: sha512-rkwNGW+3aQVSZoD0/o3mfPN6Yxh3Id0R/xzTVBVVpGNlVz8EGwusksxRlbk/A5iKTZt9zkMn3qIqmAt3vpfbzg==} engines: {node: '>=10'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} @@ -2618,10 +2602,6 @@ packages: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} - chalk@3.0.0: - resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} - engines: {node: '>=8'} - chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -2633,10 +2613,6 @@ packages: chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} - chart.js@4.5.0: - resolution: {integrity: sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==} - engines: {pnpm: '>=8'} - check-error@2.1.1: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} @@ -2712,9 +2688,6 @@ packages: resolution: {integrity: sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==} engines: {node: '>=0.10.0'} - codemirror@6.0.2: - resolution: {integrity: sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==} - color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} @@ -2731,9 +2704,6 @@ packages: colord@2.9.3: resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} - colorette@1.4.0: - resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==} - colorette@2.0.20: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} @@ -2854,9 +2824,6 @@ packages: create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - crelt@1.0.6: - resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} - cross-env@7.0.3: resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} @@ -2883,13 +2850,14 @@ packages: resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} engines: {node: '>=8.0.0'} + css-tree@3.1.0: + resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + css-what@6.2.2: resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} engines: {node: '>= 6'} - css.escape@1.5.1: - resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} - cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} @@ -2917,9 +2885,9 @@ packages: resolution: {integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==} engines: {node: '>=8.0.0'} - cssstyle@4.6.0: - resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==} - engines: {node: '>=18'} + cssstyle@5.3.1: + resolution: {integrity: sha512-g5PC9Aiph9eiczFpcgUhd9S4UUO3F+LHGRIi5NUMZ+4xtoIYbHNZwZnWA2JsFGe8OU8nl4WyaEFiZuGuxlutJQ==} + engines: {node: '>=20'} cz-conventional-changelog@3.3.0: resolution: {integrity: sha512-U466fIzU5U22eES5lTNiNbZ+d8dfcHcssH4o7QsdWaCcRs/feIPCxKYSWkYBNs5mny7MvEfwpTLWjvbm94hecw==} @@ -2933,9 +2901,9 @@ packages: resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==} engines: {node: '>=0.10'} - data-urls@5.0.0: - resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} - engines: {node: '>=18'} + data-urls@6.0.0: + resolution: {integrity: sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA==} + engines: {node: '>=20'} data-view-buffer@1.0.2: resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} @@ -2961,6 +2929,15 @@ packages: supports-color: optional: true + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + decimal.js@10.6.0: resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} @@ -2994,9 +2971,6 @@ packages: resolution: {integrity: sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==} engines: {node: '>=18'} - default-passive-events@2.0.0: - resolution: {integrity: sha512-eMtt76GpDVngZQ3ocgvRcNCklUMwID1PaNbCNxfpDXuiOXttSh0HzBbda1HU9SIUsDc02vb7g9+3I5tlqe/qMQ==} - defaults@1.0.4: resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} @@ -3039,12 +3013,12 @@ packages: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} - detect-libc@2.0.4: - resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} + detect-libc@2.1.1: + resolution: {integrity: sha512-ecqj/sy1jcK1uWrwpR67UhYrIFQ+5WlGxth34WquCbamhFA6hkkwiu37o6J5xCHdo1oixJRfVRw+ywV+Hq/0Aw==} engines: {node: '>=8'} - devalue@5.1.1: - resolution: {integrity: sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==} + devalue@5.3.2: + resolution: {integrity: sha512-UDsjUbpQn9kvm68slnrs+mfxwFkIflOhkanmyabZ8zOYk8SMEIbJ3TK+88g70hSIeytu4y18f0z/hYHMTrXIWw==} diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} @@ -3057,9 +3031,6 @@ packages: dom-accessibility-api@0.5.16: resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} - dom-accessibility-api@0.6.3: - resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} - dom-serializer@1.4.1: resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} @@ -3097,8 +3068,8 @@ packages: ecc-jsbn@0.1.2: resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==} - echarts@5.6.0: - resolution: {integrity: sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==} + echarts@6.0.0: + resolution: {integrity: sha512-Tte/grDQRiETQP4xz3iZWSvoHrkCQtwqd6hs+mifXcjrCuo2iKWbajFObuLJVBlDIJlOzgQPd1hsaKt/3+OMkQ==} ejs@3.1.10: resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} @@ -3124,8 +3095,8 @@ packages: end-of-stream@1.4.5: resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} - enhanced-resolve@5.18.2: - resolution: {integrity: sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==} + enhanced-resolve@5.18.3: + resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} engines: {node: '>=10.13.0'} entities@2.2.0: @@ -3173,8 +3144,8 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} - esbuild@0.25.8: - resolution: {integrity: sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==} + esbuild@0.25.10: + resolution: {integrity: sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==} engines: {node: '>=18'} hasBin: true @@ -3210,6 +3181,12 @@ packages: peerDependencies: eslint: '>=8.45.0' + eslint-plugin-perfectionist@4.15.1: + resolution: {integrity: sha512-MHF0cBoOG0XyBf7G0EAFCuJJu4I18wy0zAoT1OHfx2o6EOx1EFTIzr2HGeuZa1kDcusoX0xJ9V7oZmaeFd773Q==} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + eslint: '>=8.45.0' + eslint-plugin-prettier@5.5.3: resolution: {integrity: sha512-NAdMYww51ehKfDyDhv59/eIItUVzU0Io9H2E8nHNGKEeeqlnci+1gCvrHib6EmZdf6GxF+LCV5K7UC65Ezvw7w==} engines: {node: ^14.18.0 || >=16.0.0} @@ -3224,8 +3201,8 @@ packages: eslint-config-prettier: optional: true - eslint-plugin-svelte@3.11.0: - resolution: {integrity: sha512-KliWlkieHyEa65aQIkRwUFfHzT5Cn4u3BQQsu3KlkJOs7c1u7ryn84EWaOjEzilbKgttT4OfBURA8Uc4JBSQIw==} + eslint-plugin-svelte@3.12.4: + resolution: {integrity: sha512-hD7wPe+vrPgx3U2X2b/wyTMtWobm660PygMGKrWWYTc9lvtY8DpNFDaU2CJQn1szLjGbn/aJ3g8WiXuKakrEkw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.1 || ^9.0.0 @@ -3256,6 +3233,16 @@ packages: jiti: optional: true + eslint@9.37.0: + resolution: {integrity: sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + esm-env@1.2.2: resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} @@ -3299,10 +3286,6 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} - eta@3.5.0: - resolution: {integrity: sha512-e3x3FBvGzeCIHhF+zhK8FZA2vC5uFn6b4HJjegUbIWrDb4mJ7JjTGMJY9VGIbRVpmSwHopNiaJibhjIr+HfLug==} - engines: {node: '>=6.0.0'} - eventemitter3@4.0.7: resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} @@ -3370,6 +3353,15 @@ packages: picomatch: optional: true + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + fflate@0.8.2: resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} @@ -3472,14 +3464,6 @@ packages: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} - fs-extra@11.3.0: - resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==} - engines: {node: '>=14.14'} - - fs-extra@8.1.0: - resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} - engines: {node: '>=6 <7 || >=8'} - fs-extra@9.1.0: resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} engines: {node: '>=10'} @@ -3597,8 +3581,8 @@ packages: resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} engines: {node: '>=18'} - globals@16.3.0: - resolution: {integrity: sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==} + globals@16.4.0: + resolution: {integrity: sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==} engines: {node: '>=18'} globalthis@1.0.4: @@ -3608,10 +3592,6 @@ packages: globalyzer@0.1.0: resolution: {integrity: sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==} - globby@10.0.1: - resolution: {integrity: sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A==} - engines: {node: '>=8'} - globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} @@ -3785,10 +3765,6 @@ packages: resolution: {integrity: sha512-BYqTHXTGUIvg7t1r4sJNKcbDZkL92nkXA8YtRpbjFHRHGDL/NtUeiBJMeE60kIFN/Mg8ESaWQvftaYMGJzQZCQ==} engines: {node: '>=4'} - indent-string@4.0.0: - resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} - engines: {node: '>=8'} - index-to-position@1.1.0: resolution: {integrity: sha512-XPdx9Dq4t9Qk1mTMbWONJqU7boCoumEH7fRET37HX5+khDUl3J2W6PdALxhILYlIYx2amlwYcRPp28p0tSiojg==} engines: {node: '>=18'} @@ -3985,10 +3961,6 @@ packages: resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==} engines: {node: '>=12'} - is-plain-object@3.0.1: - resolution: {integrity: sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==} - engines: {node: '>=0.10.0'} - is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} @@ -4113,8 +4085,8 @@ packages: resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} engines: {node: '>= 10.13.0'} - jiti@2.5.1: - resolution: {integrity: sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==} + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true js-tokens@4.0.0: @@ -4134,9 +4106,9 @@ packages: jsbn@0.1.1: resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} - jsdom@26.1.0: - resolution: {integrity: sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==} - engines: {node: '>=18'} + jsdom@27.0.0: + resolution: {integrity: sha512-lIHeR1qlIRrIN5VMccd8tI2Sgw6ieYXSVktcSHaNe3Z5nE/tcPQYQWOq00wxMvYOsz+73eAkNenVvmPC6bba9A==} + engines: {node: '>=20'} peerDependencies: canvas: ^3.0.0 peerDependenciesMeta: @@ -4179,9 +4151,6 @@ packages: engines: {node: '>=6'} hasBin: true - jsonfile@4.0.0: - resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} - jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} @@ -4193,8 +4162,8 @@ packages: resolution: {integrity: sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==} engines: {node: '>=0.6.0'} - katex@0.16.22: - resolution: {integrity: sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg==} + katex@0.16.23: + resolution: {integrity: sha512-7VlC1hsEEolL9xNO05v9VjrvWZePkCVBJqj8ruICxYjZfHaHbaU53AlP+PODyFIXEnaEIEWi3wJy7FPZ95JAVg==} hasBin: true keyv@4.5.4: @@ -4428,11 +4397,15 @@ packages: resolution: {integrity: sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==} engines: {node: 20 || >=22} + lru-cache@11.2.2: + resolution: {integrity: sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==} + engines: {node: 20 || >=22} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - lucide-svelte@0.525.0: - resolution: {integrity: sha512-kfuN6JcCqTfCz2B76aXnyGLAzEBRSYw5GaUspM5RNHQZS5aI5yaKu06fbaofOk8cDvUtY0AUm/zAix7aUX6Q3A==} + lucide-svelte@0.548.0: + resolution: {integrity: sha512-aW2BfHWBLWf/XPSKytTPV16AWfFeFIJeUyOg7eHY2rhzVQ0u0LIvoS4pm2oskr+OJVw+NsS8fPvlBVqPfUO1XQ==} peerDependencies: svelte: ^3 || ^4 || ^5.0.0-next.42 @@ -4446,6 +4419,9 @@ packages: magic-string@0.30.17: resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + magic-string@0.30.19: + resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} + make-dir@3.1.0: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} @@ -4464,6 +4440,9 @@ packages: mdn-data@2.0.14: resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} + mdn-data@2.12.2: + resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} + meow@12.1.1: resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} engines: {node: '>=16.10'} @@ -4514,10 +4493,6 @@ packages: resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} engines: {node: '>=18'} - min-indent@1.0.1: - resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} - engines: {node: '>=4'} - minimatch@10.0.3: resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==} engines: {node: 20 || >=22} @@ -4555,8 +4530,8 @@ packages: resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} engines: {node: '>= 8'} - minizlib@3.0.2: - resolution: {integrity: sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==} + minizlib@3.1.0: + resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} engines: {node: '>= 18'} mkdirp@1.0.4: @@ -4564,11 +4539,6 @@ packages: engines: {node: '>=10'} hasBin: true - mkdirp@3.0.1: - resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} - engines: {node: '>=10'} - hasBin: true - mlly@1.7.4: resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} @@ -4659,9 +4629,6 @@ packages: resolution: {integrity: sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==} engines: {node: '>=0.10.0'} - nwsapi@2.2.20: - resolution: {integrity: sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==} - oauth-sign@0.9.0: resolution: {integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==} @@ -5149,12 +5116,6 @@ packages: resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} engines: {node: '>=6.0.0'} - prettier-plugin-svelte@3.4.0: - resolution: {integrity: sha512-pn1ra/0mPObzqoIQn/vUTR3ZZI6UuZ0sHqMK5x2jMLGrs53h0sXhkVuDcrlssHwIMk7FYrMjHBPoUSyyEEDlBQ==} - peerDependencies: - prettier: ^3.0.0 - svelte: ^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0 - prettier@3.6.2: resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} engines: {node: '>=14'} @@ -5253,10 +5214,6 @@ packages: resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} engines: {node: '>= 0.10'} - redent@3.0.0: - resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} - engines: {node: '>=8'} - reflect.getprototypeof@1.0.10: resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} engines: {node: '>= 0.4'} @@ -5360,10 +5317,6 @@ packages: rollup-plugin-bundle-size@1.0.3: resolution: {integrity: sha512-aWj0Pvzq90fqbI5vN1IvUrlf4utOqy+AERYxwWjegH1G8PzheMnrRIgQ5tkwKVtQMDP0bHZEACW/zLDF+XgfXQ==} - rollup-plugin-copy@3.5.0: - resolution: {integrity: sha512-wI8D5dvYovRMx/YYKtUNt3Yxaw4ORC9xo6Gt9t22kveWz1enG9QrhVlagzwrxSC455xD1dHMKhIJkbsQ7d48BA==} - engines: {node: '>=8.3'} - rollup-plugin-postcss@4.0.2: resolution: {integrity: sha512-05EaY6zvZdmvPUDi3uCcAQoESDcYnv8ogJJQRp6V5kZ6J6P7uAVJlrTZcaaA20wTH527YTnKfkAoPxWI/jPp4w==} engines: {node: '>=10'} @@ -5403,13 +5356,13 @@ packages: engines: {node: '>=10.0.0'} hasBin: true - rollup@4.45.1: - resolution: {integrity: sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==} + rollup@4.46.2: + resolution: {integrity: sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - rollup@4.46.2: - resolution: {integrity: sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==} + rollup@4.52.4: + resolution: {integrity: sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -5484,6 +5437,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + engines: {node: '>=10'} + hasBin: true + serialize-javascript@4.0.0: resolution: {integrity: sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==} @@ -5556,6 +5514,10 @@ packages: resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==} engines: {node: '>=18'} + sirv@3.0.2: + resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} + engines: {node: '>=18'} + slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -5718,10 +5680,6 @@ packages: 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'} @@ -5739,9 +5697,6 @@ packages: style-inject@0.3.0: resolution: {integrity: sha512-IezA2qp+vcdlhJaVm5SOdPPTUu0FCEqfNSli2vRuSIBbu5Nq5UvygTk/VzeCqfLz2Atj3dVII5QBKGZRZ0edzw==} - style-mod@4.1.2: - resolution: {integrity: sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==} - stylehacks@5.1.1: resolution: {integrity: sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==} engines: {node: ^10 || ^12 || >=14.0} @@ -5768,16 +5723,16 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - svelte-check@4.3.0: - resolution: {integrity: sha512-Iz8dFXzBNAM7XlEIsUjUGQhbEE+Pvv9odb9+0+ITTgFWZBGeJRRYqHUUglwe2EkLD5LIsQaAc4IUJyvtKuOO5w==} + svelte-check@4.3.3: + resolution: {integrity: sha512-RYP0bEwenDXzfv0P1sKAwjZSlaRyqBn0Fz1TVni58lqyEiqgwztTpmodJrGzP6ZT2aHl4MbTvWP6gbmQ3FOnBg==} engines: {node: '>= 18.0.0'} hasBin: true peerDependencies: svelte: ^4.0.0 || ^5.0.0-next.0 typescript: '>=5.0.0' - svelte-eslint-parser@1.3.0: - resolution: {integrity: sha512-VCgMHKV7UtOGcGLGNFSbmdm6kEKjtzo5nnpGU/mnx4OsFY6bZ7QwRF5DUx+Hokw5Lvdyo8dpk8B1m8mliomrNg==} + svelte-eslint-parser@1.3.3: + resolution: {integrity: sha512-oTrDR8Z7Wnguut7QH3YKh7JR19xv1seB/bz4dxU5J/86eJtZOU4eh0/jZq4dy6tAlz/KROxnkRQspv5ZEt7t+Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 @@ -5785,8 +5740,11 @@ packages: svelte: optional: true - svelte@5.36.16: - resolution: {integrity: sha512-C7HnyISfvZEofs7T4p7+bmjrbQlhd6lZfgV2tLYg6Eb3nUFM/Zu9dGlSg+GWbUBU/WPw6zDPOFNZAx9qXsoCkg==} + svelte-portal@2.2.1: + resolution: {integrity: sha512-uF7is5sM4aq5iN7QF/67XLnTUvQCf2iiG/B1BHTqLwYVY1dsVmTeXZ/LeEyU6dLjApOQdbEG9lkqHzxiQtOLEQ==} + + svelte@5.39.11: + resolution: {integrity: sha512-8MxWVm2+3YwrFbPaxOlT1bbMi6OTenrAgks6soZfiaS8Fptk4EVyRIFhJc3RpO264EeSNwgjWAdki0ufg4zkGw==} engines: {node: '>=18'} svgo@2.8.0: @@ -5809,19 +5767,19 @@ packages: resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==} engines: {node: ^14.18.0 || >=16.0.0} - tailwindcss@4.1.11: - resolution: {integrity: sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==} + tailwindcss@4.1.14: + resolution: {integrity: sha512-b7pCxjGO98LnxVkKjaZSDeNuljC4ueKUddjENJOADtubtdo8llTaJy7HwBMeLNSSo2N5QIAgklslK1+Ir8r6CA==} - tapable@2.2.2: - resolution: {integrity: sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==} + tapable@2.3.0: + resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} engines: {node: '>=6'} tar@6.2.1: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} - tar@7.4.3: - resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} + tar@7.5.1: + resolution: {integrity: sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g==} engines: {node: '>=18'} terminal-link@3.0.0: @@ -5833,6 +5791,11 @@ packages: engines: {node: '>=10'} hasBin: true + terser@5.44.0: + resolution: {integrity: sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==} + engines: {node: '>=10'} + hasBin: true + text-extensions@2.4.0: resolution: {integrity: sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==} engines: {node: '>=8'} @@ -5856,6 +5819,10 @@ packages: resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} engines: {node: '>=12.0.0'} + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + tinypool@1.1.1: resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} engines: {node: ^18.0.0 || >=20.0.0} @@ -5868,11 +5835,11 @@ packages: resolution: {integrity: sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==} engines: {node: '>=14.0.0'} - tldts-core@6.1.86: - resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==} + tldts-core@7.0.17: + resolution: {integrity: sha512-DieYoGrP78PWKsrXr8MZwtQ7GLCUeLxihtjC1jZsW1DnvSMdKPitJSe8OSYDM2u5H6g3kWJZpePqkp43TfLh0g==} - tldts@6.1.86: - resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==} + tldts@7.0.17: + resolution: {integrity: sha512-Y1KQBgDd/NUc+LfOtKS6mNsC9CCaH+m2P1RoIZy7RAPo3C3/t8X45+zgut31cRZtZ3xKPjfn3TkGTrctC2TQIQ==} hasBin: true tmp@0.0.33: @@ -5891,13 +5858,13 @@ packages: resolution: {integrity: sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==} engines: {node: '>=0.8'} - tough-cookie@5.1.2: - resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==} + tough-cookie@6.0.0: + resolution: {integrity: sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==} engines: {node: '>=16'} - tr46@5.1.1: - resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} - engines: {node: '>=18'} + tr46@6.0.0: + resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} + engines: {node: '>=20'} ts-api-utils@2.1.0: resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} @@ -6000,6 +5967,13 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' + typescript-eslint@8.46.0: + resolution: {integrity: sha512-6+ZrB6y2bT2DX3K+Qd9vn7OFOJR+xSLDj+Aw/N3zBwUt27uTw2sw2TE2+UcY1RiyBZkaGbTkVg9SSdPNUG6aUw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + typescript@4.9.5: resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} engines: {node: '>=4.2.0'} @@ -6010,6 +5984,11 @@ packages: engines: {node: '>=14.17'} hasBin: true + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + ufo@1.6.1: resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} @@ -6058,10 +6037,6 @@ packages: universal-user-agent@6.0.1: resolution: {integrity: sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==} - universalify@0.1.2: - resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} - engines: {node: '>= 4.0.0'} - universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} @@ -6086,10 +6061,6 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - uuid@10.0.0: - resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} - hasBin: true - uuid@3.4.0: resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==} deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. @@ -6114,17 +6085,6 @@ packages: engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true - vite-plugin-static-copy@2.3.1: - resolution: {integrity: sha512-EfsPcBm3ewg3UMG8RJaC0ADq6/qnUZnokXx4By4+2cAcipjT9i0Y0owIJGqmZI7d6nxk4qB1q5aXOwNuSyPdyA==} - engines: {node: ^18.0.0 || >=20.0.0} - peerDependencies: - vite: ^5.0.0 || ^6.0.0 - - vite-plugin-top-level-await@1.6.0: - resolution: {integrity: sha512-bNhUreLamTIkoulCR9aDXbTbhLk6n1YE8NJUTTxl5RYskNRtzOR0ASzSjBVRtNdjIfngDXo11qOsybGLNsrdww==} - peerDependencies: - vite: '>=2.8' - vite-plugin-wasm@3.5.0: resolution: {integrity: sha512-X5VWgCnqiQEGb+omhlBVsvTfxikKtoOgAzQ95+BZ8gQ+VfMHIjSHr0wyvXFQCa0eKQ0fKyaL0kWcEnYqBac4lQ==} peerDependencies: @@ -6138,19 +6098,19 @@ packages: vite: optional: true - vite@6.3.5: - resolution: {integrity: sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + vite@7.1.9: + resolution: {integrity: sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@types/node': ^20.19.0 || >=22.12.0 jiti: '>=1.21.0' - less: '*' + less: ^4.0.0 lightningcss: ^1.21.0 - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 terser: ^5.16.0 tsx: ^4.8.1 yaml: ^2.4.2 @@ -6214,9 +6174,6 @@ packages: jsdom: optional: true - w3c-keyname@2.2.8: - resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} - w3c-xmlserializer@5.0.0: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} @@ -6228,9 +6185,9 @@ packages: wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} - webidl-conversions@7.0.0: - resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} - engines: {node: '>=12'} + webidl-conversions@8.0.0: + resolution: {integrity: sha512-n4W4YFyz5JzOfQeA8oN7dUYpR+MBP3PIUsn2jLjWXwK5ASUzt0Jc/A5sAUZoCYFJRGF0FBKJ+1JjN43rNdsQzA==} + engines: {node: '>=20'} whatwg-encoding@3.1.1: resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} @@ -6240,9 +6197,9 @@ packages: resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} engines: {node: '>=18'} - whatwg-url@14.2.0: - resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==} - engines: {node: '>=18'} + whatwg-url@15.1.0: + resolution: {integrity: sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==} + engines: {node: '>=20'} when-exit@2.1.4: resolution: {integrity: sha512-4rnvd3A1t16PWzrBUcSDZqcAmsUIy4minDXT/CZ8F2mVDgd65i4Aalimgz1aQkRGU0iH5eT5+6Rx2TK8o443Pg==} @@ -6386,8 +6343,8 @@ packages: resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==} engines: {node: '>=18'} - zimmerframe@1.1.2: - resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==} + zimmerframe@1.1.4: + resolution: {integrity: sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==} zod-package-json@1.2.0: resolution: {integrity: sha512-tamtgPM3MkP+obfO2dLr/G+nYoYkpJKmuHdYEy6IXRKfLybruoJ5NUj0lM0LxwOpC9PpoGLbll1ecoeyj43Wsg==} @@ -6396,25 +6353,36 @@ packages: zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} - zrender@5.6.1: - resolution: {integrity: sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==} + zrender@6.0.0: + resolution: {integrity: sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg==} snapshots: - '@adobe/css-tools@4.4.3': {} - '@ampproject/remapping@2.3.0': dependencies: - '@jridgewell/gen-mapping': 0.3.12 - '@jridgewell/trace-mapping': 0.3.29 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 - '@asamuzakjp/css-color@3.2.0': + '@asamuzakjp/css-color@4.0.5': dependencies: '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) - '@csstools/css-color-parser': 3.0.10(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) '@csstools/css-tokenizer': 3.0.4 - lru-cache: 10.4.3 + lru-cache: 11.2.2 + optional: true + + '@asamuzakjp/dom-selector@6.7.3': + dependencies: + '@asamuzakjp/nwsapi': 2.3.9 + bidi-js: 1.0.3 + css-tree: 3.1.0 + is-potential-custom-element-name: 1.0.1 + lru-cache: 11.2.2 + optional: true + + '@asamuzakjp/nwsapi@2.3.9': + optional: true '@babel/code-frame@7.27.1': dependencies: @@ -6437,7 +6405,7 @@ snapshots: '@babel/traverse': 7.28.0 '@babel/types': 7.28.2 convert-source-map: 2.0.0 - debug: 4.4.1 + debug: 4.4.3 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -6448,8 +6416,8 @@ snapshots: dependencies: '@babel/parser': 7.28.0 '@babel/types': 7.28.2 - '@jridgewell/gen-mapping': 0.3.12 - '@jridgewell/trace-mapping': 0.3.29 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 '@babel/helper-annotate-as-pure@7.27.3': @@ -6489,7 +6457,7 @@ snapshots: '@babel/core': 7.28.0 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 - debug: 4.4.1 + debug: 4.4.3 lodash.debounce: 4.0.8 resolve: 1.22.10 transitivePeerDependencies: @@ -7138,7 +7106,7 @@ snapshots: '@babel/parser': 7.28.0 '@babel/template': 7.27.2 '@babel/types': 7.28.2 - debug: 4.4.1 + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -7147,62 +7115,6 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@codemirror/autocomplete@6.18.6': - dependencies: - '@codemirror/language': 6.11.2 - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.1 - '@lezer/common': 1.2.3 - - '@codemirror/commands@6.8.1': - dependencies: - '@codemirror/language': 6.11.2 - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.1 - '@lezer/common': 1.2.3 - - '@codemirror/lang-javascript@6.2.4': - dependencies: - '@codemirror/autocomplete': 6.18.6 - '@codemirror/language': 6.11.2 - '@codemirror/lint': 6.8.5 - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.1 - '@lezer/common': 1.2.3 - '@lezer/javascript': 1.5.1 - - '@codemirror/language@6.11.2': - dependencies: - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.1 - '@lezer/common': 1.2.3 - '@lezer/highlight': 1.2.1 - '@lezer/lr': 1.4.2 - style-mod: 4.1.2 - - '@codemirror/lint@6.8.5': - dependencies: - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.1 - crelt: 1.0.6 - - '@codemirror/search@6.5.11': - dependencies: - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.1 - crelt: 1.0.6 - - '@codemirror/state@6.5.2': - dependencies: - '@marijn/find-cluster-break': 1.0.2 - - '@codemirror/view@6.38.1': - dependencies: - '@codemirror/state': 6.5.2 - crelt: 1.0.6 - style-mod: 4.1.2 - w3c-keyname: 2.2.8 - '@commitlint/cli@19.8.1(@types/node@22.16.5)(typescript@5.8.3)': dependencies: '@commitlint/format': 19.8.1 @@ -7317,133 +7229,163 @@ snapshots: dependencies: '@jridgewell/trace-mapping': 0.3.9 - '@csstools/color-helpers@5.0.2': {} + '@csstools/color-helpers@5.1.0': + optional: true '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': dependencies: '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) '@csstools/css-tokenizer': 3.0.4 + optional: true - '@csstools/css-color-parser@3.0.10(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + '@csstools/css-color-parser@3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': dependencies: - '@csstools/color-helpers': 5.0.2 + '@csstools/color-helpers': 5.1.0 '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) '@csstools/css-tokenizer': 3.0.4 + optional: true '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)': dependencies: '@csstools/css-tokenizer': 3.0.4 + optional: true - '@csstools/css-tokenizer@3.0.4': {} + '@csstools/css-syntax-patches-for-csstree@1.0.14(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + optional: true + + '@csstools/css-tokenizer@3.0.4': + optional: true - '@esbuild/aix-ppc64@0.25.8': + '@esbuild/aix-ppc64@0.25.10': optional: true - '@esbuild/android-arm64@0.25.8': + '@esbuild/android-arm64@0.25.10': optional: true - '@esbuild/android-arm@0.25.8': + '@esbuild/android-arm@0.25.10': optional: true - '@esbuild/android-x64@0.25.8': + '@esbuild/android-x64@0.25.10': optional: true - '@esbuild/darwin-arm64@0.25.8': + '@esbuild/darwin-arm64@0.25.10': optional: true - '@esbuild/darwin-x64@0.25.8': + '@esbuild/darwin-x64@0.25.10': optional: true - '@esbuild/freebsd-arm64@0.25.8': + '@esbuild/freebsd-arm64@0.25.10': optional: true - '@esbuild/freebsd-x64@0.25.8': + '@esbuild/freebsd-x64@0.25.10': optional: true - '@esbuild/linux-arm64@0.25.8': + '@esbuild/linux-arm64@0.25.10': optional: true - '@esbuild/linux-arm@0.25.8': + '@esbuild/linux-arm@0.25.10': optional: true - '@esbuild/linux-ia32@0.25.8': + '@esbuild/linux-ia32@0.25.10': optional: true - '@esbuild/linux-loong64@0.25.8': + '@esbuild/linux-loong64@0.25.10': optional: true - '@esbuild/linux-mips64el@0.25.8': + '@esbuild/linux-mips64el@0.25.10': optional: true - '@esbuild/linux-ppc64@0.25.8': + '@esbuild/linux-ppc64@0.25.10': optional: true - '@esbuild/linux-riscv64@0.25.8': + '@esbuild/linux-riscv64@0.25.10': optional: true - '@esbuild/linux-s390x@0.25.8': + '@esbuild/linux-s390x@0.25.10': optional: true - '@esbuild/linux-x64@0.25.8': + '@esbuild/linux-x64@0.25.10': optional: true - '@esbuild/netbsd-arm64@0.25.8': + '@esbuild/netbsd-arm64@0.25.10': optional: true - '@esbuild/netbsd-x64@0.25.8': + '@esbuild/netbsd-x64@0.25.10': optional: true - '@esbuild/openbsd-arm64@0.25.8': + '@esbuild/openbsd-arm64@0.25.10': optional: true - '@esbuild/openbsd-x64@0.25.8': + '@esbuild/openbsd-x64@0.25.10': optional: true - '@esbuild/openharmony-arm64@0.25.8': + '@esbuild/openharmony-arm64@0.25.10': optional: true - '@esbuild/sunos-x64@0.25.8': + '@esbuild/sunos-x64@0.25.10': optional: true - '@esbuild/win32-arm64@0.25.8': + '@esbuild/win32-arm64@0.25.10': optional: true - '@esbuild/win32-ia32@0.25.8': + '@esbuild/win32-ia32@0.25.10': optional: true - '@esbuild/win32-x64@0.25.8': + '@esbuild/win32-x64@0.25.10': optional: true - '@eslint-community/eslint-utils@4.7.0(eslint@9.31.0(jiti@2.5.1))': + '@eslint-community/eslint-utils@4.7.0(eslint@9.31.0(jiti@2.6.1))': dependencies: - eslint: 9.31.0(jiti@2.5.1) + eslint: 9.31.0(jiti@2.6.1) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/eslint-utils@4.9.0(eslint@9.31.0(jiti@2.6.1))': + dependencies: + eslint: 9.31.0(jiti@2.6.1) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/eslint-utils@4.9.0(eslint@9.37.0(jiti@2.6.1))': + dependencies: + eslint: 9.37.0(jiti@2.6.1) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} - '@eslint/compat@1.3.1(eslint@9.31.0(jiti@2.5.1))': + '@eslint/compat@1.4.0(eslint@9.37.0(jiti@2.6.1))': + dependencies: + '@eslint/core': 0.16.0 optionalDependencies: - eslint: 9.31.0(jiti@2.5.1) + eslint: 9.37.0(jiti@2.6.1) '@eslint/config-array@0.21.0': dependencies: '@eslint/object-schema': 2.1.6 - debug: 4.4.1 + debug: 4.4.3 minimatch: 3.1.2 transitivePeerDependencies: - supports-color '@eslint/config-helpers@0.3.0': {} + '@eslint/config-helpers@0.4.0': + dependencies: + '@eslint/core': 0.16.0 + '@eslint/core@0.15.1': dependencies: '@types/json-schema': 7.0.15 + '@eslint/core@0.16.0': + dependencies: + '@types/json-schema': 7.0.15 + '@eslint/eslintrc@3.3.1': dependencies: ajv: 6.12.6 - debug: 4.4.1 + debug: 4.4.3 espree: 10.4.0 globals: 14.0.0 ignore: 5.3.2 @@ -7456,6 +7398,8 @@ snapshots: '@eslint/js@9.31.0': {} + '@eslint/js@9.37.0': {} + '@eslint/object-schema@2.1.6': {} '@eslint/plugin-kit@0.3.4': @@ -7463,6 +7407,11 @@ snapshots: '@eslint/core': 0.15.1 levn: 0.4.1 + '@eslint/plugin-kit@0.4.0': + dependencies: + '@eslint/core': 0.16.0 + levn: 0.4.1 + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.6': @@ -7470,6 +7419,11 @@ snapshots: '@humanfs/core': 0.19.1 '@humanwhocodes/retry': 0.3.1 + '@humanfs/node@0.16.7': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.4.3 + '@humanwhocodes/module-importer@1.0.1': {} '@humanwhocodes/retry@0.3.1': {} @@ -7611,29 +7565,34 @@ snapshots: dependencies: minipass: 7.1.2 - '@jridgewell/gen-mapping@0.3.12': + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': dependencies: - '@jridgewell/sourcemap-codec': 1.5.4 - '@jridgewell/trace-mapping': 0.3.29 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/source-map@0.3.10': dependencies: - '@jridgewell/gen-mapping': 0.3.12 - '@jridgewell/trace-mapping': 0.3.29 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 - '@jridgewell/sourcemap-codec@1.5.4': {} + '@jridgewell/sourcemap-codec@1.5.5': {} - '@jridgewell/trace-mapping@0.3.29': + '@jridgewell/trace-mapping@0.3.31': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.4 + '@jridgewell/sourcemap-codec': 1.5.5 '@jridgewell/trace-mapping@0.3.9': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.4 + '@jridgewell/sourcemap-codec': 1.5.5 '@jsdevtools/ez-spawn@3.0.4': dependencies: @@ -7642,31 +7601,6 @@ snapshots: string-argv: 0.3.2 type-detect: 4.1.0 - '@kurkle/color@0.3.4': {} - - '@lezer/common@1.2.3': {} - - '@lezer/generator@1.8.0': - dependencies: - '@lezer/common': 1.2.3 - '@lezer/lr': 1.4.2 - - '@lezer/highlight@1.2.1': - dependencies: - '@lezer/common': 1.2.3 - - '@lezer/javascript@1.5.1': - dependencies: - '@lezer/common': 1.2.3 - '@lezer/highlight': 1.2.1 - '@lezer/lr': 1.4.2 - - '@lezer/lr@1.4.2': - dependencies: - '@lezer/common': 1.2.3 - - '@marijn/find-cluster-break@1.0.2': {} - '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -7850,10 +7784,6 @@ snapshots: rollup: 4.46.2 tslib: 2.8.1 - '@rollup/plugin-virtual@3.0.2(rollup@4.45.1)': - optionalDependencies: - rollup: 4.45.1 - '@rollup/plugin-wasm@6.2.2(rollup@4.46.2)': dependencies: '@rollup/pluginutils': 5.2.0(rollup@4.46.2) @@ -7880,126 +7810,132 @@ snapshots: optionalDependencies: rollup: 4.46.2 - '@rollup/rollup-android-arm-eabi@4.45.1': - optional: true - '@rollup/rollup-android-arm-eabi@4.46.2': optional: true - '@rollup/rollup-android-arm64@4.45.1': + '@rollup/rollup-android-arm-eabi@4.52.4': optional: true '@rollup/rollup-android-arm64@4.46.2': optional: true - '@rollup/rollup-darwin-arm64@4.45.1': + '@rollup/rollup-android-arm64@4.52.4': optional: true '@rollup/rollup-darwin-arm64@4.46.2': optional: true - '@rollup/rollup-darwin-x64@4.45.1': + '@rollup/rollup-darwin-arm64@4.52.4': optional: true '@rollup/rollup-darwin-x64@4.46.2': optional: true - '@rollup/rollup-freebsd-arm64@4.45.1': + '@rollup/rollup-darwin-x64@4.52.4': optional: true '@rollup/rollup-freebsd-arm64@4.46.2': optional: true - '@rollup/rollup-freebsd-x64@4.45.1': + '@rollup/rollup-freebsd-arm64@4.52.4': optional: true '@rollup/rollup-freebsd-x64@4.46.2': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.45.1': + '@rollup/rollup-freebsd-x64@4.52.4': optional: true '@rollup/rollup-linux-arm-gnueabihf@4.46.2': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.45.1': + '@rollup/rollup-linux-arm-gnueabihf@4.52.4': optional: true '@rollup/rollup-linux-arm-musleabihf@4.46.2': optional: true - '@rollup/rollup-linux-arm64-gnu@4.45.1': + '@rollup/rollup-linux-arm-musleabihf@4.52.4': optional: true '@rollup/rollup-linux-arm64-gnu@4.46.2': optional: true - '@rollup/rollup-linux-arm64-musl@4.45.1': + '@rollup/rollup-linux-arm64-gnu@4.52.4': optional: true '@rollup/rollup-linux-arm64-musl@4.46.2': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.45.1': + '@rollup/rollup-linux-arm64-musl@4.52.4': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.46.2': + '@rollup/rollup-linux-loong64-gnu@4.52.4': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.45.1': + '@rollup/rollup-linux-loongarch64-gnu@4.46.2': optional: true '@rollup/rollup-linux-ppc64-gnu@4.46.2': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.45.1': + '@rollup/rollup-linux-ppc64-gnu@4.52.4': optional: true '@rollup/rollup-linux-riscv64-gnu@4.46.2': optional: true - '@rollup/rollup-linux-riscv64-musl@4.45.1': + '@rollup/rollup-linux-riscv64-gnu@4.52.4': optional: true '@rollup/rollup-linux-riscv64-musl@4.46.2': optional: true - '@rollup/rollup-linux-s390x-gnu@4.45.1': + '@rollup/rollup-linux-riscv64-musl@4.52.4': optional: true '@rollup/rollup-linux-s390x-gnu@4.46.2': optional: true - '@rollup/rollup-linux-x64-gnu@4.45.1': + '@rollup/rollup-linux-s390x-gnu@4.52.4': optional: true '@rollup/rollup-linux-x64-gnu@4.46.2': optional: true - '@rollup/rollup-linux-x64-musl@4.45.1': + '@rollup/rollup-linux-x64-gnu@4.52.4': optional: true '@rollup/rollup-linux-x64-musl@4.46.2': optional: true - '@rollup/rollup-win32-arm64-msvc@4.45.1': + '@rollup/rollup-linux-x64-musl@4.52.4': + optional: true + + '@rollup/rollup-openharmony-arm64@4.52.4': optional: true '@rollup/rollup-win32-arm64-msvc@4.46.2': optional: true - '@rollup/rollup-win32-ia32-msvc@4.45.1': + '@rollup/rollup-win32-arm64-msvc@4.52.4': optional: true '@rollup/rollup-win32-ia32-msvc@4.46.2': optional: true - '@rollup/rollup-win32-x64-msvc@4.45.1': + '@rollup/rollup-win32-ia32-msvc@4.52.4': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.52.4': optional: true '@rollup/rollup-win32-x64-msvc@4.46.2': optional: true + '@rollup/rollup-win32-x64-msvc@4.52.4': + optional: true + '@samverschueren/stream-to-observable@0.3.1(rxjs@6.6.7)': dependencies: any-observable: 0.3.0(rxjs@6.6.7) @@ -8010,6 +7946,8 @@ snapshots: '@sindresorhus/merge-streams@2.3.0': {} + '@standard-schema/spec@1.0.0': {} + '@surma/rollup-plugin-off-main-thread@2.2.3': dependencies: ejs: 3.1.10 @@ -8017,51 +7955,51 @@ snapshots: magic-string: 0.25.9 string.prototype.matchall: 4.0.12 - '@sveltejs/acorn-typescript@1.0.5(acorn@8.15.0)': + '@sveltejs/acorn-typescript@1.0.6(acorn@8.15.0)': dependencies: acorn: 8.15.0 - '@sveltejs/adapter-static@3.0.8(@sveltejs/kit@2.26.0(@sveltejs/vite-plugin-svelte@6.1.0(svelte@5.36.16)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)))(svelte@5.36.16)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)))': + '@sveltejs/adapter-static@3.0.10(@sveltejs/kit@2.46.4(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0)))(svelte@5.39.11)(vite@7.1.9(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0)))': dependencies: - '@sveltejs/kit': 2.26.0(@sveltejs/vite-plugin-svelte@6.1.0(svelte@5.36.16)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)))(svelte@5.36.16)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)) + '@sveltejs/kit': 2.46.4(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0)))(svelte@5.39.11)(vite@7.1.9(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0)) - '@sveltejs/kit@2.26.0(@sveltejs/vite-plugin-svelte@6.1.0(svelte@5.36.16)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)))(svelte@5.36.16)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0))': + '@sveltejs/kit@2.46.4(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0)))(svelte@5.39.11)(vite@7.1.9(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0))': dependencies: - '@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0) - '@sveltejs/vite-plugin-svelte': 6.1.0(svelte@5.36.16)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)) + '@standard-schema/spec': 1.0.0 + '@sveltejs/acorn-typescript': 1.0.6(acorn@8.15.0) + '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0)) '@types/cookie': 0.6.0 acorn: 8.15.0 cookie: 0.6.0 - devalue: 5.1.1 + devalue: 5.3.2 esm-env: 1.2.2 kleur: 4.1.5 - magic-string: 0.30.17 + magic-string: 0.30.19 mrmime: 2.0.1 sade: 1.8.1 set-cookie-parser: 2.7.1 - sirv: 3.0.1 - svelte: 5.36.16 - vite: 6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) + sirv: 3.0.2 + svelte: 5.39.11 + vite: 7.1.9(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0) - '@sveltejs/vite-plugin-svelte-inspector@5.0.0(@sveltejs/vite-plugin-svelte@6.1.0(svelte@5.36.16)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)))(svelte@5.36.16)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0))': + '@sveltejs/vite-plugin-svelte-inspector@5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0)))(svelte@5.39.11)(vite@7.1.9(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0))': dependencies: - '@sveltejs/vite-plugin-svelte': 6.1.0(svelte@5.36.16)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)) - debug: 4.4.1 - svelte: 5.36.16 - vite: 6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) + '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0)) + debug: 4.4.3 + svelte: 5.39.11 + vite: 7.1.9(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@6.1.0(svelte@5.36.16)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0))': + '@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 5.0.0(@sveltejs/vite-plugin-svelte@6.1.0(svelte@5.36.16)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)))(svelte@5.36.16)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)) - debug: 4.4.1 + '@sveltejs/vite-plugin-svelte-inspector': 5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0)))(svelte@5.39.11)(vite@7.1.9(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0)) + debug: 4.4.3 deepmerge: 4.3.1 - kleur: 4.1.5 - magic-string: 0.30.17 - svelte: 5.36.16 - vite: 6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) - vitefu: 1.1.1(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)) + magic-string: 0.30.19 + svelte: 5.39.11 + vite: 7.1.9(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0) + vitefu: 1.1.1(vite@7.1.9(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0)) transitivePeerDependencies: - supports-color @@ -8098,7 +8036,7 @@ snapshots: '@swc/core@1.13.2': dependencies: '@swc/counter': 0.1.3 - '@swc/types': 0.1.23 + '@swc/types': 0.1.25 optionalDependencies: '@swc/core-darwin-arm64': 1.13.2 '@swc/core-darwin-x64': 1.13.2 @@ -8110,85 +8048,89 @@ snapshots: '@swc/core-win32-arm64-msvc': 1.13.2 '@swc/core-win32-ia32-msvc': 1.13.2 '@swc/core-win32-x64-msvc': 1.13.2 + optional: true - '@swc/counter@0.1.3': {} + '@swc/counter@0.1.3': + optional: true - '@swc/types@0.1.23': + '@swc/types@0.1.25': dependencies: '@swc/counter': 0.1.3 + optional: true - '@swc/wasm@1.13.2': {} + '@swc/wasm@1.13.2': + optional: true - '@tailwindcss/node@4.1.11': + '@tailwindcss/node@4.1.14': dependencies: - '@ampproject/remapping': 2.3.0 - enhanced-resolve: 5.18.2 - jiti: 2.5.1 + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.18.3 + jiti: 2.6.1 lightningcss: 1.30.1 - magic-string: 0.30.17 + magic-string: 0.30.19 source-map-js: 1.2.1 - tailwindcss: 4.1.11 + tailwindcss: 4.1.14 - '@tailwindcss/oxide-android-arm64@4.1.11': + '@tailwindcss/oxide-android-arm64@4.1.14': optional: true - '@tailwindcss/oxide-darwin-arm64@4.1.11': + '@tailwindcss/oxide-darwin-arm64@4.1.14': optional: true - '@tailwindcss/oxide-darwin-x64@4.1.11': + '@tailwindcss/oxide-darwin-x64@4.1.14': optional: true - '@tailwindcss/oxide-freebsd-x64@4.1.11': + '@tailwindcss/oxide-freebsd-x64@4.1.14': optional: true - '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11': + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.14': optional: true - '@tailwindcss/oxide-linux-arm64-gnu@4.1.11': + '@tailwindcss/oxide-linux-arm64-gnu@4.1.14': optional: true - '@tailwindcss/oxide-linux-arm64-musl@4.1.11': + '@tailwindcss/oxide-linux-arm64-musl@4.1.14': optional: true - '@tailwindcss/oxide-linux-x64-gnu@4.1.11': + '@tailwindcss/oxide-linux-x64-gnu@4.1.14': optional: true - '@tailwindcss/oxide-linux-x64-musl@4.1.11': + '@tailwindcss/oxide-linux-x64-musl@4.1.14': optional: true - '@tailwindcss/oxide-wasm32-wasi@4.1.11': + '@tailwindcss/oxide-wasm32-wasi@4.1.14': optional: true - '@tailwindcss/oxide-win32-arm64-msvc@4.1.11': + '@tailwindcss/oxide-win32-arm64-msvc@4.1.14': optional: true - '@tailwindcss/oxide-win32-x64-msvc@4.1.11': + '@tailwindcss/oxide-win32-x64-msvc@4.1.14': optional: true - '@tailwindcss/oxide@4.1.11': + '@tailwindcss/oxide@4.1.14': dependencies: - detect-libc: 2.0.4 - tar: 7.4.3 + detect-libc: 2.1.1 + tar: 7.5.1 optionalDependencies: - '@tailwindcss/oxide-android-arm64': 4.1.11 - '@tailwindcss/oxide-darwin-arm64': 4.1.11 - '@tailwindcss/oxide-darwin-x64': 4.1.11 - '@tailwindcss/oxide-freebsd-x64': 4.1.11 - '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.11 - '@tailwindcss/oxide-linux-arm64-gnu': 4.1.11 - '@tailwindcss/oxide-linux-arm64-musl': 4.1.11 - '@tailwindcss/oxide-linux-x64-gnu': 4.1.11 - '@tailwindcss/oxide-linux-x64-musl': 4.1.11 - '@tailwindcss/oxide-wasm32-wasi': 4.1.11 - '@tailwindcss/oxide-win32-arm64-msvc': 4.1.11 - '@tailwindcss/oxide-win32-x64-msvc': 4.1.11 - - '@tailwindcss/vite@4.1.11(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0))': - dependencies: - '@tailwindcss/node': 4.1.11 - '@tailwindcss/oxide': 4.1.11 - tailwindcss: 4.1.11 - vite: 6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) + '@tailwindcss/oxide-android-arm64': 4.1.14 + '@tailwindcss/oxide-darwin-arm64': 4.1.14 + '@tailwindcss/oxide-darwin-x64': 4.1.14 + '@tailwindcss/oxide-freebsd-x64': 4.1.14 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.14 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.14 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.14 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.14 + '@tailwindcss/oxide-linux-x64-musl': 4.1.14 + '@tailwindcss/oxide-wasm32-wasi': 4.1.14 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.14 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.14 + + '@tailwindcss/vite@4.1.14(vite@7.1.9(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0))': + dependencies: + '@tailwindcss/node': 4.1.14 + '@tailwindcss/oxide': 4.1.14 + tailwindcss: 4.1.14 + vite: 7.1.9(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0) '@testing-library/dom@10.4.0': dependencies: @@ -8201,24 +8143,6 @@ snapshots: lz-string: 1.5.0 pretty-format: 27.5.1 - '@testing-library/jest-dom@6.6.3': - dependencies: - '@adobe/css-tools': 4.4.3 - aria-query: 5.3.2 - chalk: 3.0.0 - css.escape: 1.5.1 - dom-accessibility-api: 0.6.3 - lodash: 4.17.21 - redent: 3.0.0 - - '@testing-library/svelte@5.2.8(svelte@5.36.16)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0))(vitest@3.2.4)': - dependencies: - '@testing-library/dom': 10.4.0 - svelte: 5.36.16 - optionalDependencies: - vite: 6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) - vitest: 3.2.4(@types/node@24.1.0)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(jiti@2.5.1)(jsdom@26.1.0)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) - '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.0)': dependencies: '@testing-library/dom': 10.4.0 @@ -8251,30 +8175,14 @@ snapshots: '@types/estree@1.0.8': {} - '@types/fs-extra@8.1.5': + '@types/glob@9.0.0': dependencies: - '@types/node': 24.1.0 - - '@types/glob@7.2.0': - dependencies: - '@types/minimatch': 6.0.0 - '@types/node': 24.1.0 - - '@types/glob@8.1.0': - dependencies: - '@types/minimatch': 5.1.2 - '@types/node': 24.1.0 + glob: 11.0.3 '@types/json-schema@7.0.15': {} '@types/katex@0.16.7': {} - '@types/minimatch@5.1.2': {} - - '@types/minimatch@6.0.0': - dependencies: - minimatch: 10.0.3 - '@types/node@22.16.5': dependencies: undici-types: 6.21.0 @@ -8282,6 +8190,7 @@ snapshots: '@types/node@24.1.0': dependencies: undici-types: 7.8.0 + optional: true '@types/normalize-package-data@2.4.4': {} @@ -8293,15 +8202,15 @@ snapshots: '@types/resolve@1.20.2': {} - '@typescript-eslint/eslint-plugin@8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.31.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.31.0(jiti@2.5.1))(typescript@5.8.3)': + '@typescript-eslint/eslint-plugin@8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.3))(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.38.0(eslint@9.31.0(jiti@2.5.1))(typescript@5.8.3) + '@typescript-eslint/parser': 8.38.0(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.3) '@typescript-eslint/scope-manager': 8.38.0 - '@typescript-eslint/type-utils': 8.38.0(eslint@9.31.0(jiti@2.5.1))(typescript@5.8.3) - '@typescript-eslint/utils': 8.38.0(eslint@9.31.0(jiti@2.5.1))(typescript@5.8.3) + '@typescript-eslint/type-utils': 8.38.0(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.3) + '@typescript-eslint/utils': 8.38.0(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.3) '@typescript-eslint/visitor-keys': 8.38.0 - eslint: 9.31.0(jiti@2.5.1) + eslint: 9.31.0(jiti@2.6.1) graphemer: 1.4.0 ignore: 7.0.5 natural-compare: 1.4.0 @@ -8310,50 +8219,131 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.38.0(eslint@9.31.0(jiti@2.5.1))(typescript@5.8.3)': + '@typescript-eslint/eslint-plugin@8.46.0(@typescript-eslint/parser@8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.46.0 + '@typescript-eslint/type-utils': 8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.46.0 + eslint: 9.37.0(jiti@2.6.1) + graphemer: 1.4.0 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.38.0(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.3)': dependencies: '@typescript-eslint/scope-manager': 8.38.0 '@typescript-eslint/types': 8.38.0 '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.8.3) '@typescript-eslint/visitor-keys': 8.38.0 debug: 4.4.1 - eslint: 9.31.0(jiti@2.5.1) + eslint: 9.31.0(jiti@2.6.1) typescript: 5.8.3 transitivePeerDependencies: - supports-color + '@typescript-eslint/parser@8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.46.0 + '@typescript-eslint/types': 8.46.0 + '@typescript-eslint/typescript-estree': 8.46.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.46.0 + debug: 4.4.3 + eslint: 9.37.0(jiti@2.6.1) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/project-service@8.38.0(typescript@5.8.3)': dependencies: '@typescript-eslint/tsconfig-utils': 8.38.0(typescript@5.8.3) '@typescript-eslint/types': 8.38.0 - debug: 4.4.1 + debug: 4.4.3 typescript: 5.8.3 transitivePeerDependencies: - supports-color + '@typescript-eslint/project-service@8.45.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.45.0(typescript@5.9.3) + '@typescript-eslint/types': 8.45.0 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.46.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.46.0(typescript@5.9.3) + '@typescript-eslint/types': 8.46.0 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/scope-manager@8.38.0': dependencies: '@typescript-eslint/types': 8.38.0 '@typescript-eslint/visitor-keys': 8.38.0 + '@typescript-eslint/scope-manager@8.45.0': + dependencies: + '@typescript-eslint/types': 8.45.0 + '@typescript-eslint/visitor-keys': 8.45.0 + + '@typescript-eslint/scope-manager@8.46.0': + dependencies: + '@typescript-eslint/types': 8.46.0 + '@typescript-eslint/visitor-keys': 8.46.0 + '@typescript-eslint/tsconfig-utils@8.38.0(typescript@5.8.3)': dependencies: typescript: 5.8.3 - '@typescript-eslint/type-utils@8.38.0(eslint@9.31.0(jiti@2.5.1))(typescript@5.8.3)': + '@typescript-eslint/tsconfig-utils@8.45.0(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/tsconfig-utils@8.46.0(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.38.0(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.3)': dependencies: '@typescript-eslint/types': 8.38.0 '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.8.3) - '@typescript-eslint/utils': 8.38.0(eslint@9.31.0(jiti@2.5.1))(typescript@5.8.3) - debug: 4.4.1 - eslint: 9.31.0(jiti@2.5.1) + '@typescript-eslint/utils': 8.38.0(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.3) + debug: 4.4.3 + eslint: 9.31.0(jiti@2.6.1) ts-api-utils: 2.1.0(typescript@5.8.3) typescript: 5.8.3 transitivePeerDependencies: - supports-color + '@typescript-eslint/type-utils@8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.46.0 + '@typescript-eslint/typescript-estree': 8.46.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.37.0(jiti@2.6.1) + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/types@8.38.0': {} + '@typescript-eslint/types@8.45.0': {} + + '@typescript-eslint/types@8.46.0': {} + '@typescript-eslint/typescript-estree@8.38.0(typescript@5.8.3)': dependencies: '@typescript-eslint/project-service': 8.38.0(typescript@5.8.3) @@ -8370,51 +8360,96 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.38.0(eslint@9.31.0(jiti@2.5.1))(typescript@5.8.3)': + '@typescript-eslint/typescript-estree@8.45.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.45.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.45.0(typescript@5.9.3) + '@typescript-eslint/types': 8.45.0 + '@typescript-eslint/visitor-keys': 8.45.0 + debug: 4.4.3 + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.2 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/typescript-estree@8.46.0(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0(jiti@2.5.1)) + '@typescript-eslint/project-service': 8.46.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.46.0(typescript@5.9.3) + '@typescript-eslint/types': 8.46.0 + '@typescript-eslint/visitor-keys': 8.46.0 + debug: 4.4.3 + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.3 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.38.0(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.31.0(jiti@2.6.1)) '@typescript-eslint/scope-manager': 8.38.0 '@typescript-eslint/types': 8.38.0 '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.8.3) - eslint: 9.31.0(jiti@2.5.1) + eslint: 9.31.0(jiti@2.6.1) typescript: 5.8.3 transitivePeerDependencies: - supports-color + '@typescript-eslint/utils@8.45.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.37.0(jiti@2.6.1)) + '@typescript-eslint/scope-manager': 8.45.0 + '@typescript-eslint/types': 8.45.0 + '@typescript-eslint/typescript-estree': 8.45.0(typescript@5.9.3) + eslint: 9.37.0(jiti@2.6.1) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.37.0(jiti@2.6.1)) + '@typescript-eslint/scope-manager': 8.46.0 + '@typescript-eslint/types': 8.46.0 + '@typescript-eslint/typescript-estree': 8.46.0(typescript@5.9.3) + eslint: 9.37.0(jiti@2.6.1) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/visitor-keys@8.38.0': dependencies: '@typescript-eslint/types': 8.38.0 eslint-visitor-keys: 4.2.1 - '@vitest/browser@3.2.4(playwright@1.55.0)(vite@6.3.5(@types/node@22.16.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0))(vitest@3.2.4)': + '@typescript-eslint/visitor-keys@8.45.0': dependencies: - '@testing-library/dom': 10.4.0 - '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.0) - '@vitest/mocker': 3.2.4(vite@6.3.5(@types/node@22.16.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)) - '@vitest/utils': 3.2.4 - magic-string: 0.30.17 - sirv: 3.0.1 - tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/node@22.16.5)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(jiti@2.5.1)(jsdom@26.1.0)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) - ws: 8.18.3 - optionalDependencies: - playwright: 1.55.0 - transitivePeerDependencies: - - bufferutil - - msw - - utf-8-validate - - vite + '@typescript-eslint/types': 8.45.0 + eslint-visitor-keys: 4.2.1 - '@vitest/browser@3.2.4(playwright@1.55.0)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0))(vitest@3.2.4)': + '@typescript-eslint/visitor-keys@8.46.0': + dependencies: + '@typescript-eslint/types': 8.46.0 + eslint-visitor-keys: 4.2.1 + + '@vitest/browser@3.2.4(playwright@1.55.0)(vite@7.1.9(@types/node@22.16.5)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0))(vitest@3.2.4)': dependencies: '@testing-library/dom': 10.4.0 '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.0) - '@vitest/mocker': 3.2.4(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)) + '@vitest/mocker': 3.2.4(vite@7.1.9(@types/node@22.16.5)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0)) '@vitest/utils': 3.2.4 magic-string: 0.30.17 sirv: 3.0.1 tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/node@24.1.0)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(jiti@2.5.1)(jsdom@26.1.0)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) + vitest: 3.2.4(@types/node@22.16.5)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0) ws: 8.18.3 optionalDependencies: playwright: 1.55.0 @@ -8423,7 +8458,6 @@ snapshots: - msw - utf-8-validate - vite - optional: true '@vitest/expect@3.2.4': dependencies: @@ -8433,21 +8467,13 @@ snapshots: chai: 5.2.1 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.4(vite@6.3.5(@types/node@22.16.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0))': + '@vitest/mocker@3.2.4(vite@7.1.9(@types/node@22.16.5)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 - magic-string: 0.30.17 + magic-string: 0.30.19 optionalDependencies: - vite: 6.3.5(@types/node@22.16.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) - - '@vitest/mocker@3.2.4(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0))': - dependencies: - '@vitest/spy': 3.2.4 - estree-walker: 3.0.3 - magic-string: 0.30.17 - optionalDependencies: - vite: 6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) + vite: 7.1.9(@types/node@22.16.5)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0) '@vitest/pretty-format@3.2.4': dependencies: @@ -8462,7 +8488,7 @@ snapshots: '@vitest/snapshot@3.2.4': dependencies: '@vitest/pretty-format': 3.2.4 - magic-string: 0.30.17 + magic-string: 0.30.19 pathe: 2.0.3 '@vitest/spy@3.2.4': @@ -8478,7 +8504,7 @@ snapshots: sirv: 3.0.1 tinyglobby: 0.2.14 tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/node@22.16.5)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(jiti@2.5.1)(jsdom@26.1.0)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) + vitest: 3.2.4(@types/node@22.16.5)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0) '@vitest/utils@3.2.4': dependencies: @@ -8486,7 +8512,7 @@ snapshots: loupe: 3.2.0 tinyrainbow: 2.0.0 - '@webgpu/types@0.1.64': {} + '@webgpu/types@0.1.65': {} JSONStream@1.3.5: dependencies: @@ -8503,7 +8529,8 @@ snapshots: acorn@8.15.0: {} - agent-base@7.1.4: {} + agent-base@7.1.4: + optional: true ajv@6.12.6: dependencies: @@ -8699,6 +8726,11 @@ snapshots: before-after-hook@2.2.3: {} + bidi-js@1.0.3: + dependencies: + require-from-string: 2.0.2 + optional: true + binary-extensions@2.3.0: {} binary-install@1.1.0: @@ -8831,11 +8863,6 @@ snapshots: escape-string-regexp: 1.0.5 supports-color: 5.5.0 - chalk@3.0.0: - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - chalk@4.1.2: dependencies: ansi-styles: 4.3.0 @@ -8845,10 +8872,6 @@ snapshots: chardet@0.7.0: {} - chart.js@4.5.0: - dependencies: - '@kurkle/color': 0.3.4 - check-error@2.1.1: {} chokidar@3.6.0: @@ -8915,16 +8938,6 @@ snapshots: code-point-at@1.1.0: {} - codemirror@6.0.2: - dependencies: - '@codemirror/autocomplete': 6.18.6 - '@codemirror/commands': 6.8.1 - '@codemirror/language': 6.11.2 - '@codemirror/lint': 6.8.5 - '@codemirror/search': 6.5.11 - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.1 - color-convert@1.9.3: dependencies: color-name: 1.1.3 @@ -8939,8 +8952,6 @@ snapshots: colord@2.9.3: {} - colorette@1.4.0: {} - colorette@2.0.20: {} combined-stream@1.0.8: @@ -9035,7 +9046,7 @@ snapshots: dependencies: '@types/node': 22.16.5 cosmiconfig: 9.0.0(typescript@5.8.3) - jiti: 2.5.1 + jiti: 2.6.1 typescript: 5.8.3 cosmiconfig@7.1.0: @@ -9074,8 +9085,6 @@ snapshots: create-require@1.1.1: {} - crelt@1.0.6: {} - cross-env@7.0.3: dependencies: cross-spawn: 7.0.6 @@ -9111,9 +9120,13 @@ snapshots: mdn-data: 2.0.14 source-map: 0.6.1 - css-what@6.2.2: {} + css-tree@3.1.0: + dependencies: + mdn-data: 2.12.2 + source-map-js: 1.2.1 + optional: true - css.escape@1.5.1: {} + css-what@6.2.2: {} cssesc@3.0.0: {} @@ -9165,10 +9178,14 @@ snapshots: dependencies: css-tree: 1.1.3 - cssstyle@4.6.0: + cssstyle@5.3.1(postcss@8.5.6): dependencies: - '@asamuzakjp/css-color': 3.2.0 - rrweb-cssom: 0.8.0 + '@asamuzakjp/css-color': 4.0.5 + '@csstools/css-syntax-patches-for-csstree': 1.0.14(postcss@8.5.6) + css-tree: 3.1.0 + transitivePeerDependencies: + - postcss + optional: true cz-conventional-changelog@3.3.0(@types/node@22.16.5)(typescript@5.8.3): dependencies: @@ -9190,10 +9207,11 @@ snapshots: dependencies: assert-plus: 1.0.0 - data-urls@5.0.0: + data-urls@6.0.0: dependencies: whatwg-mimetype: 4.0.0 - whatwg-url: 14.2.0 + whatwg-url: 15.1.0 + optional: true data-view-buffer@1.0.2: dependencies: @@ -9219,7 +9237,12 @@ snapshots: dependencies: ms: 2.1.3 - decimal.js@10.6.0: {} + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decimal.js@10.6.0: + optional: true decode-uri-component@0.4.1: {} @@ -9240,8 +9263,6 @@ snapshots: bundle-name: 4.1.0 default-browser-id: 5.0.0 - default-passive-events@2.0.0: {} - defaults@1.0.4: dependencies: clone: 1.0.4 @@ -9281,9 +9302,9 @@ snapshots: detect-indent@6.1.0: {} - detect-libc@2.0.4: {} + detect-libc@2.1.1: {} - devalue@5.1.1: {} + devalue@5.3.2: {} diff@4.0.2: {} @@ -9293,8 +9314,6 @@ snapshots: dom-accessibility-api@0.5.16: {} - dom-accessibility-api@0.6.3: {} - dom-serializer@1.4.1: dependencies: domelementtype: 2.3.0 @@ -9338,10 +9357,10 @@ snapshots: jsbn: 0.1.1 safer-buffer: 2.1.2 - echarts@5.6.0: + echarts@6.0.0: dependencies: tslib: 2.3.0 - zrender: 5.6.1 + zrender: 6.0.0 ejs@3.1.10: dependencies: @@ -9361,14 +9380,15 @@ snapshots: dependencies: once: 1.4.0 - enhanced-resolve@5.18.2: + enhanced-resolve@5.18.3: dependencies: graceful-fs: 4.2.11 - tapable: 2.2.2 + tapable: 2.3.0 entities@2.2.0: {} - entities@6.0.1: {} + entities@6.0.1: + optional: true env-paths@2.2.1: {} @@ -9458,34 +9478,34 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 - esbuild@0.25.8: + esbuild@0.25.10: optionalDependencies: - '@esbuild/aix-ppc64': 0.25.8 - '@esbuild/android-arm': 0.25.8 - '@esbuild/android-arm64': 0.25.8 - '@esbuild/android-x64': 0.25.8 - '@esbuild/darwin-arm64': 0.25.8 - '@esbuild/darwin-x64': 0.25.8 - '@esbuild/freebsd-arm64': 0.25.8 - '@esbuild/freebsd-x64': 0.25.8 - '@esbuild/linux-arm': 0.25.8 - '@esbuild/linux-arm64': 0.25.8 - '@esbuild/linux-ia32': 0.25.8 - '@esbuild/linux-loong64': 0.25.8 - '@esbuild/linux-mips64el': 0.25.8 - '@esbuild/linux-ppc64': 0.25.8 - '@esbuild/linux-riscv64': 0.25.8 - '@esbuild/linux-s390x': 0.25.8 - '@esbuild/linux-x64': 0.25.8 - '@esbuild/netbsd-arm64': 0.25.8 - '@esbuild/netbsd-x64': 0.25.8 - '@esbuild/openbsd-arm64': 0.25.8 - '@esbuild/openbsd-x64': 0.25.8 - '@esbuild/openharmony-arm64': 0.25.8 - '@esbuild/sunos-x64': 0.25.8 - '@esbuild/win32-arm64': 0.25.8 - '@esbuild/win32-ia32': 0.25.8 - '@esbuild/win32-x64': 0.25.8 + '@esbuild/aix-ppc64': 0.25.10 + '@esbuild/android-arm': 0.25.10 + '@esbuild/android-arm64': 0.25.10 + '@esbuild/android-x64': 0.25.10 + '@esbuild/darwin-arm64': 0.25.10 + '@esbuild/darwin-x64': 0.25.10 + '@esbuild/freebsd-arm64': 0.25.10 + '@esbuild/freebsd-x64': 0.25.10 + '@esbuild/linux-arm': 0.25.10 + '@esbuild/linux-arm64': 0.25.10 + '@esbuild/linux-ia32': 0.25.10 + '@esbuild/linux-loong64': 0.25.10 + '@esbuild/linux-mips64el': 0.25.10 + '@esbuild/linux-ppc64': 0.25.10 + '@esbuild/linux-riscv64': 0.25.10 + '@esbuild/linux-s390x': 0.25.10 + '@esbuild/linux-x64': 0.25.10 + '@esbuild/netbsd-arm64': 0.25.10 + '@esbuild/netbsd-x64': 0.25.10 + '@esbuild/openbsd-arm64': 0.25.10 + '@esbuild/openbsd-x64': 0.25.10 + '@esbuild/openharmony-arm64': 0.25.10 + '@esbuild/sunos-x64': 0.25.10 + '@esbuild/win32-arm64': 0.25.10 + '@esbuild/win32-ia32': 0.25.10 + '@esbuild/win32-x64': 0.25.10 escalade@3.2.0: {} @@ -9497,44 +9517,58 @@ snapshots: escape-string-regexp@5.0.0: {} - eslint-config-prettier@10.1.8(eslint@9.31.0(jiti@2.5.1)): + eslint-config-prettier@10.1.8(eslint@9.31.0(jiti@2.6.1)): + dependencies: + eslint: 9.31.0(jiti@2.6.1) + + eslint-config-prettier@10.1.8(eslint@9.37.0(jiti@2.6.1)): dependencies: - eslint: 9.31.0(jiti@2.5.1) + eslint: 9.37.0(jiti@2.6.1) - eslint-plugin-perfectionist@4.15.0(eslint@9.31.0(jiti@2.5.1))(typescript@5.8.3): + eslint-plugin-perfectionist@4.15.0(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.3): dependencies: '@typescript-eslint/types': 8.38.0 - '@typescript-eslint/utils': 8.38.0(eslint@9.31.0(jiti@2.5.1))(typescript@5.8.3) - eslint: 9.31.0(jiti@2.5.1) + '@typescript-eslint/utils': 8.38.0(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.3) + eslint: 9.31.0(jiti@2.6.1) + natural-orderby: 5.0.0 + transitivePeerDependencies: + - supports-color + - typescript + + eslint-plugin-perfectionist@4.15.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3): + dependencies: + '@typescript-eslint/types': 8.45.0 + '@typescript-eslint/utils': 8.45.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.37.0(jiti@2.6.1) natural-orderby: 5.0.0 transitivePeerDependencies: - supports-color - typescript - eslint-plugin-prettier@5.5.3(eslint-config-prettier@10.1.8(eslint@9.31.0(jiti@2.5.1)))(eslint@9.31.0(jiti@2.5.1))(prettier@3.6.2): + eslint-plugin-prettier@5.5.3(eslint-config-prettier@10.1.8(eslint@9.31.0(jiti@2.6.1)))(eslint@9.31.0(jiti@2.6.1))(prettier@3.6.2): dependencies: - eslint: 9.31.0(jiti@2.5.1) + eslint: 9.31.0(jiti@2.6.1) prettier: 3.6.2 prettier-linter-helpers: 1.0.0 synckit: 0.11.11 optionalDependencies: - eslint-config-prettier: 10.1.8(eslint@9.31.0(jiti@2.5.1)) + eslint-config-prettier: 10.1.8(eslint@9.31.0(jiti@2.6.1)) - eslint-plugin-svelte@3.11.0(eslint@9.31.0(jiti@2.5.1))(svelte@5.36.16)(ts-node@10.9.2(@swc/core@1.13.2)(@swc/wasm@1.13.2)(@types/node@24.1.0)(typescript@5.8.3)): + eslint-plugin-svelte@3.12.4(eslint@9.37.0(jiti@2.6.1))(svelte@5.39.11)(ts-node@10.9.2(@swc/core@1.13.2)(@swc/wasm@1.13.2)(@types/node@24.1.0)(typescript@5.9.3)): dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0(jiti@2.5.1)) - '@jridgewell/sourcemap-codec': 1.5.4 - eslint: 9.31.0(jiti@2.5.1) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.37.0(jiti@2.6.1)) + '@jridgewell/sourcemap-codec': 1.5.5 + eslint: 9.37.0(jiti@2.6.1) esutils: 2.0.3 - globals: 16.3.0 + globals: 16.4.0 known-css-properties: 0.37.0 postcss: 8.5.6 - postcss-load-config: 3.1.4(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.13.2)(@swc/wasm@1.13.2)(@types/node@24.1.0)(typescript@5.8.3)) + postcss-load-config: 3.1.4(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.13.2)(@swc/wasm@1.13.2)(@types/node@24.1.0)(typescript@5.9.3)) postcss-safe-parser: 7.0.1(postcss@8.5.6) semver: 7.7.2 - svelte-eslint-parser: 1.3.0(svelte@5.36.16) + svelte-eslint-parser: 1.3.3(svelte@5.39.11) optionalDependencies: - svelte: 5.36.16 + svelte: 5.39.11 transitivePeerDependencies: - ts-node @@ -9547,9 +9581,9 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.31.0(jiti@2.5.1): + eslint@9.31.0(jiti@2.6.1): dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0(jiti@2.5.1)) + '@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0(jiti@2.6.1)) '@eslint-community/regexpp': 4.12.1 '@eslint/config-array': 0.21.0 '@eslint/config-helpers': 0.3.0 @@ -9585,7 +9619,49 @@ snapshots: natural-compare: 1.4.0 optionator: 0.9.4 optionalDependencies: - jiti: 2.5.1 + jiti: 2.6.1 + transitivePeerDependencies: + - supports-color + + eslint@9.37.0(jiti@2.6.1): + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.37.0(jiti@2.6.1)) + '@eslint-community/regexpp': 4.12.1 + '@eslint/config-array': 0.21.0 + '@eslint/config-helpers': 0.4.0 + '@eslint/core': 0.16.0 + '@eslint/eslintrc': 3.3.1 + '@eslint/js': 9.37.0 + '@eslint/plugin-kit': 0.4.0 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.6.1 transitivePeerDependencies: - supports-color @@ -9605,7 +9681,7 @@ snapshots: esrap@2.1.0: dependencies: - '@jridgewell/sourcemap-codec': 1.5.4 + '@jridgewell/sourcemap-codec': 1.5.5 esrecurse@4.3.0: dependencies: @@ -9625,8 +9701,6 @@ snapshots: esutils@2.0.3: {} - eta@3.5.0: {} - eventemitter3@4.0.7: {} eventemitter3@5.0.1: {} @@ -9697,6 +9771,10 @@ snapshots: optionalDependencies: picomatch: 4.0.3 + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + fflate@0.8.2: {} figures@1.7.0: @@ -9800,18 +9878,6 @@ snapshots: jsonfile: 6.1.0 universalify: 2.0.1 - fs-extra@11.3.0: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.1.0 - universalify: 2.0.1 - - fs-extra@8.1.0: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 4.0.0 - universalify: 0.1.2 - fs-extra@9.1.0: dependencies: at-least-node: 1.0.0 @@ -9948,7 +10014,7 @@ snapshots: globals@15.15.0: {} - globals@16.3.0: {} + globals@16.4.0: {} globalthis@1.0.4: dependencies: @@ -9957,17 +10023,6 @@ snapshots: globalyzer@0.1.0: {} - globby@10.0.1: - dependencies: - '@types/glob': 7.2.0 - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.3.3 - glob: 7.2.3 - ignore: 5.3.2 - merge2: 1.4.1 - slash: 3.0.0 - globby@11.1.0: dependencies: array-union: 2.1.0 @@ -10054,13 +10109,15 @@ snapshots: html-encoding-sniffer@4.0.0: dependencies: whatwg-encoding: 3.1.1 + optional: true http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.4 - debug: 4.4.1 + debug: 4.4.3 transitivePeerDependencies: - supports-color + optional: true http-signature@1.2.0: dependencies: @@ -10071,9 +10128,10 @@ snapshots: https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.4 - debug: 4.4.1 + debug: 4.4.3 transitivePeerDependencies: - supports-color + optional: true human-signals@5.0.0: {} @@ -10086,6 +10144,7 @@ snapshots: iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 + optional: true icss-replace-symbols@1.1.0: {} @@ -10127,8 +10186,6 @@ snapshots: indent-string@3.2.0: {} - indent-string@4.0.0: {} - index-to-position@1.1.0: {} inflight@1.0.6: @@ -10339,9 +10396,8 @@ snapshots: is-path-inside@4.0.0: {} - is-plain-object@3.0.1: {} - - is-potential-custom-element-name@1.0.1: {} + is-potential-custom-element-name@1.0.1: + optional: true is-promise@2.2.2: {} @@ -10451,7 +10507,7 @@ snapshots: merge-stream: 2.0.0 supports-color: 7.2.0 - jiti@2.5.1: {} + jiti@2.6.1: {} js-tokens@4.0.0: {} @@ -10468,32 +10524,34 @@ snapshots: jsbn@0.1.1: {} - jsdom@26.1.0: + jsdom@27.0.0(postcss@8.5.6): dependencies: - cssstyle: 4.6.0 - data-urls: 5.0.0 + '@asamuzakjp/dom-selector': 6.7.3 + cssstyle: 5.3.1(postcss@8.5.6) + data-urls: 6.0.0 decimal.js: 10.6.0 html-encoding-sniffer: 4.0.0 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.20 parse5: 7.3.0 rrweb-cssom: 0.8.0 saxes: 6.0.0 symbol-tree: 3.2.4 - tough-cookie: 5.1.2 + tough-cookie: 6.0.0 w3c-xmlserializer: 5.0.0 - webidl-conversions: 7.0.0 + webidl-conversions: 8.0.0 whatwg-encoding: 3.1.1 whatwg-mimetype: 4.0.0 - whatwg-url: 14.2.0 + whatwg-url: 15.1.0 ws: 8.18.3 xml-name-validator: 5.0.0 transitivePeerDependencies: - bufferutil + - postcss - supports-color - utf-8-validate + optional: true jsesc@3.0.2: {} @@ -10515,10 +10573,6 @@ snapshots: json5@2.2.3: {} - jsonfile@4.0.0: - optionalDependencies: - graceful-fs: 4.2.11 - jsonfile@6.1.0: dependencies: universalify: 2.0.1 @@ -10534,7 +10588,7 @@ snapshots: json-schema: 0.4.0 verror: 1.10.0 - katex@0.16.22: + katex@0.16.23: dependencies: commander: 8.3.0 @@ -10591,7 +10645,7 @@ snapshots: lightningcss@1.30.1: dependencies: - detect-libc: 2.0.4 + detect-libc: 2.1.1 optionalDependencies: lightningcss-darwin-arm64: 1.30.1 lightningcss-darwin-x64: 1.30.1 @@ -10759,13 +10813,16 @@ snapshots: lru-cache@11.1.0: {} + lru-cache@11.2.2: + optional: true + lru-cache@5.1.1: dependencies: yallist: 3.1.1 - lucide-svelte@0.525.0(svelte@5.36.16): + lucide-svelte@0.548.0(svelte@5.39.11): dependencies: - svelte: 5.36.16 + svelte: 5.39.11 lz-string@1.5.0: {} @@ -10775,7 +10832,11 @@ snapshots: magic-string@0.30.17: dependencies: - '@jridgewell/sourcemap-codec': 1.5.4 + '@jridgewell/sourcemap-codec': 1.5.5 + + magic-string@0.30.19: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 make-dir@3.1.0: dependencies: @@ -10794,6 +10855,9 @@ snapshots: mdn-data@2.0.14: {} + mdn-data@2.12.2: + optional: true + meow@12.1.1: {} meow@13.2.0: {} @@ -10873,8 +10937,6 @@ snapshots: mimic-function@5.0.1: {} - min-indent@1.0.1: {} - minimatch@10.0.3: dependencies: '@isaacs/brace-expansion': 5.0.0 @@ -10908,14 +10970,12 @@ snapshots: minipass: 3.3.6 yallist: 4.0.0 - minizlib@3.0.2: + minizlib@3.1.0: dependencies: minipass: 7.1.2 mkdirp@1.0.4: {} - mkdirp@3.0.1: {} - mlly@1.7.4: dependencies: acorn: 8.15.0 @@ -11033,8 +11093,6 @@ snapshots: number-is-nan@1.0.1: {} - nwsapi@2.2.20: {} - oauth-sign@0.9.0: {} object-assign@4.1.1: {} @@ -11195,6 +11253,7 @@ snapshots: parse5@7.3.0: dependencies: entities: 6.0.1 + optional: true path-exists@4.0.0: {} @@ -11317,13 +11376,13 @@ snapshots: postcss: 8.5.6 ts-node: 10.9.2(@swc/core@1.13.2)(@swc/wasm@1.13.2)(@types/node@22.16.5)(typescript@5.8.3) - postcss-load-config@3.1.4(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.13.2)(@swc/wasm@1.13.2)(@types/node@24.1.0)(typescript@5.8.3)): + postcss-load-config@3.1.4(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.13.2)(@swc/wasm@1.13.2)(@types/node@24.1.0)(typescript@5.9.3)): dependencies: lilconfig: 2.1.0 yaml: 1.10.2 optionalDependencies: postcss: 8.5.6 - ts-node: 10.9.2(@swc/core@1.13.2)(@swc/wasm@1.13.2)(@types/node@24.1.0)(typescript@5.8.3) + ts-node: 10.9.2(@swc/core@1.13.2)(@swc/wasm@1.13.2)(@types/node@24.1.0)(typescript@5.9.3) postcss-merge-longhand@5.1.7(postcss@8.5.6): dependencies: @@ -11502,11 +11561,6 @@ snapshots: dependencies: fast-diff: 1.3.0 - prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.36.16): - dependencies: - prettier: 3.6.2 - svelte: 5.36.16 - prettier@3.6.2: {} pretty-bytes@3.0.1: @@ -11608,11 +11662,6 @@ snapshots: dependencies: resolve: 1.22.10 - redent@3.0.0: - dependencies: - indent-string: 4.0.0 - strip-indent: 3.0.0 - reflect.getprototypeof@1.0.10: dependencies: call-bind: 1.0.8 @@ -11743,14 +11792,6 @@ snapshots: chalk: 1.1.3 maxmin: 2.1.0 - rollup-plugin-copy@3.5.0: - dependencies: - '@types/fs-extra': 8.1.5 - colorette: 1.4.0 - fs-extra: 8.1.0 - globby: 10.0.1 - is-plain-object: 3.0.1 - rollup-plugin-postcss@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.13.2)(@swc/wasm@1.13.2)(@types/node@22.16.5)(typescript@5.8.3)): dependencies: chalk: 4.1.2 @@ -11776,7 +11817,7 @@ snapshots: jest-worker: 26.6.2 rollup: 2.79.2 serialize-javascript: 4.0.0 - terser: 5.43.1 + terser: 5.44.0 rollup-plugin-typescript2@0.32.1(rollup@2.79.2)(typescript@4.9.5): dependencies: @@ -11805,32 +11846,6 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - rollup@4.45.1: - dependencies: - '@types/estree': 1.0.8 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.45.1 - '@rollup/rollup-android-arm64': 4.45.1 - '@rollup/rollup-darwin-arm64': 4.45.1 - '@rollup/rollup-darwin-x64': 4.45.1 - '@rollup/rollup-freebsd-arm64': 4.45.1 - '@rollup/rollup-freebsd-x64': 4.45.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.45.1 - '@rollup/rollup-linux-arm-musleabihf': 4.45.1 - '@rollup/rollup-linux-arm64-gnu': 4.45.1 - '@rollup/rollup-linux-arm64-musl': 4.45.1 - '@rollup/rollup-linux-loongarch64-gnu': 4.45.1 - '@rollup/rollup-linux-powerpc64le-gnu': 4.45.1 - '@rollup/rollup-linux-riscv64-gnu': 4.45.1 - '@rollup/rollup-linux-riscv64-musl': 4.45.1 - '@rollup/rollup-linux-s390x-gnu': 4.45.1 - '@rollup/rollup-linux-x64-gnu': 4.45.1 - '@rollup/rollup-linux-x64-musl': 4.45.1 - '@rollup/rollup-win32-arm64-msvc': 4.45.1 - '@rollup/rollup-win32-ia32-msvc': 4.45.1 - '@rollup/rollup-win32-x64-msvc': 4.45.1 - fsevents: 2.3.3 - rollup@4.46.2: dependencies: '@types/estree': 1.0.8 @@ -11857,7 +11872,36 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.46.2 fsevents: 2.3.3 - rrweb-cssom@0.8.0: {} + rollup@4.52.4: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.52.4 + '@rollup/rollup-android-arm64': 4.52.4 + '@rollup/rollup-darwin-arm64': 4.52.4 + '@rollup/rollup-darwin-x64': 4.52.4 + '@rollup/rollup-freebsd-arm64': 4.52.4 + '@rollup/rollup-freebsd-x64': 4.52.4 + '@rollup/rollup-linux-arm-gnueabihf': 4.52.4 + '@rollup/rollup-linux-arm-musleabihf': 4.52.4 + '@rollup/rollup-linux-arm64-gnu': 4.52.4 + '@rollup/rollup-linux-arm64-musl': 4.52.4 + '@rollup/rollup-linux-loong64-gnu': 4.52.4 + '@rollup/rollup-linux-ppc64-gnu': 4.52.4 + '@rollup/rollup-linux-riscv64-gnu': 4.52.4 + '@rollup/rollup-linux-riscv64-musl': 4.52.4 + '@rollup/rollup-linux-s390x-gnu': 4.52.4 + '@rollup/rollup-linux-x64-gnu': 4.52.4 + '@rollup/rollup-linux-x64-musl': 4.52.4 + '@rollup/rollup-openharmony-arm64': 4.52.4 + '@rollup/rollup-win32-arm64-msvc': 4.52.4 + '@rollup/rollup-win32-ia32-msvc': 4.52.4 + '@rollup/rollup-win32-x64-gnu': 4.52.4 + '@rollup/rollup-win32-x64-msvc': 4.52.4 + fsevents: 2.3.3 + + rrweb-cssom@0.8.0: + optional: true run-applescript@7.0.0: {} @@ -11909,6 +11953,7 @@ snapshots: saxes@6.0.0: dependencies: xmlchars: 2.2.0 + optional: true scoped-regex@3.0.0: {} @@ -11918,6 +11963,8 @@ snapshots: semver@7.7.2: {} + semver@7.7.3: {} + serialize-javascript@4.0.0: dependencies: randombytes: 2.1.0 @@ -12009,6 +12056,12 @@ snapshots: mrmime: 2.0.1 totalist: 3.0.1 + sirv@3.0.2: + dependencies: + '@polka/url': 1.0.0-next.29 + mrmime: 2.0.1 + totalist: 3.0.1 + slash@3.0.0: {} slash@5.1.0: {} @@ -12185,10 +12238,6 @@ snapshots: strip-final-newline@3.0.0: {} - strip-indent@3.0.0: - dependencies: - min-indent: 1.0.1 - strip-json-comments@2.0.1: {} strip-json-comments@3.1.1: {} @@ -12201,8 +12250,6 @@ snapshots: style-inject@0.3.0: {} - style-mod@4.1.2: {} - stylehacks@5.1.1(postcss@8.5.6): dependencies: browserslist: 4.25.1 @@ -12226,19 +12273,19 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - svelte-check@4.3.0(picomatch@4.0.3)(svelte@5.36.16)(typescript@5.8.3): + svelte-check@4.3.3(picomatch@4.0.3)(svelte@5.39.11)(typescript@5.9.3): dependencies: - '@jridgewell/trace-mapping': 0.3.29 + '@jridgewell/trace-mapping': 0.3.31 chokidar: 4.0.3 - fdir: 6.4.6(picomatch@4.0.3) + fdir: 6.5.0(picomatch@4.0.3) picocolors: 1.1.1 sade: 1.8.1 - svelte: 5.36.16 - typescript: 5.8.3 + svelte: 5.39.11 + typescript: 5.9.3 transitivePeerDependencies: - picomatch - svelte-eslint-parser@1.3.0(svelte@5.36.16): + svelte-eslint-parser@1.3.3(svelte@5.39.11): dependencies: eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 @@ -12247,13 +12294,15 @@ snapshots: postcss-scss: 4.0.9(postcss@8.5.6) postcss-selector-parser: 7.1.0 optionalDependencies: - svelte: 5.36.16 + svelte: 5.39.11 + + svelte-portal@2.2.1: {} - svelte@5.36.16: + svelte@5.39.11: dependencies: - '@ampproject/remapping': 2.3.0 - '@jridgewell/sourcemap-codec': 1.5.4 - '@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0) + '@jridgewell/remapping': 2.3.5 + '@jridgewell/sourcemap-codec': 1.5.5 + '@sveltejs/acorn-typescript': 1.0.6(acorn@8.15.0) '@types/estree': 1.0.8 acorn: 8.15.0 aria-query: 5.3.2 @@ -12263,8 +12312,8 @@ snapshots: esrap: 2.1.0 is-reference: 3.0.3 locate-character: 3.0.0 - magic-string: 0.30.17 - zimmerframe: 1.1.2 + magic-string: 0.30.19 + zimmerframe: 1.1.4 svgo@2.8.0: dependencies: @@ -12280,15 +12329,16 @@ snapshots: symbol-observable@4.0.0: {} - symbol-tree@3.2.4: {} + symbol-tree@3.2.4: + optional: true synckit@0.11.11: dependencies: '@pkgr/core': 0.2.9 - tailwindcss@4.1.11: {} + tailwindcss@4.1.14: {} - tapable@2.2.2: {} + tapable@2.3.0: {} tar@6.2.1: dependencies: @@ -12299,13 +12349,12 @@ snapshots: mkdirp: 1.0.4 yallist: 4.0.0 - tar@7.4.3: + tar@7.5.1: dependencies: '@isaacs/fs-minipass': 4.0.1 chownr: 3.0.0 minipass: 7.1.2 - minizlib: 3.0.2 - mkdirp: 3.0.1 + minizlib: 3.1.0 yallist: 5.0.0 terminal-link@3.0.0: @@ -12320,6 +12369,13 @@ snapshots: commander: 2.20.3 source-map-support: 0.5.21 + terser@5.44.0: + dependencies: + '@jridgewell/source-map': 0.3.10 + acorn: 8.15.0 + commander: 2.20.3 + source-map-support: 0.5.21 + text-extensions@2.4.0: {} through@2.3.8: {} @@ -12337,7 +12393,12 @@ snapshots: tinyglobby@0.2.14: dependencies: - fdir: 6.4.6(picomatch@4.0.3) + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 tinypool@1.1.1: {} @@ -12346,11 +12407,13 @@ snapshots: tinyspy@4.0.3: {} - tldts-core@6.1.86: {} + tldts-core@7.0.17: + optional: true - tldts@6.1.86: + tldts@7.0.17: dependencies: - tldts-core: 6.1.86 + tldts-core: 7.0.17 + optional: true tmp@0.0.33: dependencies: @@ -12367,18 +12430,24 @@ snapshots: psl: 1.15.0 punycode: 2.3.1 - tough-cookie@5.1.2: + tough-cookie@6.0.0: dependencies: - tldts: 6.1.86 + tldts: 7.0.17 + optional: true - tr46@5.1.1: + tr46@6.0.0: dependencies: punycode: 2.3.1 + optional: true ts-api-utils@2.1.0(typescript@5.8.3): dependencies: typescript: 5.8.3 + ts-api-utils@2.1.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + ts-node@10.9.2(@swc/core@1.13.2)(@swc/wasm@1.13.2)(@types/node@22.16.5)(typescript@5.8.3): dependencies: '@cspotcode/source-map-support': 0.8.1 @@ -12400,7 +12469,7 @@ snapshots: '@swc/core': 1.13.2 '@swc/wasm': 1.13.2 - ts-node@10.9.2(@swc/core@1.13.2)(@swc/wasm@1.13.2)(@types/node@24.1.0)(typescript@5.8.3): + ts-node@10.9.2(@swc/core@1.13.2)(@swc/wasm@1.13.2)(@types/node@24.1.0)(typescript@5.9.3): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 @@ -12414,7 +12483,7 @@ snapshots: create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 5.8.3 + typescript: 5.9.3 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 optionalDependencies: @@ -12497,21 +12566,34 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typescript-eslint@8.38.0(eslint@9.31.0(jiti@2.5.1))(typescript@5.8.3): + typescript-eslint@8.38.0(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.31.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.31.0(jiti@2.5.1))(typescript@5.8.3) - '@typescript-eslint/parser': 8.38.0(eslint@9.31.0(jiti@2.5.1))(typescript@5.8.3) + '@typescript-eslint/eslint-plugin': 8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.3))(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.3) + '@typescript-eslint/parser': 8.38.0(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.3) '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.8.3) - '@typescript-eslint/utils': 8.38.0(eslint@9.31.0(jiti@2.5.1))(typescript@5.8.3) - eslint: 9.31.0(jiti@2.5.1) + '@typescript-eslint/utils': 8.38.0(eslint@9.31.0(jiti@2.6.1))(typescript@5.8.3) + eslint: 9.31.0(jiti@2.6.1) typescript: 5.8.3 transitivePeerDependencies: - supports-color + typescript-eslint@8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.46.0(@typescript-eslint/parser@8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.46.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.37.0(jiti@2.6.1) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + typescript@4.9.5: {} typescript@5.8.3: {} + typescript@5.9.3: {} + ufo@1.6.1: {} unbox-primitive@1.1.0: @@ -12523,7 +12605,8 @@ snapshots: undici-types@6.21.0: {} - undici-types@7.8.0: {} + undici-types@7.8.0: + optional: true undici@6.21.3: {} @@ -12546,8 +12629,6 @@ snapshots: universal-user-agent@6.0.1: {} - universalify@0.1.2: {} - universalify@2.0.1: {} update-browserslist-db@1.1.3(browserslist@4.25.1): @@ -12577,8 +12658,6 @@ snapshots: util-deprecate@1.0.2: {} - uuid@10.0.0: {} - uuid@3.4.0: {} v8-compile-cache-lib@3.0.1: {} @@ -12596,34 +12675,13 @@ snapshots: core-util-is: 1.0.2 extsprintf: 1.3.0 - vite-node@3.2.4(@types/node@22.16.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0): - dependencies: - cac: 6.7.14 - debug: 4.4.1 - es-module-lexer: 1.7.0 - pathe: 2.0.3 - vite: 6.3.5(@types/node@22.16.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) - transitivePeerDependencies: - - '@types/node' - - jiti - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - - vite-node@3.2.4(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0): + vite-node@3.2.4(@types/node@22.16.5)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0): dependencies: cac: 6.7.14 - debug: 4.4.1 + debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) + vite: 7.1.9(@types/node@22.16.5)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0) transitivePeerDependencies: - '@types/node' - jiti @@ -12638,162 +12696,87 @@ snapshots: - tsx - yaml - vite-plugin-static-copy@2.3.1(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)): - dependencies: - chokidar: 3.6.0 - fast-glob: 3.3.3 - fs-extra: 11.3.0 - p-map: 7.0.3 - picocolors: 1.1.1 - vite: 6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) - - vite-plugin-top-level-await@1.6.0(rollup@4.45.1)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)): + vite-plugin-wasm@3.5.0(vite@7.1.9(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0)): dependencies: - '@rollup/plugin-virtual': 3.0.2(rollup@4.45.1) - '@swc/core': 1.13.2 - '@swc/wasm': 1.13.2 - uuid: 10.0.0 - vite: 6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) - transitivePeerDependencies: - - '@swc/helpers' - - rollup - - vite-plugin-wasm@3.5.0(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)): - dependencies: - vite: 6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) - - vite-tsconfig-paths@5.1.4(typescript@5.8.3)(vite@6.3.5(@types/node@22.16.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)): - dependencies: - debug: 4.4.1 - globrex: 0.1.2 - tsconfck: 3.1.6(typescript@5.8.3) - optionalDependencies: - vite: 6.3.5(@types/node@22.16.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) - transitivePeerDependencies: - - supports-color - - typescript + vite: 7.1.9(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0) - vite-tsconfig-paths@5.1.4(typescript@5.8.3)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)): + vite-tsconfig-paths@5.1.4(typescript@5.8.3)(vite@7.1.9(@types/node@22.16.5)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0)): dependencies: debug: 4.4.1 globrex: 0.1.2 tsconfck: 3.1.6(typescript@5.8.3) optionalDependencies: - vite: 6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) + vite: 7.1.9(@types/node@22.16.5)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0) transitivePeerDependencies: - supports-color - typescript - vite@6.3.5(@types/node@22.16.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0): + vite@7.1.9(@types/node@22.16.5)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0): dependencies: - esbuild: 0.25.8 - fdir: 6.4.6(picomatch@4.0.3) + esbuild: 0.25.10 + fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.45.1 - tinyglobby: 0.2.14 + rollup: 4.52.4 + tinyglobby: 0.2.15 optionalDependencies: '@types/node': 22.16.5 fsevents: 2.3.3 - jiti: 2.5.1 + jiti: 2.6.1 lightningcss: 1.30.1 - terser: 5.43.1 + terser: 5.44.0 yaml: 2.8.0 - vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0): + vite@7.1.9(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0): dependencies: - esbuild: 0.25.8 - fdir: 6.4.6(picomatch@4.0.3) + esbuild: 0.25.10 + fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.45.1 - tinyglobby: 0.2.14 + rollup: 4.52.4 + tinyglobby: 0.2.15 optionalDependencies: '@types/node': 24.1.0 fsevents: 2.3.3 - jiti: 2.5.1 + jiti: 2.6.1 lightningcss: 1.30.1 - terser: 5.43.1 + terser: 5.44.0 yaml: 2.8.0 - vitefu@1.1.1(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)): + vitefu@1.1.1(vite@7.1.9(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0)): optionalDependencies: - vite: 6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) + vite: 7.1.9(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0) - vitest@3.2.4(@types/node@22.16.5)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(jiti@2.5.1)(jsdom@26.1.0)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0): + vitest@3.2.4(@types/node@22.16.5)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0): dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@6.3.5(@types/node@22.16.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)) + '@vitest/mocker': 3.2.4(vite@7.1.9(@types/node@22.16.5)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 '@vitest/spy': 3.2.4 '@vitest/utils': 3.2.4 chai: 5.2.1 - debug: 4.4.1 + debug: 4.4.3 expect-type: 1.2.2 - magic-string: 0.30.17 + magic-string: 0.30.19 pathe: 2.0.3 picomatch: 4.0.3 std-env: 3.9.0 tinybench: 2.9.0 tinyexec: 0.3.2 - tinyglobby: 0.2.14 + tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 6.3.5(@types/node@22.16.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) - vite-node: 3.2.4(@types/node@22.16.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) + vite: 7.1.9(@types/node@22.16.5)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0) + vite-node: 3.2.4(@types/node@22.16.5)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.16.5 - '@vitest/browser': 3.2.4(playwright@1.55.0)(vite@6.3.5(@types/node@22.16.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0))(vitest@3.2.4) - '@vitest/ui': 3.2.4(vitest@3.2.4) - jsdom: 26.1.0 - transitivePeerDependencies: - - jiti - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - - vitest@3.2.4(@types/node@24.1.0)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(jiti@2.5.1)(jsdom@26.1.0)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0): - dependencies: - '@types/chai': 5.2.2 - '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)) - '@vitest/pretty-format': 3.2.4 - '@vitest/runner': 3.2.4 - '@vitest/snapshot': 3.2.4 - '@vitest/spy': 3.2.4 - '@vitest/utils': 3.2.4 - chai: 5.2.1 - debug: 4.4.1 - expect-type: 1.2.2 - magic-string: 0.30.17 - pathe: 2.0.3 - picomatch: 4.0.3 - std-env: 3.9.0 - tinybench: 2.9.0 - tinyexec: 0.3.2 - tinyglobby: 0.2.14 - tinypool: 1.1.1 - tinyrainbow: 2.0.0 - vite: 6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) - vite-node: 3.2.4(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) - why-is-node-running: 2.3.0 - optionalDependencies: - '@types/node': 24.1.0 - '@vitest/browser': 3.2.4(playwright@1.55.0)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0))(vitest@3.2.4) + '@vitest/browser': 3.2.4(playwright@1.55.0)(vite@7.1.9(@types/node@22.16.5)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.0))(vitest@3.2.4) '@vitest/ui': 3.2.4(vitest@3.2.4) - jsdom: 26.1.0 + jsdom: 27.0.0(postcss@8.5.6) transitivePeerDependencies: - jiti - less @@ -12808,11 +12791,10 @@ snapshots: - tsx - yaml - w3c-keyname@2.2.8: {} - w3c-xmlserializer@5.0.0: dependencies: xml-name-validator: 5.0.0 + optional: true wasm-pack@0.13.1: dependencies: @@ -12824,18 +12806,22 @@ snapshots: dependencies: defaults: 1.0.4 - webidl-conversions@7.0.0: {} + webidl-conversions@8.0.0: + optional: true whatwg-encoding@3.1.1: dependencies: iconv-lite: 0.6.3 + optional: true - whatwg-mimetype@4.0.0: {} + whatwg-mimetype@4.0.0: + optional: true - whatwg-url@14.2.0: + whatwg-url@15.1.0: dependencies: - tr46: 5.1.1 - webidl-conversions: 7.0.0 + tr46: 6.0.0 + webidl-conversions: 8.0.0 + optional: true when-exit@2.1.4: {} @@ -12938,9 +12924,11 @@ snapshots: xdg-basedir@5.1.0: {} - xml-name-validator@5.0.0: {} + xml-name-validator@5.0.0: + optional: true - xmlchars@2.2.0: {} + xmlchars@2.2.0: + optional: true y18n@5.0.8: {} @@ -12976,7 +12964,7 @@ snapshots: yoctocolors@2.1.1: {} - zimmerframe@1.1.2: {} + zimmerframe@1.1.4: {} zod-package-json@1.2.0: dependencies: @@ -12984,6 +12972,6 @@ snapshots: zod@3.25.76: {} - zrender@5.6.1: + zrender@6.0.0: dependencies: tslib: 2.3.0 From 3732a043cc06137650f95450e3a4b6ca72d34d23 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 26 Oct 2025 19:47:42 -0600 Subject: [PATCH 535/590] Add optimizer setup with weight decay groups --- .../piston-train-toy/src/lib/train/session.ts | 37 +-- .../src/lib/train/utils/optim.ts | 231 ++++++++++++++++++ 2 files changed, 241 insertions(+), 27 deletions(-) create mode 100644 examples/piston-train-toy/src/lib/train/utils/optim.ts diff --git a/examples/piston-train-toy/src/lib/train/session.ts b/examples/piston-train-toy/src/lib/train/session.ts index 37de201e..3a8dc397 100644 --- a/examples/piston-train-toy/src/lib/train/session.ts +++ b/examples/piston-train-toy/src/lib/train/session.ts @@ -1,6 +1,6 @@ import type { Config } from '$lib/workspace/config'; -import { Adam, AdamW, SGD, type Tensor } from '@piston-ml/piston-web'; +import { type Tensor } from '@piston-ml/piston-web'; import * as piston from '@piston-ml/piston-web'; import type { BuiltData } from './data/pipeline'; @@ -27,6 +27,7 @@ import { createDataloader, createModel } from './utils/model'; +import { configureOptimizerForModel } from './utils/optim'; import { seededRandom } from './utils/random'; // @ts-expect-error polyfill @@ -116,32 +117,14 @@ export class TrainingSession { // Build and store the training data pipeline (iterator bound to current dataset/collate) this.dataPipeline = await buildDataPipeline(this.config, trainGenerator, this.trainDataset); - - const optimizerConfig = this.config.optimizer; - const effectiveWeightDecay = optimizerConfig.weightDecay.present - ? optimizerConfig.weightDecay.value - : 0.0; - if (optimizerConfig.type === 'AdamW' || optimizerConfig.type === 'Adam') { - this.optimizer = new (optimizerConfig.type === 'AdamW' ? AdamW : Adam)( - this.model.parameters(), - piston.gpu, - { - lr: optimizerConfig.lr, - betas: [optimizerConfig.adam.beta1, optimizerConfig.adam.beta2], - eps: optimizerConfig.adam.eps, - weightDecay: effectiveWeightDecay, - amsgrad: optimizerConfig.adam.amsgrad - } - ); - } else if (optimizerConfig.type === 'SGD') { - this.optimizer = new SGD(this.model.parameters(), piston.gpu, { - lr: optimizerConfig.lr, - momentum: optimizerConfig.sgd.momentum, - dampening: optimizerConfig.sgd.dampening, - weightDecay: effectiveWeightDecay, - nesterov: optimizerConfig.sgd.nesterov - }); - } + // Create optimizer based on model type, using the (possibly restored) model parameters + this.optimizer = configureOptimizerForModel( + this.model, + isEncoderOnly, + isEncoderDecoder, + this.config.optimizer, + piston.gpu + ); this.model.train(); diff --git a/examples/piston-train-toy/src/lib/train/utils/optim.ts b/examples/piston-train-toy/src/lib/train/utils/optim.ts new file mode 100644 index 00000000..2a2cdf2a --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/utils/optim.ts @@ -0,0 +1,231 @@ +import type { OptimizerConfig } from '$lib/workspace/config'; + +import { + AdamW, + type Device, + type Module, + nn, + type Optimizer, + type Parameter as ParameterType, + type ParamGroup, + SGD +} from '@piston-ml/piston-web'; + +function paramsFromNamesSorted( + names: Iterable, + paramDict: Map +): ParameterType[] { + return Array.from(names) + .sort() + .map((name) => paramDict.get(name)!) + .filter((p) => p != null); +} + +/** + * Validates that all model parameters are included in the parameter groups. + * Throws an error if any parameters are missing from the groups. + * @param model - The model to validate + * @param paramGroups - The parameter groups to check + * @throws Error if any model parameters are not included in the parameter groups + */ +function validateParameterGroups( + model: Module, + paramGroups: ParamGroup[], + paramDict: Map +): void { + // Get all parameters from the model + const allModelParams = new Set(); + for (const [_, param] of model.namedParameters()) { + allModelParams.add(param); + } + + // Get all parameters from the parameter groups + const groupParams = new Set(); + for (const group of paramGroups) { + for (const param of group.params) { + groupParams.add(param); + } + } + + // Find parameters that are in the model but not in any group + const missingParams: ParameterType[] = []; + for (const param of allModelParams) { + if (!groupParams.has(param)) { + missingParams.push(param); + } + } + + if (missingParams.length > 0) { + // Find the names of the missing parameters using paramDict + const missingParamNames: string[] = []; + for (const [name, param] of paramDict) { + if (missingParams.includes(param)) { + missingParamNames.push(name); + } + } + + throw new Error( + `Found ${missingParams.length} model parameters that are not included in any parameter group (${groupParams.size} included). ` + + `All model parameters must be assigned to a parameter group for training. ` + + `Missing parameters: ${missingParamNames.join(', ')}` + ); + } +} + +function getWeightDecayParams( + model: Module, + useWeightDecayGroups: boolean, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + whitelistWeightModules: (new (...args: any[]) => Module)[], + // eslint-disable-next-line @typescript-eslint/no-explicit-any + blacklistWeightModules: (new (...args: any[]) => Module)[] +): { paramDict: Map; decay: Set; noDecay: Set } { + const decay = new Set(); + const noDecay = new Set(); + + const paramDict = new Map(); + + for (const [mn, m] of model.namedModules()) { + for (const [pn, p] of m.namedParameters()) { + const fpn = mn ? `${mn}.${pn}` : pn; + + paramDict.set(fpn, p); + + if (useWeightDecayGroups) { + if (pn.endsWith('bias')) { + // All biases will not be decayed + noDecay.add(fpn); + } else if (pn.endsWith('weight')) { + if (whitelistWeightModules.some((cls) => m instanceof cls)) { + // Weights of whitelist modules will be weight decayed + decay.add(fpn); + } else if (blacklistWeightModules.some((cls) => m instanceof cls)) { + // Weights of blacklist modules will NOT be weight decayed + noDecay.add(fpn); + } + } else { + // Parameters that are not weights or biases (shouldn't exist in std models) + // Add to decay by default, adjust if necessary for specific models. + decay.add(fpn); + } + } else { + decay.add(fpn); + } + } + } + + if (useWeightDecayGroups) { + // Validate that we considered every parameter + const allParamNames = new Set( + Array.from(model.namedParameters()).map(([name]) => name) + ); + const interParams = new Set([...decay].filter((x) => noDecay.has(x))); + const unionParams = new Set([...decay, ...noDecay]); + + if (interParams.size !== 0) { + throw new Error( + `Parameters ${JSON.stringify(Array.from(interParams))} made it into both decay/noDecay sets` + ); + } + const missingParams = new Set([...allParamNames].filter((x) => !unionParams.has(x))); + if (missingParams.size !== 0) { + throw new Error( + `Parameters ${JSON.stringify( + Array.from(missingParams) + )} were not separated into either decay/noDecay set` + ); + } + } + + return { paramDict, decay, noDecay }; +} + +/** + * Based on what minGPT does: + * Configures the optimizer based on the training configuration. + * Separates parameters into weight decay and no weight decay groups. + * @param trainConfig - The optimizer + * configuration + * @param device - The computation device + * @returns The configured optimizer + */ +function configureOptimizers( + model: Module, + moduleLayersPrefixes: string[], + lmHeadPrefix: string, + trainConfig: OptimizerConfig, + device: Device +): Optimizer { + const whitelistWeightModules = [nn.Linear]; + const blacklistWeightModules = [nn.LayerNorm, nn.RMSNorm, nn.Embedding]; + + const effectiveWeightDecay = trainConfig.weightDecay.present + ? trainConfig.weightDecay.value + : 0.0; + + const { paramDict, decay, noDecay } = getWeightDecayParams( + model, + trainConfig.weightDecay.useWeightDecayGroups, + whitelistWeightModules, + blacklistWeightModules + ); + // Deterministic param lists by name + const decayParamsValues = paramsFromNamesSorted(decay, paramDict); + const noDecayParamsValues = paramsFromNamesSorted(noDecay, paramDict); + + if (trainConfig.type === 'AdamW' || trainConfig.type === 'Adam' || trainConfig.type === 'SGD') { + const optimGroups: ParamGroup[] = [ + { + params: decayParamsValues, + weightDecay: effectiveWeightDecay + }, + ...(noDecayParamsValues.length > 0 + ? [ + { + params: noDecayParamsValues, + weightDecay: 0.0 // no decay + } + ] + : []) + ]; + + validateParameterGroups(model, optimGroups, paramDict); + + // Create the AdamW optimizer + if (trainConfig.type === 'AdamW' || trainConfig.type === 'Adam') { + return new AdamW(optimGroups, device, { + lr: trainConfig.lr, + betas: [trainConfig.adam.beta1, trainConfig.adam.beta2], + eps: trainConfig.adam.eps, + weightDecay: effectiveWeightDecay, + amsgrad: trainConfig.adam.amsgrad + }); + } else if (trainConfig.type === 'SGD') { + return new SGD(optimGroups, device, { + lr: trainConfig.lr, + momentum: trainConfig.sgd.momentum, + dampening: trainConfig.sgd.dampening, + weightDecay: effectiveWeightDecay, + nesterov: trainConfig.sgd.nesterov + }); + } + } + + throw new Error(`Unknown optimizer type: ${trainConfig.type}`); +} + +export function configureOptimizerForModel( + model: Module, + isEncoderOnly: boolean, + isEncoderDecoder: boolean, + config: OptimizerConfig, + device: Device +): Optimizer { + if (isEncoderOnly) { + return configureOptimizers(model, ['encoder.layer'], 'mlmHead', config, device); + } else if (isEncoderDecoder) { + return configureOptimizers(model, ['decoder.layer', 'encoder.layer'], 'lmHead', config, device); + } else { + return configureOptimizers(model, ['decoder.layer'], 'lmHead', config, device); + } +} From e7d688294e7aaa64f45b80d2928dce9809ecdd1b Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 26 Oct 2025 19:49:45 -0600 Subject: [PATCH 536/590] Add example support for Muon; make default --- .../lib/components/controls/Controls.svelte | 45 ++++- .../src/lib/train/utils/optim.ts | 166 ++++++++++++++++++ .../src/lib/workspace/config.svelte.ts | 7 +- .../src/lib/workspace/config.ts | 7 +- 4 files changed, 222 insertions(+), 3 deletions(-) diff --git a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte index 3984f70c..4679780e 100644 --- a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte +++ b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte @@ -405,6 +405,15 @@ { name: 'Robbins & Monro, 1951', url: 'https://www.jstor.org/stable/2236626?seq=1' } ] } + }, + { + value: 'Muon', + title: 'Muon with AdamW', + citations: { + entries: [ + { name: 'Jordan et al., 2024', url: 'https://kellerjordan.github.io/posts/muon/' } + ] + } } ]} hasDefaultValue={equalsConfigDefault('optimizer.type')} @@ -452,7 +461,41 @@
    - {#if config.optimizer.type === 'AdamW' || config.optimizer.type === 'Adam'} + {#if config.optimizer.type === 'Muon'} + +
    + resetConfigToDefaults('optimizer.muon.nsSteps')} + /> + resetConfigToDefaults('optimizer.muon.momentum')} + /> + resetConfigToDefaults('optimizer.muon.nesterov')} + /> +
    +
    + {/if} + {#if config.optimizer.type === 'AdamW' || config.optimizer.type === 'Adam' || config.optimizer.type === 'Muon'} {@const settingsName = config.optimizer.type === 'Adam' ? 'Adam' : 'AdamW'}
    diff --git a/examples/piston-train-toy/src/lib/train/utils/optim.ts b/examples/piston-train-toy/src/lib/train/utils/optim.ts index 2a2cdf2a..0abe58b1 100644 --- a/examples/piston-train-toy/src/lib/train/utils/optim.ts +++ b/examples/piston-train-toy/src/lib/train/utils/optim.ts @@ -4,6 +4,8 @@ import { AdamW, type Device, type Module, + MuonWithAdamW, + type MuonWithAdamWParamGroup, nn, type Optimizer, type Parameter as ParameterType, @@ -11,6 +13,15 @@ import { SGD } from '@piston-ml/piston-web'; +// Deterministic sorting helpers +function compareByName(a: [string, T], b: [string, T]): number { + return a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0; +} + +function sortEntriesByName(entries: Array<[string, T]>): Array<[string, T]> { + return entries.sort(compareByName); +} + function paramsFromNamesSorted( names: Iterable, paramDict: Map @@ -209,6 +220,161 @@ function configureOptimizers( nesterov: trainConfig.sgd.nesterov }); } + } else if (trainConfig.type === 'Muon') { + // Get parameter groups by type + const paramEntries = sortEntriesByName(Array.from(paramDict.entries())); + const moduleLayersParams = paramEntries.filter(([n]) => + moduleLayersPrefixes.some((prefix) => n.startsWith(prefix)) + ); + // Sort each category deterministically by name + const hiddenMatrixParams = sortEntriesByName( + moduleLayersParams.filter(([n, p]) => p.ndim >= 2 && !n.toLowerCase().includes('embed')) + ); + const scalarParams = sortEntriesByName(moduleLayersParams.filter(([_, p]) => p.ndim < 2)); + const embedParams = sortEntriesByName( + paramEntries.filter(([n, _]) => n.toLowerCase().includes('embed')) + ); + const headParams = sortEntriesByName(paramEntries.filter(([n]) => n.startsWith(lmHeadPrefix))); + // Any other params we just throw to AdamW + const filteredParams = new Set([ + ...hiddenMatrixParams.map(([n]) => n), + ...scalarParams.map(([n]) => n), + ...embedParams.map(([n]) => n), + ...headParams.map(([n]) => n) + ]); + const remainingParams = paramEntries.filter(([n]) => !filteredParams.has(n)); + + if (remainingParams.length > 0) { + console.warn( + `Found ${remainingParams.length} parameters that don't fit Muon categorization and will be handled by AdamW:`, + remainingParams.map(([name]) => name) + ); + } + + // Apply weight decay grouping to each parameter type + const paramGroups: MuonWithAdamWParamGroup[] = []; + + // Hidden matrix parameters for Muon optimizer + if (trainConfig.weightDecay.useWeightDecayGroups) { + const hiddenDecay = hiddenMatrixParams.filter(([name]) => decay.has(name)).map(([_, p]) => p); + const hiddenNoDecay = hiddenMatrixParams + .filter(([name]) => noDecay.has(name)) + .map(([_, p]) => p); + + if (hiddenDecay.length > 0) { + paramGroups.push({ + optimizer: 'muon', + lr: trainConfig.lr, + weightDecay: effectiveWeightDecay, + momentum: trainConfig.muon.momentum, + nsSteps: trainConfig.muon.nsSteps, + nesterov: trainConfig.muon.nesterov, + params: hiddenDecay + }); + } + + if (hiddenNoDecay.length > 0) { + paramGroups.push({ + optimizer: 'muon', + lr: trainConfig.lr, + weightDecay: 0.0, // no decay + momentum: trainConfig.muon.momentum, + nsSteps: trainConfig.muon.nsSteps, + nesterov: trainConfig.muon.nesterov, + params: hiddenNoDecay + }); + } + } else { + if (hiddenMatrixParams.length > 0) { + paramGroups.push({ + optimizer: 'muon', + lr: trainConfig.lr, + weightDecay: effectiveWeightDecay, + momentum: trainConfig.muon.momentum, + nsSteps: trainConfig.muon.nsSteps, + nesterov: trainConfig.muon.nesterov, + params: hiddenMatrixParams.map(([_, p]) => p) + }); + } + } + + // Scalar, embedding, and head parameters for AdamW optimizer + const adamwParams = sortEntriesByName([ + ...scalarParams, + ...embedParams, + ...headParams, + ...remainingParams + ]); + + // Check if there is any overlap between the two optimizers getting overlap of adamWparams + const adamwParamSet = new Set(adamwParams.map(([n]) => n)); + const muonParamSet = new Set(hiddenMatrixParams.map(([n]) => n)); + const overlap = adamwParamSet.intersection(muonParamSet); + if (overlap.size > 0) { + throw new Error( + `Overlap between AdamW and Muon parameters: ${Array.from(overlap).join(', ')}` + ); + } + + if (trainConfig.weightDecay.useWeightDecayGroups) { + const adamwDecay = adamwParams.filter(([name]) => decay.has(name)).map(([_, p]) => p); + const adamwNoDecay = adamwParams.filter(([name]) => noDecay.has(name)).map(([_, p]) => p); + + if (adamwDecay.length > 0) { + paramGroups.push({ + optimizer: 'adamw', + lr: trainConfig.lr, + betas: [trainConfig.adam.beta1, trainConfig.adam.beta2], + eps: trainConfig.adam.eps, + weightDecay: effectiveWeightDecay, + amsgrad: trainConfig.adam.amsgrad, + params: adamwDecay + }); + } + + if (adamwNoDecay.length > 0) { + paramGroups.push({ + optimizer: 'adamw', + lr: trainConfig.lr, + betas: [trainConfig.adam.beta1, trainConfig.adam.beta2], + eps: trainConfig.adam.eps, + weightDecay: 0.0, // no decay + amsgrad: trainConfig.adam.amsgrad, + params: adamwNoDecay + }); + } + } else { + if (adamwParams.length > 0) { + paramGroups.push({ + optimizer: 'adamw', + lr: trainConfig.lr, + betas: [trainConfig.adam.beta1, trainConfig.adam.beta2], + eps: trainConfig.adam.eps, + weightDecay: effectiveWeightDecay, + amsgrad: trainConfig.adam.amsgrad, + params: adamwParams.map(([_, p]) => p) + }); + } + } + + validateParameterGroups(model, paramGroups, paramDict); + + return new MuonWithAdamW(paramGroups, device, { + muon: { + lr: trainConfig.lr, + weightDecay: effectiveWeightDecay, + momentum: trainConfig.muon.momentum, + nsSteps: trainConfig.muon.nsSteps, + nesterov: trainConfig.muon.nesterov + }, + adamw: { + lr: trainConfig.lr, + betas: [trainConfig.adam.beta1, trainConfig.adam.beta2], + eps: trainConfig.adam.eps, + weightDecay: effectiveWeightDecay, + amsgrad: trainConfig.adam.amsgrad + } + }); } throw new Error(`Unknown optimizer type: ${trainConfig.type}`); diff --git a/examples/piston-train-toy/src/lib/workspace/config.svelte.ts b/examples/piston-train-toy/src/lib/workspace/config.svelte.ts index d23f394b..ed661337 100644 --- a/examples/piston-train-toy/src/lib/workspace/config.svelte.ts +++ b/examples/piston-train-toy/src/lib/workspace/config.svelte.ts @@ -73,7 +73,7 @@ const CONFIG_DEFAULTS: Config = { } }, optimizer: { - type: 'AdamW', + type: 'Muon', lr: 1e-3, weightDecay: { present: true, @@ -90,6 +90,11 @@ const CONFIG_DEFAULTS: Config = { momentum: 0.9, dampening: 0, nesterov: false + }, + muon: { + momentum: 0.95, + nsSteps: 5, + nesterov: true } }, version: 1 diff --git a/examples/piston-train-toy/src/lib/workspace/config.ts b/examples/piston-train-toy/src/lib/workspace/config.ts index db27e424..e95abf45 100644 --- a/examples/piston-train-toy/src/lib/workspace/config.ts +++ b/examples/piston-train-toy/src/lib/workspace/config.ts @@ -90,7 +90,7 @@ export interface ModelConfig { } export interface OptimizerConfig { - type: 'AdamW' | 'Adam' | 'SGD'; + type: 'AdamW' | 'Adam' | 'SGD' | 'Muon'; lr: number; weightDecay: { present: boolean; @@ -108,6 +108,11 @@ export interface OptimizerConfig { momentum: number; nesterov: boolean; }; + muon: { + nsSteps: number; + momentum: number; + nesterov: boolean; + }; } export interface Config { From e03cc2c3d03c4bddd44ccda21d673e2862119fbb Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 26 Oct 2025 20:11:22 -0600 Subject: [PATCH 537/590] Add learning rate scheduling --- .../lib/components/controls/Controls.svelte | 14 + .../controls/LRSchedulePicker.svelte | 493 ++++++++++++++++++ .../piston-train-toy/src/lib/train/session.ts | 54 +- .../src/lib/workspace/config.svelte.ts | 24 + .../src/lib/workspace/config.ts | 17 + .../src/routes/tabs/Metrics.svelte | 22 +- 6 files changed, 621 insertions(+), 3 deletions(-) create mode 100644 examples/piston-train-toy/src/lib/components/controls/LRSchedulePicker.svelte diff --git a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte index 4679780e..be5372dc 100644 --- a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte +++ b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte @@ -18,6 +18,7 @@ import CollapsibleSection from './CollapsibleSection.svelte'; import ControlsStatistic from './ControlsStatistic.svelte'; import DatasetControls from './DatasetControls.svelte'; + import LRSchedulePicker from './LRSchedulePicker.svelte'; import NumberInput from './NumberInput.svelte'; import SelectModelTopology from './select/SelectModelTopology.svelte'; import SelectWithCitations from './select/SelectWithCitations.svelte'; @@ -432,6 +433,19 @@ onReset={() => resetConfigToDefaults('optimizer.lr')} /> + + resetConfigToDefaults('optimizer.lrScheduler.present')} + > + + + + import { config, equalsConfigDefault, resetConfigToDefaults } from '$lib/workspace/config.svelte'; + import { + ConstantLR, + CosineAnnealingLR, + ExponentialLR, + LinearLR, + type LRScheduler, + type Optimizer, + StepLR + } from '@piston-ml/piston-web'; + import { MediaQuery } from 'svelte/reactivity'; + + import CheckboxInput from './checkbox/CheckboxInput.svelte'; + import SelectInput from './select/SelectInput.svelte'; + import Slider from './Slider.svelte'; + + let logScale = $state(false); + let chartContainer: HTMLDivElement; + let containerWidth = $state(300); // Default width + let stepsToShow = $state(1000); // Local state for visualization range + + const isMobile = new MediaQuery('pointer: coarse'); + + // Mock optimizer for scheduler testing + function createMockOptimizer(learningRate: number) { + return { + paramGroups: [{ lr: learningRate, initialLr: learningRate }] + }; + } + + function generateSchedulerPoints(maxSteps: number = 100) { + const points: [number, number][] = []; + const schedulerType = config.optimizer.lrScheduler.type; + const baseLr = config.optimizer.lr; + + // Create mock optimizer for scheduler testing + const mockOptimizer = createMockOptimizer(baseLr) as unknown as Optimizer; + + // Create the appropriate scheduler + let scheduler: LRScheduler; + try { + switch (schedulerType) { + case 'constant': + scheduler = new ConstantLR( + mockOptimizer, + config.optimizer.lrScheduler.constantSchedule.factor, + config.optimizer.lrScheduler.constantSchedule.totalIters + ); + break; + case 'linear': + scheduler = new LinearLR( + mockOptimizer, + config.optimizer.lrScheduler.linearSchedule.startFactor, + config.optimizer.lrScheduler.linearSchedule.endFactor, + config.optimizer.lrScheduler.linearSchedule.totalIters + ); + break; + case 'cosine': + scheduler = new CosineAnnealingLR( + mockOptimizer, + config.optimizer.lrScheduler.cosineAnnealingSchedule.tMax, + config.optimizer.lrScheduler.cosineAnnealingSchedule.etaMin + ); + break; + case 'step': + scheduler = new StepLR( + mockOptimizer, + config.optimizer.lrScheduler.stepSchedule.stepSize, + config.optimizer.lrScheduler.stepSchedule.gamma + ); + break; + case 'exponential': + scheduler = new ExponentialLR( + mockOptimizer, + config.optimizer.lrScheduler.exponentialSchedule.gamma + ); + break; + default: + // If unknown scheduler, just return constant learning rate + for (let step = 0; step <= maxSteps; step++) { + points.push([step, baseLr]); + } + return points; + } + } catch (error) { + console.warn('Could not create scheduler:', error); + // Fallback to constant learning rate + for (let step = 0; step <= maxSteps; step++) { + points.push([step, baseLr]); + } + return points; + } + + // Generate points by stepping through the scheduler + for (let step = 0; step <= maxSteps; step++) { + const currentLr = scheduler.getLastLr()[0] || baseLr; + points.push([step, currentLr]); + + // Step the scheduler for next iteration + if (step < maxSteps) { + scheduler.step(); + } + } + + return points; + } + + const points = $derived(generateSchedulerPoints(stepsToShow)); + + let chartHeight = $state(100); + + // Update chart height when media query or container changes + $effect(() => { + // Trigger reactivity when media query changes + const _isMobile = isMobile.current; + + // Get CSS variable value and convert rem to px + if (typeof window !== 'undefined' && chartContainer) { + // Temporarily set a CSS property to convert rem to px + const originalHeight = chartContainer.style.height; + chartContainer.style.height = 'calc(var(--spacing) * 32)'; + const computedHeight = parseFloat(getComputedStyle(chartContainer).height); + chartContainer.style.height = originalHeight; + + chartHeight = computedHeight || 100; + } + }); + + const chartWidth = $derived(containerWidth || 300); + + // Text measurement elements + let measurementSvg: SVGSVGElement; + let leftPadding = $state(30); + let rightPadding = $state(10); + let topPadding = $state(15); + let bottomPadding = $state(20); + let labelHeight = $state(0); + + const plotWidth = $derived(chartWidth - leftPadding - rightPadding); + const plotHeight = $derived(chartHeight - topPadding - bottomPadding); + + const svgPoints = $derived.by(() => { + if (points.length === 0) return ''; + + const maxSteps = Math.max(...points.map(([x]) => x)); + const minSteps = Math.min(...points.map(([x]) => x)); + const lrs = points.map(([, y]) => y); + const maxLr = Math.max(...lrs); + const minLr = Math.min(...lrs); + // const minLr = 1e-6; // Fixed bottom at 1e-6 + + // Avoid division by zero + if (maxSteps === minSteps) { + return ''; + } + + if (maxLr === minLr) { + // Constant learning rate - draw a horizontal line + const y = chartHeight - bottomPadding - plotHeight / 2; + const xStart = leftPadding; + const xEnd = leftPadding + plotWidth; + return `${xStart},${y} ${xEnd},${y}`; + } + + return points + .map(([step, lr]) => { + const x = leftPadding + ((step - minSteps) * plotWidth) / (maxSteps - minSteps); + + let y: number; + if (logScale && minLr > 0 && maxLr > 0) { + const logMinLr = Math.log10(minLr); + const logMaxLr = Math.log10(maxLr); + const logLr = Math.log10(lr); + y = + chartHeight - bottomPadding - ((logLr - logMinLr) * plotHeight) / (logMaxLr - logMinLr); + } else { + y = chartHeight - bottomPadding - ((lr - minLr) * plotHeight) / (maxLr - minLr); + } + + return `${x},${y}`; + }) + .join(' '); + }); + + // Format numbers for display + function formatNumber(num: number): string { + // if (num >= 1000) return num.toExponential(2); + // if (num >= 1) return num.toFixed(3); + // if (num >= 0.001) return num.toFixed(4); + return num.toExponential(2); + } + + const maxLrLabel = $derived.by(() => { + if (points.length === 0) return ''; + const maxLr = Math.max(...points.map(([, y]) => y)); + return formatNumber(maxLr); + }); + + const minLrLabel = $derived.by(() => { + if (points.length === 0) return ''; + const minLr = Math.min(...points.map(([, y]) => y)); + return formatNumber(minLr); + }); + + // Function to measure text width + async function measureTextDimensions(text: string, className: string): Promise<[number, number]> { + if (!measurementSvg) return [0, 0]; + + // Check if fonts are loaded before measuring + if (document.fonts && document.fonts.ready) { + await document.fonts.ready; + } + + const textElement = document.createElementNS('http://www.w3.org/2000/svg', 'text'); + textElement.classList.add(className); + textElement.textContent = text; + + // eslint-disable-next-line svelte/no-dom-manipulating + measurementSvg.appendChild(textElement); + const bbox = textElement.getBBox(); + // eslint-disable-next-line svelte/no-dom-manipulating + measurementSvg.removeChild(textElement); + + return [bbox.width, bbox.height]; + } + + // Calculate dynamic padding based on text measurements + $effect(() => { + const _isMobile = isMobile.current; + if (!measurementSvg || points.length === 0 || typeof window === 'undefined') return; + + // Wrap this whole thing in an async function so we can wait for fonts to load if they haven't yet + (async () => { + // Measure Y-axis labels (LR values) + const [maxLrWidth] = await measureTextDimensions(maxLrLabel, 'text-2xs'); + const [minLrWidth] = await measureTextDimensions(minLrLabel, 'text-2xs'); + const maxYLabelWidth = Math.max(maxLrWidth, minLrWidth); + + // Measure X-axis label and step values + const [stepsLabelWidth, stepsLabelHeight] = await measureTextDimensions('steps', 'text-2xs'); + const maxSteps = Math.max(...points.map(([x]) => x)); + const [_maxStepsWidth, maxStepsHeight] = await measureTextDimensions( + maxSteps.toString(), + 'text-2xs' + ); + + // Calculate maximum height needed for X-axis labels + const maxXLabelHeight = stepsLabelHeight + maxStepsHeight; + + // Update padding with some buffer + leftPadding = Math.max(20, maxYLabelWidth + 5); + // Kind of a dumb way to go about this + rightPadding = stepsLabelWidth / 2; + bottomPadding = Math.max(20, maxXLabelHeight); // Dynamic padding based on text height + labelHeight = stepsLabelHeight; + topPadding = 15; // Fixed for now + })(); + }); + + +
    + + + + + resetConfigToDefaults('optimizer.lrScheduler.type')} + /> + + +
    + + + + + + + {#if points.length > 0} + {@const lrs = points.map(([, y]) => y)} + {@const maxLr = Math.max(...lrs)} + {@const minLr = Math.min(...lrs)} + {#if maxLr === minLr} + + {maxLrLabel} + {:else} + + {maxLrLabel} + {minLrLabel} + {/if} + {/if} + + + steps + {#if points.length > 0} + {Math.max(...points.map(([x]) => x))} + {/if} + + + {#if svgPoints} + + {/if} + + + +
    + +
    +
    + + +
    + + (stepsToShow = 1000)} + /> + + + {#if config.optimizer.lrScheduler.type === 'linear'} + resetConfigToDefaults('optimizer.lrScheduler.linearSchedule.startFactor')} + /> + resetConfigToDefaults('optimizer.lrScheduler.linearSchedule.endFactor')} + /> + resetConfigToDefaults('optimizer.lrScheduler.linearSchedule.totalIters')} + /> + {/if} + + + {#if config.optimizer.lrScheduler.type === 'constant'} + resetConfigToDefaults('optimizer.lrScheduler.constantSchedule.factor')} + /> + resetConfigToDefaults('optimizer.lrScheduler.constantSchedule.totalIters')} + /> + {/if} + + + {#if config.optimizer.lrScheduler.type === 'cosine'} + resetConfigToDefaults('optimizer.lrScheduler.cosineAnnealingSchedule.tMax')} + /> + + resetConfigToDefaults('optimizer.lrScheduler.cosineAnnealingSchedule.etaMin')} + /> + {/if} + + + {#if config.optimizer.lrScheduler.type === 'step'} + resetConfigToDefaults('optimizer.lrScheduler.stepSchedule.stepSize')} + /> + resetConfigToDefaults('optimizer.lrScheduler.stepSchedule.gamma')} + /> + {/if} + + + {#if config.optimizer.lrScheduler.type === 'exponential'} + resetConfigToDefaults('optimizer.lrScheduler.exponentialSchedule.gamma')} + /> + {/if} +
    +
    diff --git a/examples/piston-train-toy/src/lib/train/session.ts b/examples/piston-train-toy/src/lib/train/session.ts index 3a8dc397..f46eb68d 100644 --- a/examples/piston-train-toy/src/lib/train/session.ts +++ b/examples/piston-train-toy/src/lib/train/session.ts @@ -1,6 +1,13 @@ import type { Config } from '$lib/workspace/config'; -import { type Tensor } from '@piston-ml/piston-web'; +import { + CosineAnnealingLR, + ExponentialLR, + LinearLR, + type LRScheduler, + StepLR, + type Tensor +} from '@piston-ml/piston-web'; import * as piston from '@piston-ml/piston-web'; import type { BuiltData } from './data/pipeline'; @@ -40,6 +47,7 @@ export class TrainingSession { private model!: GeneratableModel; private optimizer!: piston.Optimizer; + private scheduler: LRScheduler | undefined; private trainDataset!: ToyDatasetLike; private blockSize!: number | { source: number; target: number }; @@ -126,6 +134,40 @@ export class TrainingSession { piston.gpu ); + // Create learning rate scheduler if configured + if (this.config.optimizer.lrScheduler.present) { + const lrConfig = this.config.optimizer.lrScheduler; + switch (lrConfig.type) { + case 'step': + this.scheduler = new StepLR( + this.optimizer, + lrConfig.stepSchedule.stepSize, + lrConfig.stepSchedule.gamma + ); + break; + case 'cosine': + this.scheduler = new CosineAnnealingLR( + this.optimizer, + lrConfig.cosineAnnealingSchedule.tMax, + lrConfig.cosineAnnealingSchedule.etaMin + ); + break; + case 'exponential': + this.scheduler = new ExponentialLR(this.optimizer, lrConfig.exponentialSchedule.gamma); + break; + case 'linear': + this.scheduler = new LinearLR( + this.optimizer, + lrConfig.linearSchedule.startFactor, + lrConfig.linearSchedule.endFactor, + lrConfig.linearSchedule.totalIters + ); + break; + default: + throw new Error(`Unknown scheduler type: ${lrConfig.type}`); + } + } + this.model.train(); this.isSetup = true; @@ -211,6 +253,11 @@ export class TrainingSession { this.optimizer.zeroGrad(true); + // Step learning rate scheduler if present + if (this.scheduler) { + this.scheduler.step(); + } + if (loggingStep) { const currentTime = Date.now(); const totalElapsedSeconds = (currentTime - this.startTimeMs!) / 1000; @@ -249,6 +296,11 @@ export class TrainingSession { 'speed/tokens_per_second': tokensPerSecond, 'speed/wall_clock_seconds': totalElapsedSeconds }; + // Log current learning rate if scheduler is present + const currentLr = this.optimizer.paramGroups[0].lr; + if (currentLr) { + logData['optimizer/learning_rate'] = currentLr; + } this.logMetrics(logData, { step: this.stepCount }); diff --git a/examples/piston-train-toy/src/lib/workspace/config.svelte.ts b/examples/piston-train-toy/src/lib/workspace/config.svelte.ts index ed661337..02b8983f 100644 --- a/examples/piston-train-toy/src/lib/workspace/config.svelte.ts +++ b/examples/piston-train-toy/src/lib/workspace/config.svelte.ts @@ -80,6 +80,30 @@ const CONFIG_DEFAULTS: Config = { value: 1e-2, useWeightDecayGroups: true }, + lrScheduler: { + present: true, + type: 'cosine', + stepSchedule: { + stepSize: 100, + gamma: 0.8 + }, + constantSchedule: { + factor: 1 / 3, + totalIters: 100 + }, + cosineAnnealingSchedule: { + tMax: 500, + etaMin: 1e-4 + }, + exponentialSchedule: { + gamma: 0.999 + }, + linearSchedule: { + startFactor: 1.0, + endFactor: 1 / 3, + totalIters: 1000 + } + }, adam: { beta1: 0.9, beta2: 0.999, diff --git a/examples/piston-train-toy/src/lib/workspace/config.ts b/examples/piston-train-toy/src/lib/workspace/config.ts index e95abf45..4c77b48a 100644 --- a/examples/piston-train-toy/src/lib/workspace/config.ts +++ b/examples/piston-train-toy/src/lib/workspace/config.ts @@ -1,3 +1,11 @@ +import type { + ConstantConfig, + CosineAnnealingConfig, + ExponentialConfig, + LinearConfig, + StepConfig +} from '@piston-ml/piston-web'; + export type { AdditionConfig, ModularAdditionConfig, @@ -97,6 +105,15 @@ export interface OptimizerConfig { value: number; useWeightDecayGroups: boolean; }; + lrScheduler: { + present: boolean; + type: string; + stepSchedule: StepConfig; + constantSchedule: ConstantConfig; + cosineAnnealingSchedule: CosineAnnealingConfig; + exponentialSchedule: ExponentialConfig; + linearSchedule: LinearConfig; + }; adam: { beta1: number; beta2: number; diff --git a/examples/piston-train-toy/src/routes/tabs/Metrics.svelte b/examples/piston-train-toy/src/routes/tabs/Metrics.svelte index b951483c..dd4e88dd 100644 --- a/examples/piston-train-toy/src/routes/tabs/Metrics.svelte +++ b/examples/piston-train-toy/src/routes/tabs/Metrics.svelte @@ -2,7 +2,11 @@ import MetricsSection from '$lib/components/metrics/MetricsSection.svelte'; import RunChart from '$lib/components/metrics/RunChart.svelte'; import ToggleChips from '$lib/components/ToggleChips.svelte'; - import { getMetricGroups, getMetricNamesFromLastNRuns } from '$lib/workspace/runs.svelte'; + import { + getCurrentRun, + getMetricGroups, + getMetricNamesFromLastNRuns + } from '$lib/workspace/runs.svelte'; import { metricsSectionsOpen, metricVisibility, @@ -15,8 +19,17 @@ // Group metrics by prefix (everything before the first '/') const metricGroups = $derived(getMetricGroups(metricNames)); + const runConfig = $derived(getCurrentRun()?.config); + + // Derived: lr scheduler present + const lrSchedulerPresent = $derived( + (runConfig?.optimizer?.lrScheduler?.present ?? false) || + (runConfig?.optimizer?.warmupSteps?.present ?? false) + ); + function getDefaultMetricVisibility(name: string): boolean { if (name === 'speed/wall_clock_seconds') return false; + if (name === 'optimizer/learning_rate') return !!lrSchedulerPresent; return true; } @@ -42,7 +55,12 @@
    {:else}
    - {#each Object.entries(metricGroups) as [groupName, metrics] (groupName)} + {#each Object.entries(metricGroups).sort(([a], [b]) => { + const order = ['train', 'optimizer']; + const aPriority = order.indexOf(a); + const bPriority = order.indexOf(b); + return (aPriority === -1 ? 999 : aPriority) - (bPriority === -1 ? 999 : bPriority) || a.localeCompare(b); + }) as [groupName, metrics] (groupName)} {@const filteredMetrics = getFilteredMetrics(groupName, metrics)} {@const hasMetrics = filteredMetrics.length > 0} {@const sectionOpen = (metricsSectionsOpen.current[groupName] ?? true) && hasMetrics} From 8dc298e0f1f543d5ab9c3482137f867a321582eb Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 26 Oct 2025 20:43:20 -0600 Subject: [PATCH 538/590] Add natural language datasets --- examples/piston-train-toy/.gitignore | 3 + examples/piston-train-toy/package.json | 1 + .../controls/DatasetControls.svelte | 39 + .../components/controls/DatasetSample.svelte | 160 +- .../components/controls/SelectDataset.svelte | 29 +- .../src/lib/train/data/collate.ts | 46 +- .../src/lib/train/data/index.ts | 39 +- .../src/lib/train/data/natural/collate.ts | 78 + .../src/lib/train/data/natural/dataset.ts | 308 +++ .../src/lib/train/data/natural/index.ts | 2 + .../src/lib/train/data/natural/shardCache.ts | 144 + .../src/lib/train/data/pipeline.ts | 11 +- .../piston-train-toy/src/lib/train/session.ts | 10 +- .../src/lib/train/tokenizer.ts | 2455 ++++++++++++++++- .../piston-train-toy/src/lib/train/types.ts | 17 + .../src/lib/train/utils/model.ts | 102 +- .../src/lib/workspace/config.svelte.ts | 26 +- .../src/lib/workspace/config.ts | 4 + examples/piston-train-toy/vite.config.ts | 20 +- examples/piston-train-toy/wrangler.jsonc | 5 + pnpm-lock.yaml | 9 + 21 files changed, 3415 insertions(+), 93 deletions(-) create mode 100644 examples/piston-train-toy/src/lib/train/data/natural/collate.ts create mode 100644 examples/piston-train-toy/src/lib/train/data/natural/dataset.ts create mode 100644 examples/piston-train-toy/src/lib/train/data/natural/index.ts create mode 100644 examples/piston-train-toy/src/lib/train/data/natural/shardCache.ts diff --git a/examples/piston-train-toy/.gitignore b/examples/piston-train-toy/.gitignore index 3b462cb0..a1bfde7c 100644 --- a/examples/piston-train-toy/.gitignore +++ b/examples/piston-train-toy/.gitignore @@ -21,3 +21,6 @@ Thumbs.db # Vite vite.config.js.timestamp-* vite.config.ts.timestamp-* + +static/tokenizer +static/tokenized diff --git a/examples/piston-train-toy/package.json b/examples/piston-train-toy/package.json index 5509597a..6ae89cca 100644 --- a/examples/piston-train-toy/package.json +++ b/examples/piston-train-toy/package.json @@ -14,6 +14,7 @@ "format": "prettier --write ." }, "dependencies": { + "@huggingface/jinja": "^0.5.1", "@piston-ml/piston-web": "workspace:^", "@types/katex": "^0.16.7", "echarts": "^6.0.0", diff --git a/examples/piston-train-toy/src/lib/components/controls/DatasetControls.svelte b/examples/piston-train-toy/src/lib/components/controls/DatasetControls.svelte index a11fe70d..d456a002 100644 --- a/examples/piston-train-toy/src/lib/components/controls/DatasetControls.svelte +++ b/examples/piston-train-toy/src/lib/components/controls/DatasetControls.svelte @@ -2,10 +2,12 @@ import type { ConfigParameter } from '$lib/train/data/toy/config'; import { DATASET_CONFIG_METADATA } from '$lib/train/data'; + import { NATURAL_DATASET_META } from '$lib/train/data/natural'; import { config, equalsConfigDefault, resetConfigToDefaults } from '$lib/workspace/config.svelte'; import CheckboxInput from './checkbox/CheckboxInput.svelte'; import DatasetSample from './DatasetSample.svelte'; + import RadioGroupInput from './radio/RadioGroupInput.svelte'; import SelectDataset from './SelectDataset.svelte'; import Slider from './Slider.svelte'; let { datasetName }: { datasetName: typeof config.data.dataset } = $props(); @@ -20,6 +22,7 @@ : undefined ); const showMaskRatio = $derived(config.model.topology === 'encoder'); + const isNatural = $derived(Object.keys(NATURAL_DATASET_META).includes(config.data.dataset)); const showDivider = $derived(showMaskRatio); @@ -92,6 +95,42 @@
    {/if} {/key} + {#if isNatural} + resetConfigToDefaults('data.natural.contextSize')} + /> + String(config.data.natural.vocabSize), + (v) => + (config.data.natural.vocabSize = + v === 'char' ? 'char' : (parseInt(v) as typeof config.data.natural.vocabSize)) + } + options={[ + { value: 'char', label: 'Character-level' }, + { value: '512', label: '512' }, + { value: '1024', label: '1024' }, + { value: '2048', label: '2048' }, + { value: '4096', label: '4096' }, + { value: '8192', label: '8192' }, + { value: '16384', label: '16384' }, + { value: '32768', label: '32768' }, + { value: '65536', label: '65536' } + ]} + hasDefaultValue={equalsConfigDefault('data.natural.vocabSize')} + onReset={() => resetConfigToDefaults('data.natural.vocabSize')} + /> + {/if} {#if showDivider}
    {/if} diff --git a/examples/piston-train-toy/src/lib/components/controls/DatasetSample.svelte b/examples/piston-train-toy/src/lib/components/controls/DatasetSample.svelte index a793a40a..2a2e3c2c 100644 --- a/examples/piston-train-toy/src/lib/components/controls/DatasetSample.svelte +++ b/examples/piston-train-toy/src/lib/components/controls/DatasetSample.svelte @@ -2,11 +2,68 @@ import { decodeSingle } from '$lib/train/tokenizer'; import { getCurrentDataset } from '$lib/workspace/config.svelte'; - const { - sampleData: { collated, hasPrompt }, - tokenizer, - dataset - } = $derived(getCurrentDataset() ?? { sampleData: [], tokenizer: null, dataset: null }); + const { sampleData, tokenizer, dataset } = $derived( + getCurrentDataset() ?? { sampleData: [], tokenizer: null, dataset: null } + ); + + // + // BEGIN involved thing we do to avoid height jittering when switching between datasets or tokenizers + // + + let tokenizerPending = $state(false); + let containerHeight = $state(0); + let lastStableHeight = $state(0); + let sampleDataPending = $state(false); + const anyPending = $derived(tokenizerPending || sampleDataPending); + + let currentTokenizerPromise: Promise | null = $state(null); + let currentSampleDataPromise: Promise | null = $state(null); + + $effect(() => { + if (tokenizer instanceof Promise && tokenizer !== currentTokenizerPromise) { + if (containerHeight > 0) { + lastStableHeight = containerHeight; + } + currentTokenizerPromise = tokenizer; + tokenizerPending = true; + tokenizer.finally(() => { + if (currentTokenizerPromise === tokenizer) { + tokenizerPending = false; + } + }); + } else if (!(tokenizer instanceof Promise)) { + currentTokenizerPromise = null; + tokenizerPending = false; + } + }); + + $effect(() => { + if (sampleData instanceof Promise && sampleData !== currentSampleDataPromise) { + if (containerHeight > 0) { + lastStableHeight = containerHeight; + } + currentSampleDataPromise = sampleData; + sampleDataPending = true; + sampleData.finally(() => { + if (currentSampleDataPromise === sampleData) { + sampleDataPending = false; + } + }); + } else if (!(sampleData instanceof Promise)) { + currentSampleDataPromise = null; + sampleDataPending = false; + } + }); + + $effect(() => { + if (!anyPending && containerHeight > 0) { + lastStableHeight = containerHeight; + } + }); + + // + // END involved thing + // function maskedFlagsForRange( fullSequence: number[] | undefined, @@ -25,7 +82,13 @@ - {decodeSingle(value, tokenizer)} + {#if tokenizer instanceof Promise} + {#await tokenizer then tokenizer} + {decodeSingle(value, tokenizer)} + {/await} + {:else} + {decodeSingle(value, tokenizer)} + {/if} {/snippet} @@ -37,50 +100,57 @@
    {/snippet} -
    - {#if collated.length > 0} -
    - - - - {#if hasPrompt} - - {/if} - - - - - {#each collated as { prompt, target, fullSequence } (Array.prototype.concat.call(fullSequence, prompt ?? [], target ?? []))} - {@const pLen = prompt?.length || 0} - {@const targetFlags = maskedFlagsForRange(fullSequence, pLen, target?.length ?? 0)} +
    0 ? `${lastStableHeight}px` : 'auto'} + style:overflow-y={anyPending ? 'hidden' : 'visible'} +> + {#await sampleData then { collated, hasPrompt }} + {#if collated.length > 0} +
    +
    PromptTarget
    + {#if hasPrompt} - {@const promptFlags = maskedFlagsForRange(fullSequence, 0, pLen)} + + {/if} + + + + + {#each collated as { prompt, target, fullSequence } (Array.prototype.concat.call(fullSequence, prompt ?? [], target ?? []))} + {@const pLen = prompt?.length || 0} + {@const targetFlags = maskedFlagsForRange(fullSequence, pLen, target?.length ?? 0)} + + {#if hasPrompt} + {@const promptFlags = maskedFlagsForRange(fullSequence, 0, pLen)} + + {/if} + + {/each} + + {#if hasPrompt} + {/if} - - - {/each} - - {#if hasPrompt} - {/if} - - - -
    PromptTarget
    + {@render tokenSequence(prompt!, promptFlags)} + - {@render tokenSequence(prompt!, promptFlags)} + {@render tokenSequence(target ?? fullSequence, targetFlags)}
    ... - {@render tokenSequence(target ?? fullSequence, targetFlags)} -
    ......
    -
    - {/if} + + + +
    + {/if} + {/await}
    diff --git a/examples/piston-train-toy/src/lib/components/controls/SelectDataset.svelte b/examples/piston-train-toy/src/lib/components/controls/SelectDataset.svelte index 645eda35..85dd1679 100644 --- a/examples/piston-train-toy/src/lib/components/controls/SelectDataset.svelte +++ b/examples/piston-train-toy/src/lib/components/controls/SelectDataset.svelte @@ -24,7 +24,26 @@ })); } + const GROUP_LABELS = { + natural: 'Natural Language', + toy: 'Toy/Synthetic' + }; + const options = $derived(getAvailableDatasets()); + const optionsGrouped = $derived.by(() => { + const groups = options.reduce( + (acc, opt) => { + acc[opt.type] = acc[opt.type] || []; + acc[opt.type].push(opt); + return acc; + }, + {} as Record + ); + return Object.entries(groups).map(([type, options]) => ({ + label: GROUP_LABELS[type as keyof typeof GROUP_LABELS], + options + })); + }); type $$Props = { value: DatasetName; @@ -97,7 +116,15 @@ {/snippet}
    - + {#snippet option(_opt, _selected)} {@const opt = _opt as DatasetOption} {@render nameAndBadges(opt)} diff --git a/examples/piston-train-toy/src/lib/train/data/collate.ts b/examples/piston-train-toy/src/lib/train/data/collate.ts index 331d904b..799d64c9 100644 --- a/examples/piston-train-toy/src/lib/train/data/collate.ts +++ b/examples/piston-train-toy/src/lib/train/data/collate.ts @@ -1,8 +1,16 @@ -import type { ToyCollateFnType } from '$lib/train/types'; +import type { + NaturalCollateFnType, + PistonCollateFnType, + PistonDatasetType, + ToyCollateFnType +} from '$lib/train/types'; import { gpu, int32, tensor, Tensor } from '@piston-ml/piston-web'; -import type { CollatedRawSequence, ToyDatasetLike, ToySequence } from './toy/dataset'; +import type { CollatedRawSequence, ToySequence } from './toy/dataset'; + +import { NaturalLanguageDataset } from './natural/dataset'; +import ToyDataset from './toy/dataset'; export type CollateWrapFunction = (sequences: number[][]) => T; @@ -11,18 +19,34 @@ export function tensorWrap(sequences: number[][]): Tensor { } // Utility functions for accessing raw format data -export function getCollatedSampleData( - dataset: ToyDatasetLike, - collateFn: ToyCollateFnType, +export async function getCollatedSampleData( + dataset: PistonDatasetType, + collateFn: PistonCollateFnType, sampleCount: number = 4 -): { +): Promise<{ samples: ToySequence[] | number[][]; collated: CollatedRawSequence[]; -} { - // Generate sample sequences - const iterator = dataset[Symbol.iterator](); - const samples = Array.from({ length: sampleCount }, () => iterator.next().value); - const collated = (collateFn as ToyCollateFnType)(samples as ToySequence[]).raw; +}> { + let collated: CollatedRawSequence[]; + let samples: ToySequence[] | number[][]; + + if (dataset instanceof ToyDataset) { + const iterator = dataset[Symbol.iterator](); + samples = Array.from({ length: sampleCount }, () => iterator.next().value); + collated = (collateFn as ToyCollateFnType)(samples as ToySequence[]).raw; + } else if (dataset instanceof NaturalLanguageDataset) { + const iterator = dataset[Symbol.asyncIterator](); + samples = []; + for (let i = 0; i < sampleCount; i++) { + const sample = await iterator.next(); + samples.push(sample.value); + } + collated = (collateFn as NaturalCollateFnType)(samples as number[][]).samples.map((s) => ({ + fullSequence: s + })); + } else { + throw new Error('Unsupported dataset type'); + } return { samples, collated }; } diff --git a/examples/piston-train-toy/src/lib/train/data/index.ts b/examples/piston-train-toy/src/lib/train/data/index.ts index 02e3780d..ff1679df 100644 --- a/examples/piston-train-toy/src/lib/train/data/index.ts +++ b/examples/piston-train-toy/src/lib/train/data/index.ts @@ -1,6 +1,27 @@ +import type { Config } from '$lib/workspace/config'; +import type { Random } from 'random-js'; + +import type { PistonDatasetType } from '../types'; + +import { + buildNaturalLanguageDataset, + NATURAL_DATASET_META, + type NaturalDatasetName +} from './natural'; +import { buildToyDataset } from './toy'; import { TOY_DATASET_CONFIG_DEFAULTS, TOY_DATASET_CONFIG_METADATA } from './toy/config'; export const DATASET_CONFIG_METADATA = { + ...Object.fromEntries( + Object.entries(NATURAL_DATASET_META).map(([name, meta]) => [ + name, + { + ...meta, + supportsModelTypes: ['encoder', 'decoder'], + type: 'natural' + } + ]) + ), ...Object.fromEntries( Object.entries(TOY_DATASET_CONFIG_METADATA).map(([name, meta]) => [ name, @@ -9,6 +30,22 @@ export const DATASET_CONFIG_METADATA = { ) } as const; -export const DATASET_CONFIG_DEFAULTS = TOY_DATASET_CONFIG_DEFAULTS; +export const DATASET_CONFIG_DEFAULTS = { + ...TOY_DATASET_CONFIG_DEFAULTS, + ...(Object.fromEntries( + Object.entries(NATURAL_DATASET_META).map(([name]) => [name, {}]) + ) as Record) +} as const; + +export function buildDataset( + config: Config, + generator: Random, + split: 'train' | 'val' +): PistonDatasetType { + if (Object.keys(NATURAL_DATASET_META).includes(config.data.dataset)) { + return buildNaturalLanguageDataset(split, config); + } + return buildToyDataset(config, generator); +} export * from './collate'; diff --git a/examples/piston-train-toy/src/lib/train/data/natural/collate.ts b/examples/piston-train-toy/src/lib/train/data/natural/collate.ts new file mode 100644 index 00000000..7b5a9a8f --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/data/natural/collate.ts @@ -0,0 +1,78 @@ +import type { Tensor } from '@piston-ml/piston-web'; +import type { Random } from 'random-js'; + +import { type CollateWrapFunction, tensorWrap } from '../collate'; + +export type NaturalLanguageAutoregressiveBatch = { + tensors: [T, T]; // [input, target] + samples: number[][]; +}; + +export type NaturalLanguageBidirectionalBatch = { + tensors: [T, T, T]; // [input, labels, attentionMask] + samples: number[][]; +}; + +export function naturalLanguageAutoregressiveCollate( + batch: number[][], + options: { wrapFunction?: CollateWrapFunction | null } = {} +): NaturalLanguageAutoregressiveBatch { + const wrap = options.wrapFunction === undefined ? tensorWrap : options.wrapFunction; + + const inputsArr: number[][] = []; + const targetsArr: number[][] = []; + for (const sequence of batch) { + const L = sequence.length; + if (L < 2) { + inputsArr.push([]); + targetsArr.push([]); + continue; + } + inputsArr.push(sequence.slice(0, L - 1)); + targetsArr.push(sequence.slice(1)); + } + + const inputs = wrap ? wrap(inputsArr) : inputsArr; + const targets = wrap ? wrap(targetsArr) : targetsArr; + + return { tensors: [inputs as T, targets as T], samples: batch }; +} + +export function naturalLanguageBidirectionalCollate( + batch: number[][], + options: { + maskRatio: number; + generator: Random; + maskTokenId: number; + wrapFunction?: CollateWrapFunction | null; + } +): NaturalLanguageBidirectionalBatch { + const { maskRatio, generator, maskTokenId, wrapFunction = tensorWrap } = options; + + const inputIdsArr: number[][] = []; + const labelsArr: number[][] = []; + const attentionMaskArr: number[][] = []; + + for (const sequence of batch) { + const labels: number[] = new Array(sequence.length).fill(-100); + const inputs: number[] = [...sequence]; + const attn: number[] = new Array(sequence.length).fill(1); + + for (let i = 0; i < sequence.length; i++) { + if (generator.real(0, 1) < maskRatio) { + labels[i] = sequence[i]; + inputs[i] = maskTokenId; + } + } + + inputIdsArr.push(inputs); + labelsArr.push(labels); + attentionMaskArr.push(attn); + } + + const inputIds = wrapFunction ? wrapFunction(inputIdsArr) : inputIdsArr; + const labels = wrapFunction ? wrapFunction(labelsArr) : labelsArr; + const attentionMask = wrapFunction ? wrapFunction(attentionMaskArr) : attentionMaskArr; + + return { tensors: [inputIds as T, labels as T, attentionMask as T], samples: batch }; +} diff --git a/examples/piston-train-toy/src/lib/train/data/natural/dataset.ts b/examples/piston-train-toy/src/lib/train/data/natural/dataset.ts new file mode 100644 index 00000000..3eb450ac --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/data/natural/dataset.ts @@ -0,0 +1,308 @@ +import type { CitationEntries } from '$lib/components/controls/Citations.svelte'; +import type { Config } from '$lib/workspace/config'; + +import { PUBLIC_DATA_URL } from '$env/static/public'; +import { PreTrainedTokenizer } from '$lib/train/tokenizer'; +import { AsyncIterableDataset } from '@piston-ml/piston-web'; + +import type { ToyTokenizer } from '../toy/types'; + +import { globalShardCache } from './shardCache'; + +export type NaturalDatasetSplit = 'train' | 'val'; + +export type NaturalDatasetName = 'tinychat' | 'tinyshakespeare' | 'tinystories' | 'fineweb'; +export type NaturalDatasetMeta = { + name: string; + description: string; + citations: CitationEntries; +}; +export const NATURAL_DATASET_META: Record = { + tinystories: { + name: 'TinyStories (v2)', + description: 'Short stories generated by GPT-4 using a limited vocabulary.', + citations: { + entries: [ + { + name: 'Eldan, 2023', + url: 'https://arxiv.org/abs/2305.07759' + } + ] + } + }, + tinyshakespeare: { + name: 'TinyShakespeare', + description: "40,000 lines of Shakespeare from a variety of Shakespeare's plays.", + citations: { + entries: [ + { + name: 'Karpathy, 2015', + url: 'https://github.com/karpathy/char-rnn' + } + ] + } + }, + tinychat: { + name: 'TinyChat', + description: 'Short conversations generated by GPT-4o.', + citations: { + entries: [ + { + name: 'Raghavendra, 2024', + url: 'https://web.archive.org/https://nikhilr.io/posts/TinyChat15M/' + } + ] + } + }, + fineweb: { + name: 'FineWeb', + description: 'Cleaned and deduplicated English web data from CommonCrawl.', + citations: { + entries: [ + { + name: 'Penedo et al., 2024', + url: 'https://huggingface.co/spaces/HuggingFaceFW/blogpost-fineweb-v1' + } + ] + } + } +}; + +export interface NaturalDatasetConfig { + name: NaturalDatasetName; + split: NaturalDatasetSplit; + contextSize: number; + masked: boolean; + vocabSize: 'char' | 512 | 1024 | 2048 | 4096 | 8192 | 16384 | 32768 | 65536; +} + +function joinUrl(base: string, path: string): string { + const trimmedBase = base.endsWith('/') ? base.slice(0, -1) : base; + const trimmedPath = path.startsWith('/') ? path.slice(1) : path; + return `${trimmedBase}/${trimmedPath}`; +} + +function pad4(n: number): string { + return n.toString().padStart(4, '0'); +} + +// Format is from Karpathy's llm.c: https://github.com/karpathy/llm.c +function parseShard(buffer: ArrayBuffer): Uint16Array { + if (buffer.byteLength < 256 * 4) { + throw new Error('Shard too small to contain header'); + } + const header = new Int32Array(buffer, 0, 256); + const magic = header[0]; + const version = header[1]; + const ntok = header[2]; + if (magic !== 20251003) { + throw new Error(`magic number mismatch in the data .bin file (got ${magic})`); + } + if (version !== 1) { + throw new Error(`unsupported version ${version}`); + } + const tokens = new Uint16Array(buffer, 256 * 4); + if (tokens.length !== ntok) { + throw new Error(`number of tokens read (${tokens.length}) does not match header (${ntok})`); + } + return tokens; +} + +async function fetchShard(url: string): Promise { + // Try cache first + const cached = await globalShardCache.get(url).catch(() => undefined); + if (cached) { + return parseShard(cached); + } + const res = await fetch(url); + if (res.status === 404) return null; + if (!res.ok) { + throw new Error(`Failed to fetch shard ${url}: ${res.status} ${res.statusText}`); + } + const buf = await res.arrayBuffer(); + // Store in cache (best-effort) + void globalShardCache.set(url, buf); + return parseShard(buf); +} + +// const charTokenizer: ToyTokenizer = { +// vocab: Object.fromEntries(Array.from({ length: 256 }, (_, i) => [String.fromCharCode(i), i])), +// ids: Object.fromEntries(Array.from({ length: 256 }, (_, i) => [i, String.fromCharCode(i)])), +// lastToken: 0, +// decode: (tokens: number[]) => tokens.map((token) => String.fromCharCode(token)).join('') +// }; + +function buildCharTokenizer(masked: boolean): ToyTokenizer { + const vocabList = Array.from({ length: 256 }, (_, i) => String.fromCharCode(i)); + vocabList.push(''); + vocabList[32] = '␣'; + if (masked) { + vocabList.push(''); + } + const vocab = Object.fromEntries(vocabList.map((char, i) => [char, i])); + const ids = Object.fromEntries(vocabList.map((char, i) => [i, char])); + return { + vocab, + ids, + lastToken: vocabList.length - 1, + decode: (tokens: number[]) => tokens.map((token) => ids[token] ?? '').join('') + }; +} + +type NaturalLanguageShard = { + data: Uint16Array; + cursor: number; +}; + +export class NaturalLanguageDataset extends AsyncIterableDataset { + readonly name: NaturalDatasetName; + readonly split: NaturalDatasetSplit; + readonly contextSize: number; + readonly charLevel: boolean; + vocabSize: number | Promise; + maskId: number | Promise | null; + eosId: number | Promise; + bosId: number | Promise; + tokenizer: PreTrainedTokenizer | Promise | ToyTokenizer | null; + + private shard: NaturalLanguageShard | null = null; + private shardIndex: number = 0; + private nextShardPromise: Promise | null = null; + + constructor(config: NaturalDatasetConfig) { + super(); + this.name = config.name; + this.split = config.split; + this.contextSize = config.contextSize; + this.charLevel = Boolean(config.vocabSize === 'char'); + const baseVocabSize = config.vocabSize === 'char' ? 257 : config.vocabSize; + + this.tokenizer = null; + + if (config.vocabSize === 'char') { + this.tokenizer = buildCharTokenizer(config.masked); + if (config.masked) { + this.vocabSize = baseVocabSize + 1; + this.maskId = this.vocabSize - 1; + this.eosId = this.vocabSize - 2; + this.bosId = this.vocabSize - 2; + } else { + this.vocabSize = baseVocabSize; + this.maskId = null; + this.eosId = baseVocabSize - 1; + this.bosId = baseVocabSize - 1; + } + } else { + this.vocabSize = config.vocabSize; + this.tokenizer = PreTrainedTokenizer.fromPretrained(`${this.name}/${this.vocabSize}`); + this.maskId = this.tokenizer.then((tokenizer) => tokenizer.maskTokenId!); + this.eosId = this.tokenizer.then((tokenizer) => tokenizer.eosTokenId!); + this.bosId = this.tokenizer.then((tokenizer) => tokenizer.bosTokenId!); + + this.tokenizer.then((tokenizer) => { + this.tokenizer = tokenizer; + this.maskId = tokenizer.maskTokenId!; + this.eosId = tokenizer.eosTokenId!; + this.bosId = tokenizer.bosTokenId!; + }); + } + } + + private buildShardUrl(shardIndex: number): string { + const file = `${this.split}-${pad4(shardIndex)}.bin`; + return joinUrl( + PUBLIC_DATA_URL, + `/tokenized/${this.name}/${this.charLevel ? 'char' : this.vocabSize}/${file}?v=1` + ); + } + + private async ensureCurrentLoaded(): Promise { + if (this.shard) return; + const first = await fetchShard(this.buildShardUrl(0)); + if (!first) { + // No data available; mark as exhausted + this.shard = null; + return; + } + this.shard = { data: first, cursor: 0 }; + if (this.split === 'train') { + this.nextShardPromise = fetchShard(this.buildShardUrl(1)); + } + } + + private async advanceShard(): Promise { + if (this.split === 'val') { + // Validation: single shard only; end iteration + this.shard = null; + return; + } + // Train: move to next shard, keeping one-shard lookahead + const next = this.nextShardPromise ? await this.nextShardPromise : null; + if (!next) { + this.shard = null; + return; + } + this.shardIndex += 1; + this.shard = { data: next, cursor: 0 }; + // Prefetch following shard + this.nextShardPromise = fetchShard(this.buildShardUrl(this.shardIndex + 1)); + } + + public async *[Symbol.asyncIterator](): AsyncIterator { + await this.ensureCurrentLoaded(); + while (true) { + if (!this.shard) { + return; + } + const tokens = this.shard.data; + const start = this.shard.cursor; + const end = start + this.contextSize; + if (end <= tokens.length) { + const slice = tokens.slice(start, end); + this.shard.cursor = end; + yield Array.from(slice); + continue; + } + // Advance if we need more tokens than are available in the current shard + await this.advanceShard(); + } + } + + // Export/import state for resumption + public exportState(): { shardIndex: number; cursor: number } | null { + if (!this.shard) return { shardIndex: this.shardIndex, cursor: 0 }; + return { shardIndex: this.shardIndex, cursor: this.shard.cursor }; + } + + public async importState(state: { shardIndex: number; cursor: number } | null): Promise { + if (!state) return; + this.shardIndex = Math.max(0, state.shardIndex | 0); + const current = await fetchShard(this.buildShardUrl(this.shardIndex)); + if (!current) { + this.shard = null; + this.nextShardPromise = null; + return; + } + this.shard = { data: current, cursor: Math.min(state.cursor | 0, current.length) }; + if (this.split === 'train') { + this.nextShardPromise = fetchShard(this.buildShardUrl(this.shardIndex + 1)); + } else { + this.nextShardPromise = null; + } + } +} + +export function buildNaturalLanguageDataset( + split: NaturalDatasetSplit, + config: Config +): NaturalLanguageDataset { + return new NaturalLanguageDataset({ + name: config.data.dataset as NaturalDatasetName, + split, + contextSize: config.data.natural.contextSize, + masked: config.model.topology === 'encoder', + vocabSize: config.data.natural.vocabSize + }); +} + +export default NaturalLanguageDataset; diff --git a/examples/piston-train-toy/src/lib/train/data/natural/index.ts b/examples/piston-train-toy/src/lib/train/data/natural/index.ts new file mode 100644 index 00000000..300a42ca --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/data/natural/index.ts @@ -0,0 +1,2 @@ +export * from './collate'; +export * from './dataset'; diff --git a/examples/piston-train-toy/src/lib/train/data/natural/shardCache.ts b/examples/piston-train-toy/src/lib/train/data/natural/shardCache.ts new file mode 100644 index 00000000..e77430a4 --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/data/natural/shardCache.ts @@ -0,0 +1,144 @@ +// A tiny IndexedDB-backed LRU cache specialized for dataset shard ArrayBuffers. +// Uses one object store for blobs and a metadata store for LRU bookkeeping. + +const DB_NAME = 'natural-shard-cache'; +const DB_VERSION = 1; +const STORE_BLOBS = 'blobs'; +const META_KEY = 'lru'; +const STORE_META = 'meta'; + +export interface ShardCacheOptions { + // Maximum total size in bytes + maxBytes?: number; +} + +type LruEntry = { last: number; size: number }; +type LruState = { entries: Record; totalSize: number }; + +const DEFAULTS: Required = { + maxBytes: 256 * 1024 * 1024 // 256MB +}; + +function openDb(): Promise { + return new Promise((resolve, reject) => { + const req = indexedDB.open(DB_NAME, DB_VERSION); + req.onupgradeneeded = () => { + const db = req.result; + if (!db.objectStoreNames.contains(STORE_BLOBS)) { + db.createObjectStore(STORE_BLOBS); + } + if (!db.objectStoreNames.contains(STORE_META)) { + db.createObjectStore(STORE_META); + } + }; + req.onsuccess = () => resolve(req.result); + req.onerror = () => reject(req.error); + req.onblocked = () => reject(new Error('IndexedDB upgrade blocked')); + }); +} + +function promisify(req: IDBRequest): Promise { + return new Promise((resolve, reject) => { + req.onsuccess = () => resolve(req.result); + req.onerror = () => reject(req.error); + }); +} + +function txRequest( + db: IDBDatabase, + storeName: string, + mode: IDBTransactionMode, + op: (store: IDBObjectStore) => IDBRequest +): Promise { + const tx = db.transaction(storeName, mode); + const store = tx.objectStore(storeName); + return promisify(op(store)); +} + +async function readMeta(db: IDBDatabase): Promise { + const res = await txRequest(db, STORE_META, 'readonly', (s) => + s.get(META_KEY) + ); + return res ?? { entries: {}, totalSize: 0 }; +} + +async function writeMeta(db: IDBDatabase, meta: LruState): Promise { + await txRequest(db, STORE_META, 'readwrite', (s) => s.put(meta, META_KEY)); +} + +async function getBlob(db: IDBDatabase, url: string): Promise { + return txRequest(db, STORE_BLOBS, 'readonly', (s) => s.get(url)); +} + +async function putBlob(db: IDBDatabase, url: string, buf: ArrayBuffer): Promise { + await txRequest(db, STORE_BLOBS, 'readwrite', (s) => s.put(buf, url)); +} + +async function deleteBlob(db: IDBDatabase, url: string): Promise { + await txRequest(db, STORE_BLOBS, 'readwrite', (s) => s.delete(url)); +} + +export class ShardCache { + private dbPromise: Promise | null = null; + private options: Required; + + constructor(options: ShardCacheOptions = {}) { + this.options = { ...DEFAULTS, ...options }; + } + + private get db(): Promise { + if (!this.dbPromise) this.dbPromise = openDb(); + return this.dbPromise; + } + + async get(url: string): Promise { + const db = await this.db; + const [meta, buf] = await Promise.all([readMeta(db), getBlob(db, url)]); + if (!buf) return undefined; + // Bump LRU timestamp + meta.entries[url] = { last: performance.now(), size: buf.byteLength }; + await writeMeta(db, meta); + console.debug('dataset shard cache hit', url); + return buf; + } + + async set(url: string, buf: ArrayBuffer): Promise { + const db = await this.db; + const meta = await readMeta(db); + const size = buf.byteLength; + const prevSize = meta.entries[url]?.size ?? 0; + meta.entries[url] = { last: performance.now(), size }; + meta.totalSize += size - prevSize; + await putBlob(db, url, buf); + await this.evictIfNeeded(db, meta); + } + + private async evictIfNeeded(db: IDBDatabase, meta: LruState): Promise { + try { + // Keep at most maxBytes + const { maxBytes } = this.options; + const keys = Object.keys(meta.entries); + if (keys.length === 0) return; + + const overBytes = Math.max(0, meta.totalSize - maxBytes); + if (overBytes === 0) return; + + // Sort by last access ascending + keys.sort((a, b) => meta.entries[a].last - meta.entries[b].last); + + let bytesToFree = overBytes; + for (const key of keys) { + if (bytesToFree <= 0) break; + const size = meta.entries[key]?.size ?? 0; + await deleteBlob(db, key); + delete meta.entries[key]; + meta.totalSize = Math.max(0, meta.totalSize - size); + if (bytesToFree > 0) bytesToFree = Math.max(0, bytesToFree - size); + } + } finally { + await writeMeta(db, meta); + } + } +} + +export const globalShardCache = new ShardCache(); diff --git a/examples/piston-train-toy/src/lib/train/data/pipeline.ts b/examples/piston-train-toy/src/lib/train/data/pipeline.ts index aa8b972f..37749db1 100644 --- a/examples/piston-train-toy/src/lib/train/data/pipeline.ts +++ b/examples/piston-train-toy/src/lib/train/data/pipeline.ts @@ -2,14 +2,13 @@ import type { Config } from '$lib/workspace/config'; import type { Tensor } from '@piston-ml/piston-web'; import type { Random } from 'random-js'; -import type { ToyDatasetLike } from './toy/dataset'; +import type { PistonDatasetType } from '../types'; -import { tensorWrap } from '.'; +import { buildDataset, tensorWrap } from '.'; import { createDataloader } from '../utils/model'; -import { buildToyDataset } from './toy'; export type BuiltData = { - dataset: ToyDatasetLike; + dataset: PistonDatasetType; iterator: AsyncIterator<{ readonly tensors: Tensor[]; readonly samples?: unknown[]; @@ -19,9 +18,9 @@ export type BuiltData = { export async function buildDataPipeline( config: Config, generator: Random, - trainDatasetOverride?: ToyDatasetLike + trainDatasetOverride?: PistonDatasetType ): Promise { - const trainDataset = trainDatasetOverride ?? buildToyDataset(config, generator); + const trainDataset = trainDatasetOverride ?? buildDataset(config, generator, 'train'); const [trainDataloader] = createDataloader(config, trainDataset, generator, tensorWrap); return { diff --git a/examples/piston-train-toy/src/lib/train/session.ts b/examples/piston-train-toy/src/lib/train/session.ts index f46eb68d..a73502a9 100644 --- a/examples/piston-train-toy/src/lib/train/session.ts +++ b/examples/piston-train-toy/src/lib/train/session.ts @@ -14,15 +14,13 @@ import type { BuiltData } from './data/pipeline'; import type { ToyAutoregressiveBatch, ToyBidirectionalBatch, - ToyDatasetLike, ToyEncoderDecoderBatch } from './data/toy/dataset'; import type { RunWorkerEvent, RunWorkerEventWithoutRunId, WorkerEvent } from './protocol'; -import type { GeneratableModel } from './types'; +import type { GeneratableModel, PistonDatasetType } from './types'; -import { tensorWrap } from './data'; +import { buildDataset, tensorWrap } from './data'; import { buildDataPipeline } from './data/pipeline'; -import { buildToyDataset } from './data/toy'; import { DecoderTransformer, EncoderDecoderTransformer, @@ -48,7 +46,7 @@ export class TrainingSession { private model!: GeneratableModel; private optimizer!: piston.Optimizer; private scheduler: LRScheduler | undefined; - private trainDataset!: ToyDatasetLike; + private trainDataset!: PistonDatasetType; private blockSize!: number | { source: number; target: number }; private isSetup: boolean = false; @@ -91,7 +89,7 @@ export class TrainingSession { // training const trainGenerator = seededRandom(); - this.trainDataset = buildToyDataset(this.config, trainGenerator); + this.trainDataset = buildDataset(this.config, trainGenerator, 'train'); // Calculate vocab size using shared utility const vocabSize = calculateVocabSize(this.trainDataset); diff --git a/examples/piston-train-toy/src/lib/train/tokenizer.ts b/examples/piston-train-toy/src/lib/train/tokenizer.ts index 487ec809..39d51f49 100644 --- a/examples/piston-train-toy/src/lib/train/tokenizer.ts +++ b/examples/piston-train-toy/src/lib/train/tokenizer.ts @@ -2,8 +2,2461 @@ * @fileoverview Simplified Tokenizer implementation adapted from huggingface/transformers.js */ +import { PUBLIC_DATA_URL } from '$env/static/public'; +import { Template } from '@huggingface/jinja'; +import { int32, Tensor, tensor } from '@piston-ml/piston-web'; + import type { ToyTokenizer } from './data/toy/types'; -export function decodeSingle(value: number, tokenizer: ToyTokenizer | null): string { +/* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */ +abstract class Callable { + /** + * Creates a new instance of the Callable class. + */ + constructor() { + /** + * Creates a closure that delegates to a private method 'call' with the given arguments. + * @param args Zero or more arguments to pass to the 'call' method. + * @returns The result of calling the 'call' method. + */ + const closure = ((...args: Args) => { + return (closure as unknown as { call: (...args: Args) => Return }).call(...args); + }) as unknown as (...args: Args) => Return; + return Object.setPrototypeOf(closure, new.target.prototype) as unknown as this & + ((...args: Args) => Return); + } + + /** + * This method should be implemented in subclasses to provide the + * functionality of the callable object. + * + * @param args Zero or more arguments to pass to the 'call' method. + * @throws {Error} If the subclass does not implement the `call` method. + */ + protected abstract call(..._args: Args): Return; +} +interface Callable { + (...args: Args): Return; +} + +// Discriminated config helpers +type WithType = { type: TType }; + +// Normalizer configs +type NormalizerSequenceConfig = WithType<'Sequence'> & { normalizers: NormalizerConfig[] }; +type NFCConfig = WithType<'NFC'>; +type NFDConfig = WithType<'NFD'>; +type NFKCConfig = WithType<'NFKC'>; +type NFKDConfig = WithType<'NFKD'>; +type StripConfig = WithType<'Strip'> & { stripLeft?: boolean; stripRight?: boolean }; +type LowercaseConfig = WithType<'Lowercase'>; +type PrependConfig = WithType<'Prepend'> & { prepend: string }; +type NormalizerConfig = + | NormalizerSequenceConfig + | NFCConfig + | NFDConfig + | NFKCConfig + | NFKDConfig + | StripConfig + | LowercaseConfig + | PrependConfig; + +// PreTokenizer configs and options +type PreTokenizeOptions = { sectionIndex?: number }; +type PreTokenizerSequenceConfig = WithType<'Sequence'> & { pretokenizers: PreTokenizerConfig[] }; +type WhitespacePreTokenizerConfig = WithType<'Whitespace'>; +type WhitespaceSplitConfig = WithType<'WhitespaceSplit'>; +type MetaspacePreTokenizerConfig = WithType<'Metaspace'> & { + addPrefixSpace: boolean; + replacement: string; + strRep?: string; + prependScheme?: 'first' | 'never' | 'always'; +}; +type ByteLevelPreTokenizerConfig = WithType<'ByteLevel'> & { + addPrefixSpace: boolean; + trimOffsets: boolean; + useRegex?: boolean; +}; +type PreTokenizerConfig = + | PreTokenizerSequenceConfig + | WhitespacePreTokenizerConfig + | WhitespaceSplitConfig + | MetaspacePreTokenizerConfig + | ByteLevelPreTokenizerConfig; + +// PostProcessor configs and options +type PostProcessorOptions = { addSpecialTokens?: boolean }; +type PostProcessorResult = { tokens: string[]; tokenTypeIds?: number[] }; +type PostProcessorSequenceConfig = WithType<'Sequence'> & { processors: PostProcessorConfig[] }; +type ByteLevelPostProcessorConfig = WithType<'ByteLevel'>; +type PostProcessorConfig = PostProcessorSequenceConfig | ByteLevelPostProcessorConfig; + +// Decoder configs +type ByteLevelDecoderConfig = WithType<'ByteLevel'> & { trimOffsets?: boolean }; +type ByteFallbackConfig = WithType<'ByteFallback'>; +type FuseDecoderConfig = WithType<'Fuse'>; +type StripDecoderConfig = WithType<'Strip'> & { content: string; start: number; stop: number }; +type DecoderSequenceConfig = WithType<'Sequence'> & { decoders: DecoderConfig[] }; +type BPEDecoderConfig = WithType<'BPEDecoder'> & { suffix: string }; +type DecoderConfig = + | ByteLevelDecoderConfig + | ByteFallbackConfig + | FuseDecoderConfig + | StripDecoderConfig + | DecoderSequenceConfig + | BPEDecoderConfig; + +// Model configs +export interface TokenizerModelConfig { + fuseUnk: boolean; + byteFallback: boolean; + ignoreMerges: boolean; +} + +type BPEConfig = WithType<'BPE'> & + TokenizerModelConfig & { + vocab: Record; + merges: string[] | [string, string][]; + unkToken: string; + endOfWordSuffix?: string; + continuingSubwordSuffix?: string | null; + }; + +type TokenizerModelFactoryConfig = BPEConfig; // Extend when additional models are added + +// Tokenizer JSON and runtime config +interface TokenizerJSON { + normalizer: NormalizerConfig | null; + preTokenizer: PreTokenizerConfig | null; + model: TokenizerModelFactoryConfig; + postProcessor: PostProcessorConfig | null; + decoder: DecoderConfig | null; + addedTokens: AddedTokenConfig[]; +} + +interface TokenizerConfig { + [key: string]: unknown; + additionalSpecialTokens?: string[]; + modelMaxLength: number; + removeSpace: boolean; + cleanUpTokenizationSpaces?: boolean; + paddingSide?: 'left' | 'right'; + addBosToken?: boolean; + addEosToken?: boolean; + chatTemplate?: null | Array<{ name: string; template: string }> | Record; +} + +const TOKENIZER_URL = PUBLIC_DATA_URL + 'tokenizer'; + +/** + * Loads a tokenizer from the specified path. + * @param tokenizerName The path to the tokenizer directory. + * @returns A promise that resolves with tokenizer JSON and config. + */ +async function loadTokenizer(tokenizerName: string): Promise<[TokenizerJSON, TokenizerConfig]> { + return Promise.all([ + fetchJSON(TOKENIZER_URL, `${tokenizerName}/tokenizer.json`).then( + camelCaseKeysDeep + ), + fetchJSON(TOKENIZER_URL, `${tokenizerName}/tokenizer_config.json`).then( + camelCaseKeysDeep + ) + ]); +} + +function isPlainObject(value: unknown): value is Record { + return ( + value !== null && typeof value === 'object' && Object.getPrototypeOf(value) === Object.prototype + ); +} + +function toCamelKey(key: string): string { + return key.includes('_') + ? key.replace(/_+([a-zA-Z0-9])/g, (_m, c: string) => c.toUpperCase()) + : key; +} + +function camelCaseKeysDeep(input: T): T { + if (Array.isArray(input)) { + return input.map((item) => camelCaseKeysDeep(item)) as unknown as T; + } + if (isPlainObject(input)) { + const obj = input as Record; + const out: Record = Object.create(null); + for (const [key, value] of Object.entries(obj)) { + const transformed = camelCaseKeysDeep(value); + // Preserve original snake_case for compatibility + out[key] = transformed; + const camelKey = toCamelKey(key); + if (camelKey !== key && !(camelKey in out)) { + out[camelKey] = transformed; + } + } + return out as unknown as T; + } + return input; +} + +// Minimal fetch wrapper used here; replace with project-util if available +async function fetchJSON(basePath: string, fileName: string): Promise { + const url = `${basePath.replace(/\/$/, '')}/${fileName}`; + const res = await fetch(url); + if (!res.ok) throw new Error(`Failed to load ${fileName} from ${url}`); + return res.json() as Promise; +} + +/** + * Helper function to convert an Object to a Map + * @param obj The object to convert. + * @returns The map. + */ +function objectToMap(obj: Record): Map { + return new Map(Object.entries(obj)); +} + +/** + * Helper function to fuse consecutive unknown tokens. + * @param arr The list of input tokens + * @param tokensToIds The mapping from tokens to token ids. + * @param unkTokenId The value to fuse on. + */ +function fuseUnk(arr: string[], tokensToIds: Map, unkTokenId: number): string[] { + const fused = []; + let i = 0; + while (i < arr.length) { + fused.push(arr[i]); + if ((tokensToIds.get(arr[i]) ?? unkTokenId) !== unkTokenId) { + ++i; + continue; + } + + while (++i < arr.length && (tokensToIds.get(arr[i]) ?? unkTokenId) === unkTokenId) { + if (tokensToIds.get(fused[fused.length - 1]) !== unkTokenId) { + fused[fused.length - 1] += arr[i]; + } + } + } + + return fused; +} + +/** + * Split a string on whitespace. + * @param text The text to split. + * @returns The split string. + */ +function whitespaceSplit(text: string): string[] { + return text.match(/\S+/g) || []; +} + +/** + * Represent a token added by the user on top of the existing Model vocabulary. + * AddedToken can be configured to specify the behavior they should have in various situations like: + * - Whether they should only match single words + * - Whether to include any whitespace on its left or right + */ +interface AddedTokenConfig { + content: string; + id: number; + singleWord?: boolean; + lstrip?: boolean; + rstrip?: boolean; + normalized?: boolean; + special?: boolean; +} +class AddedToken { + content: string; + id: number; + singleWord: boolean; + lstrip: boolean; + rstrip: boolean; + special: boolean; + normalized: boolean | null; + /** + * Creates a new instance of AddedToken. + * @param config Added token configuration object. + * @param config.content The content of the added token. + * @param config.id The id of the added token. + * @param config.singleWord Whether this token must be a single word or can break words. + * @param config.lstrip Whether this token should strip whitespaces on its left. + * @param config.rstrip Whether this token should strip whitespaces on its right. + * @param config.normalized Whether this token should be normalized. + * @param config.special Whether this token is special. + */ + constructor(config: AddedTokenConfig) { + this.content = config.content; + this.id = config.id; + this.singleWord = config.singleWord ?? false; + this.lstrip = config.lstrip ?? false; + this.rstrip = config.rstrip ?? false; + this.special = config.special ?? false; + this.normalized = config.normalized ?? null; + } +} + +export interface TokenizerModelConfig { + fuseUnk: boolean; + byteFallback: boolean; + ignoreMerges: boolean; +} + +/** + * Abstract base class for tokenizer models. + */ +export class TokenizerModel extends Callable<[string[]], string[]> { + config: TokenizerModelConfig; + vocab: string[]; + tokensToIds: Map; + unkTokenId?: number; + unkToken?: string; + endOfWordSuffix?: string; + fuseUnk: boolean; + /** + * Creates a new instance of TokenizerModel. + * @param config The configuration object for the TokenizerModel. + */ + constructor(config: TokenizerModelConfig) { + super(); + this.config = config; + + this.vocab = []; + + this.tokensToIds = new Map(); + + this.unkTokenId = undefined; + this.unkToken = undefined; + this.endOfWordSuffix = undefined; + + this.fuseUnk = this.config.fuseUnk ?? false; + } + + /** + * Instantiates a new TokenizerModel instance based on the configuration object provided. + * @param config The configuration object for the TokenizerModel. + * @param _args Optional arguments to pass to the specific TokenizerModel constructor. + * @returns A new instance of a TokenizerModel. + * @throws Will throw an error if the TokenizerModel type in the config is not recognized. + */ + static fromConfig(config: TokenizerModelFactoryConfig, ..._args: unknown[]): TokenizerModel { + switch (config.type) { + case 'BPE': + default: + return new BPE(config); + } + } + + /** + * Internal function to call the TokenizerModel instance. + * @param tokens The tokens to encode. + * @returns The encoded tokens. + */ + protected call(...[tokens]: [string[]]): string[] { + tokens = this.encode(tokens); + if (this.fuseUnk) { + // Fuse unknown tokens + tokens = fuseUnk(tokens, this.tokensToIds, this.unkTokenId as number); + } + return tokens; + } + + /** + * Encodes a list of tokens into a list of token IDs. + * @param tokens The tokens to encode. + * @returns The encoded tokens. + * @throws Will throw an error if not implemented in a subclass. + */ + encode(_tokens: string[]): string[] { + throw Error('encode should be implemented in subclass.'); + } + + /** + * Converts a list of tokens into a list of token IDs. + * @param tokens The tokens to convert. + * @returns The converted token IDs. + */ + convertTokensToIds(tokens: string[]): number[] { + return tokens.map((t) => this.tokensToIds.get(t) ?? (this.unkTokenId as number)); + } + + /** + * Converts a list of token IDs into a list of tokens. + * @param ids The token IDs to convert. + * @returns The converted tokens. + */ + convertIdsToTokens(ids: number[] | bigint[]): string[] { + return ids.map((i) => this.vocab[Number(i)] ?? (this.unkToken as string)); + } +} + +/** + * Returns list of utf-8 byte and a mapping to unicode strings. + * Specifically avoids mapping to whitespace/control characters the BPE code barfs on. + * @returns Object with utf-8 byte keys and unicode string values. + */ +const BYTES_TO_UNICODE = (() => { + // Returns list of utf-8 byte and a mapping to unicode strings. + // We specifically avoids mapping to whitespace/control characters the bpe code barfs on. + + const bs = [ + ...Array.from( + { length: '~'.charCodeAt(0) - '!'.charCodeAt(0) + 1 }, + (_, i) => i + '!'.charCodeAt(0) + ), + ...Array.from( + { length: '¬'.charCodeAt(0) - '¡'.charCodeAt(0) + 1 }, + (_, i) => i + '¡'.charCodeAt(0) + ), + ...Array.from( + { length: 'ÿ'.charCodeAt(0) - '®'.charCodeAt(0) + 1 }, + (_, i) => i + '®'.charCodeAt(0) + ) + ]; + const cs = bs.slice(); + let n = 0; + for (let b = 0; b < 256; ++b) { + if (!bs.includes(b)) { + bs.push(b); + cs.push(256 + n); + n += 1; + } + } + const ccs = cs.map((n) => String.fromCharCode(n)); + return Object.fromEntries(bs.map((b, i) => [b, ccs[i]])); +})(); + +const UNICODE_TO_BYTES = Object.fromEntries( + Object.entries(BYTES_TO_UNICODE).map(([key, value]) => [value, key]) +); + +interface BPENode { + token: string; + bias: number; + score?: number; + prev?: BPENode; + next?: BPENode; +} + +/** + * BPE class for encoding text into Byte-Pair-Encoding (BPE) tokens. + */ +class BPE extends TokenizerModel { + merges!: [string, string][]; + bpeRanks!: Map; + continuingSubwordSuffix!: string | null; + byteFallback!: boolean; + textEncoder!: TextEncoder; + ignoreMerges!: boolean; + maxLengthToCache!: number; + cacheCapacity!: number; + cache!: LRUCache; + + constructor(config: BPEConfig) { + super(config); + this.tokensToIds = objectToMap(config.vocab); + this.unkTokenId = this.tokensToIds.get(config.unkToken) as number; + this.unkToken = config.unkToken as string; + this.vocab = new Array(this.tokensToIds.size); + for (const [key, value] of this.tokensToIds) { + this.vocab[value] = key; + } + const useNewMergeFormat = Array.isArray(config.merges[0]); + this.merges = useNewMergeFormat + ? (config.merges as [string, string][]) + : (config.merges as string[]).map((x) => x.split(' ', 2) as [string, string]); + this.bpeRanks = new Map(this.merges.map((x, i) => [JSON.stringify(x), i])) as Map< + string, + number + >; + this.endOfWordSuffix = config.endOfWordSuffix as string | undefined; + this.continuingSubwordSuffix = (config.continuingSubwordSuffix ?? null) as string | null; + this.byteFallback = (this.config.byteFallback ?? false) as boolean; + if (this.byteFallback) { + this.textEncoder = new TextEncoder(); + } + this.ignoreMerges = (this.config.ignoreMerges ?? false) as boolean; + this.maxLengthToCache = 256; + this.cacheCapacity = 10000; + this.cache = new LRUCache(this.cacheCapacity); + } + clearCache() { + this.cache.clear(); + } + bpe(token: string): string[] { + if (token.length === 0) { + return []; + } + const cached = this.cache.get(token); + if (cached !== undefined) { + return cached; + } + const word = Array.from(token); + if (this.endOfWordSuffix) { + word[word.length - 1] += this.endOfWordSuffix; + } + let result: string[] = []; + if (word.length > 1) { + const queue = new PriorityQueue((a, b) => (a.score as number) < (b.score as number)); + let startingNode: BPENode = { + token: word[0], + bias: 0, + prev: undefined, + next: undefined + }; + let previousNode = startingNode; + for (let i = 1; i < word.length; ++i) { + const currentNode: BPENode = { + bias: i / word.length, + token: word[i], + prev: previousNode, + next: undefined + }; + previousNode.next = currentNode; + this.addNode(queue, previousNode); + previousNode = currentNode; + } + while (!queue.isEmpty()) { + const node = queue.pop() as BPENode & { + deleted?: boolean; + prev?: BPENode & { deleted?: boolean }; + next?: BPENode & { deleted?: boolean }; + }; + if (node.deleted || !node.next || node.next.deleted) continue; + node.deleted = true; + node.next.deleted = true; + if (node.prev) { + const newPreviousNode = { ...(node.prev as BPENode) } as BPENode; + node.prev.deleted = true; + node.prev = newPreviousNode; + if (newPreviousNode.prev) { + (newPreviousNode.prev as BPENode).next = newPreviousNode; + } else { + startingNode = newPreviousNode; + } + } + const merged: BPENode = { + token: node.token + (node.next as BPENode).token, + bias: node.bias, + prev: node.prev, + next: (node.next as BPENode).next + }; + if (merged.prev) { + (merged.prev as BPENode).next = merged; + this.addNode(queue, merged.prev as BPENode); + } else { + startingNode = merged; + } + if (merged.next) { + (merged.next as BPENode).prev = merged; + this.addNode(queue, merged); + } + } + for ( + let currentNode: BPENode | undefined = startingNode; + currentNode !== undefined; + currentNode = currentNode.next + ) { + result.push(currentNode.token); + } + } else { + result = word; + } + if (this.continuingSubwordSuffix) { + for (let i = 0; i < result.length - 1; ++i) { + result[i] += this.continuingSubwordSuffix; + } + } + if (token.length < this.maxLengthToCache) { + this.cache.put(token, result); + } + return result; + } + private addNode(queue: PriorityQueue, node: BPENode) { + const rank = this.bpeRanks.get(JSON.stringify([node.token, (node.next as BPENode).token])); + if (rank !== undefined) { + node.score = rank + node.bias; + queue.push(node); + } + } + encode(tokens: string[]): string[] { + const outputTokens: string[] = []; + for (const token of tokens) { + if (this.ignoreMerges && this.tokensToIds.has(token)) { + outputTokens.push(token); + continue; + } + const bpeTokenList = this.bpe(token); + for (const t of bpeTokenList) { + if (this.tokensToIds.has(t)) { + outputTokens.push(t); + } else if (this.byteFallback) { + const byteTokens = Array.from(this.textEncoder.encode(t)).map( + (x) => `<0x${x.toString(16).toUpperCase().padStart(2, '0')}>` + ); + if (byteTokens.every((x) => this.tokensToIds.has(x))) { + outputTokens.push(...byteTokens); + } else { + outputTokens.push(this.unkToken as string); + } + } else { + outputTokens.push(this.unkToken as string); + } + } + } + return outputTokens; + } +} + +/** + * A base class for text normalization. + */ +abstract class Normalizer extends Callable<[string], string> { + config: TConfig; + /** + * @param config The configuration object for the normalizer. + */ + constructor(config: TConfig) { + super(); + this.config = config; + } + static fromConfig(config: TConfig): Normalizer { + switch (config.type) { + case 'Sequence': + return new NormalizerSequence(config); + case 'NFC': + return new NFC(config); + case 'NFD': + return new NFD(config); + case 'NFKC': + return new NFKC(config); + case 'NFKD': + return new NFKD(config); + case 'Strip': + return new StripNormalizer(config); + case 'Lowercase': + return new Lowercase(config); + case 'Prepend': + return new Prepend(config); + } + } + + normalize(_text: string): string { + throw Error('normalize should be implemented in subclass.'); + } + + protected call(...[text]: [string]): string { + return this.normalize(text); + } +} + +/** + * A normalizer that applies Unicode normalization to the input text. + */ +abstract class UnicodeNormalizer extends Normalizer { + form: 'NFC' | 'NFD' | 'NFKC' | 'NFKD' | undefined = undefined; + + /** + * Normalize the input text by applying Unicode normalization. + * @param text The input text to be normalized. + * @returns The normalized text. + */ + normalize(text: string) { + text = text.normalize(this.form as 'NFC'); + return text; + } +} + +/** + * A normalizer that applies Unicode normalization form C (NFC) to the input text. + * Canonical Decomposition, followed by Canonical Composition. + */ +class NFC extends UnicodeNormalizer { + form = 'NFC' as const; +} + +/** + * A normalizer that applies Unicode normalization form D (NFD) to the input text. + * Canonical Decomposition. + */ +class NFD extends UnicodeNormalizer { + form = 'NFD' as const; +} + +/** + * A normalizer that applies Unicode normalization form KC (NFKC) to the input text. + * Compatibility Decomposition, followed by Canonical Composition. + */ +class NFKC extends UnicodeNormalizer { + form = 'NFKC' as const; +} + +/** + * A normalizer that applies Unicode normalization form KD (NFKD) to the input text. + * Compatibility Decomposition. + */ +class NFKD extends UnicodeNormalizer { + form = 'NFKD' as const; +} + +/** + * A normalizer that strips leading and/or trailing whitespace from the input text. + */ +class StripNormalizer extends Normalizer { + /** + * Strip leading and/or trailing whitespace from the input text. + * @param text The input text. + * @returns The normalized text. + */ + normalize(text: string) { + const cfg = this.config; + if (cfg.stripLeft && cfg.stripRight) { + // Fast path to avoid an extra trim call + text = text.trim(); + } else { + if (cfg.stripLeft) { + text = text.trimStart(); + } + if (cfg.stripRight) { + text = text.trimEnd(); + } + } + return text; + } +} + +/** + * A Normalizer that lowercases the input string. + */ +class Lowercase extends Normalizer { + /** + * Lowercases the input string. + * @param text The text to normalize. + * @returns The normalized text. + */ + normalize(text: string) { + text = text.toLowerCase(); + return text; + } +} + +/** + * A Normalizer that prepends a string to the input string. + */ +class Prepend extends Normalizer { + /** + * Prepends the input string. + * @param text The text to normalize. + * @returns The normalized text. + */ + normalize(text: string) { + const cfg = this.config; + text = cfg.prepend + text; + return text; + } +} + +/** + * A Normalizer that applies a sequence of Normalizers. + */ + +class NormalizerSequence extends Normalizer { + normalizers: Normalizer[]; + + constructor(config: NormalizerSequenceConfig) { + super(config); + this.normalizers = config.normalizers.map((x) => Normalizer.fromConfig(x)); + } + /** + * Apply a sequence of Normalizers to the input text. + * @param text The text to normalize. + * @returns The normalized text. + */ + normalize(text: string) { + return this.normalizers.reduce((t, normalizer) => { + return normalizer.normalize(t); + }, text); + } +} + +/** + * A callable class representing a pre-tokenizer used in tokenization. Subclasses + * should implement the `preTokenizeText` method to define the specific pre-tokenization logic. + */ +abstract class PreTokenizer extends Callable< + [string | string[], PreTokenizeOptions | undefined], + string[] +> { + /** + * Factory method that returns an instance of a subclass of `PreTokenizer` based on the provided configuration. + * + * @static + * @param config A configuration object for the pre-tokenizer. + * @returns An instance of a subclass of `PreTokenizer`. + * @throws If the provided configuration object does not correspond to any known pre-tokenizer. + */ + static fromConfig(config: PreTokenizerConfig): PreTokenizer { + switch (config.type) { + case 'Sequence': + return new PreTokenizerSequence(config); + case 'Whitespace': + return new WhitespacePreTokenizer(); + case 'WhitespaceSplit': + return new WhitespaceSplit(); + case 'Metaspace': + return new MetaspacePreTokenizer(config); + case 'ByteLevel': + return new ByteLevelPreTokenizer(config); + default: + throw new Error('Unknown PreTokenizer type'); + } + } + + /** + * Method that should be implemented by subclasses to define the specific pre-tokenization logic. + * + * @param text The text to pre-tokenize. + * @param options Additional options for the pre-tokenization logic. + * @returns The pre-tokenized text. + * @throws {Error} If the method is not implemented in the subclass. + */ + abstract preTokenizeText(text: string, options?: PreTokenizeOptions): string[]; + + /** + * Tokenizes the given text into pre-tokens. + * @param text The text or array of texts to pre-tokenize. + * @param options Additional options for the pre-tokenization logic. + * @returns An array of pre-tokens. + */ + preTokenize(text: string | string[], options?: PreTokenizeOptions): string[] { + return ( + Array.isArray(text) + ? (text as string[]).map((x) => this.preTokenizeText(x, options)) + : this.preTokenizeText(text as string, options) + ).flat(); + } + + /** + * Alias for {@link PreTokenizer#preTokenize}. + * @param text The text or array of texts to pre-tokenize. + * @param options Additional options for the pre-tokenization logic. + * @returns An array of pre-tokens. + */ + protected call( + ...[text, options]: [string | string[], PreTokenizeOptions | undefined] + ): string[] { + return this.preTokenize(text, options); + } +} + +/** + * A pre-tokenizer that splits text into Byte-Pair-Encoding (BPE) subwords. + * @extends PreTokenizer + */ + +class ByteLevelPreTokenizer extends PreTokenizer { + config: ByteLevelPreTokenizerConfig; + addPrefixSpace!: boolean; + trimOffsets!: boolean; + useRegex!: boolean; + pattern!: RegExp; + byteEncoder!: Record; + textEncoder!: TextEncoder; + constructor(config: ByteLevelPreTokenizerConfig) { + super(); + this.config = config; + this.addPrefixSpace = this.config.addPrefixSpace; + this.trimOffsets = this.config.trimOffsets; + this.useRegex = this.config.useRegex ?? true; + this.pattern = /'s|'t|'re|'ve|'m|'ll|'d| ?\p{L}+| ?\p{N}+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+/gu; + this.byteEncoder = BYTES_TO_UNICODE as Record; + this.textEncoder = new TextEncoder(); + } + preTokenizeText(text: string, _options?: PreTokenizeOptions): string[] { + if (this.addPrefixSpace && !text.startsWith(' ')) { + text = ' ' + text; + } + const tokens = this.useRegex ? text.match(this.pattern) || [] : [text]; + return tokens.map((token) => + Array.from(this.textEncoder.encode(token), (byte) => this.byteEncoder[byte]).join('') + ); + } +} + +type PostProcessorArgs = [string[], (string[] | null | undefined)?, PostProcessorOptions?]; +abstract class PostProcessor extends Callable { + config: PostProcessorConfig; + constructor(config: PostProcessorConfig) { + super(); + this.config = config; + } + static fromConfig(config: PostProcessorConfig): PostProcessor { + switch (config.type) { + case 'ByteLevel': + return new ByteLevelPostProcessor(config); + case 'Sequence': + return new PostProcessorSequence(config); + default: + throw new Error('Unknown PostProcessor type'); + } + } + + abstract postProcess( + tokens: string[], + ...args: [string[] | null | undefined, PostProcessorOptions?] + ): PostProcessorResult; + + protected call( + ...[tokens, ...args]: [string[], (string[] | null | undefined)?, PostProcessorOptions?] + ): PostProcessorResult { + return this.postProcess(tokens, ...args); + } +} + +/** + * A PostProcessor that returns the given tokens as is. + */ +class ByteLevelPostProcessor extends PostProcessor { + postProcess(tokens: string[], tokensPair: string[] | null = null): { tokens: string[] } { + if (tokensPair) { + tokens = mergeArrays(tokens, tokensPair); + } + return { tokens }; + } +} + +/** + * A post-processor that applies multiple post-processors in sequence. + */ +class PostProcessorSequence extends PostProcessor { + processors: PostProcessor[]; + constructor(config: PostProcessorSequenceConfig) { + super(config); + this.processors = config.processors.map((x) => PostProcessor.fromConfig(x)); + } + postProcess( + tokens: string[], + tokensPair: string[] | null = null, + options: PostProcessorOptions = {} + ): { tokens: string[]; tokenTypeIds?: number[] } { + let tokenTypeIds: number[] | undefined; + for (const processor of this.processors) { + if (processor instanceof ByteLevelPostProcessor) { + const output = processor.postProcess(tokens); + tokens = output.tokens; + if (tokensPair) { + const pairOutput = processor.postProcess(tokensPair); + tokensPair = pairOutput.tokens; + } + } else { + const output = processor.postProcess(tokens, tokensPair ?? null, options); + tokens = output.tokens; + if (output.tokenTypeIds) { + tokenTypeIds = output.tokenTypeIds; + } + } + } + return { tokens, tokenTypeIds: tokenTypeIds }; + } +} + +/** + * The base class for token decoders. + */ +abstract class Decoder extends Callable< + [string[]], + string +> { + config: TConfig; + addedTokens: AddedToken[]; + endOfWordSuffix?: string; + constructor(config: TConfig) { + super(); + this.config = config; + this.addedTokens = []; + this.endOfWordSuffix = undefined; + } + static fromConfig(config: DecoderConfig): Decoder { + switch (config.type) { + case 'ByteLevel': + return new ByteLevelDecoder(config); + case 'ByteFallback': + return new ByteFallback(config); + case 'Fuse': + return new FuseDecoder(config); + case 'Strip': + return new StripDecoder(config); + case 'Sequence': + return new DecoderSequence(config); + case 'BPEDecoder': + return new BPEDecoder(config); + default: + throw new Error('Unknown Decoder type'); + } + } + protected call(...[tokens]: [string[]]): string { + return this.decode(tokens); + } + decode(tokens: string[]): string { + return this.decodeChain(tokens).join(''); + } + abstract decodeChain(tokens: string[]): string[]; +} + +class ByteFallback extends Decoder { + textDecoder!: TextDecoder; + constructor(config: ByteFallbackConfig) { + super(config); + this.textDecoder = new TextDecoder(); + } + decodeChain(tokens: string[]): string[] { + const newTokens: string[] = []; + let previousByteTokens: number[] = []; + for (const token of tokens) { + let bytes: number | null = null; + if (token.length === 6 && token.startsWith('<0x') && token.endsWith('>')) { + const byte = parseInt(token.slice(3, 5), 16); + if (!isNaN(byte)) { + bytes = byte; + } + } + if (bytes !== null) { + previousByteTokens.push(bytes); + } else { + if (previousByteTokens.length > 0) { + const string = this.textDecoder.decode(Uint8Array.from(previousByteTokens)); + newTokens.push(string); + previousByteTokens = []; + } + newTokens.push(token); + } + } + if (previousByteTokens.length > 0) { + const string = this.textDecoder.decode(Uint8Array.from(previousByteTokens)); + newTokens.push(string); + previousByteTokens = []; + } + return newTokens; + } +} + +/** + * Fuse simply fuses all tokens into one big string. + * It's usually the last decoding step anyway, but this decoder + * exists incase some decoders need to happen after that step + */ +class FuseDecoder extends Decoder { + /** @type {Decoder['decodeChain']} */ + decodeChain(tokens: string[]): string[] { + return [tokens.join('')]; + } +} + +class StripDecoder extends Decoder { + content!: string; + start!: number; + stop!: number; + constructor(config: StripDecoderConfig) { + super(config); + const cfg = this.config; + this.content = cfg.content; + this.start = cfg.start; + this.stop = cfg.stop; + } + /** @type {Decoder['decodeChain']} */ + decodeChain(tokens: string[]): string[] { + return tokens.map((token) => { + let startCut = 0; + for (let i = 0; i < this.start; ++i) { + if (token[i] === this.content) { + startCut = i + 1; + continue; + } else { + break; + } + } + let stopCut = token.length; + for (let i = 0; i < this.stop; ++i) { + const index = token.length - i - 1; + if (token[index] === this.content) { + stopCut = index; + continue; + } else { + break; + } + } + return token.slice(startCut, stopCut); + }); + } +} + +/** + * Byte-level decoder for tokenization output. Inherits from the `Decoder` class. + * @extends Decoder + */ +class ByteLevelDecoder extends Decoder { + byteDecoder!: Record; + textDecoder!: TextDecoder; + constructor(config: ByteLevelDecoderConfig) { + super(config); + this.byteDecoder = UNICODE_TO_BYTES as unknown as Record; + this.textDecoder = new TextDecoder('utf-8', { fatal: false, ignoreBOM: true }); + this.endOfWordSuffix = undefined; + } + convertTokensToString(tokens: string[]): string { + const text = tokens.join(''); + const byteArray = new Uint8Array([...text].map((c) => this.byteDecoder[c])); + const decodedText = this.textDecoder.decode(byteArray); + return decodedText; + } + /** @type {Decoder['decodeChain']} */ + decodeChain(tokens: string[]): string[] { + const subTexts: string[] = []; + let currentSubText: string[] = []; + for (const token of tokens) { + if (this.addedTokens.find((x) => x.content === token) !== undefined) { + if (currentSubText.length > 0) { + subTexts.push(this.convertTokensToString(currentSubText)); + currentSubText = []; + } + subTexts.push(token); + } else { + currentSubText.push(token); + } + } + if (currentSubText.length > 0) { + subTexts.push(this.convertTokensToString(currentSubText)); + } + return subTexts; + } +} + +/** + * Apply a sequence of decoders. + * @extends Decoder + */ +class DecoderSequence extends Decoder { + decoders!: Decoder[]; + constructor(config: DecoderSequenceConfig) { + super(config); + this.decoders = config.decoders.map((x) => Decoder.fromConfig(x)); + } + /** @type {Decoder['decodeChain']} */ + decodeChain(tokens: string[]): string[] { + return this.decoders.reduce((toks: string[], decoder: Decoder) => { + return decoder.decodeChain(toks); + }, tokens); + } +} + +class BPEDecoder extends Decoder { + suffix!: string; + constructor(config: BPEDecoderConfig) { + super(config); + const cfg = this.config; + this.suffix = cfg.suffix; + } + /** @type {Decoder['decodeChain']} */ + decodeChain(tokens: string[]): string[] { + return tokens.map((token, i) => { + return token.replaceAll(this.suffix, i === tokens.length - 1 ? '' : ' '); + }); + } +} + +/** + * This PreTokenizer replaces spaces with the given replacement character, adds a prefix space if requested, + * and returns a list of tokens. + * @extends PreTokenizer + */ +class MetaspacePreTokenizer extends PreTokenizer { + addPrefixSpace: boolean; + replacement: string; + strRep: string; + prependScheme: 'first' | 'never' | 'always'; + /** + * @param {Object} config The configuration object for the MetaspacePreTokenizer. + * @param {boolean} config.addPrefixSpace Whether to add a prefix space to the first token. + * @param {string} config.replacement The character to replace spaces with. + * @param {string} [config.strRep=config.replacement] An optional string representation of the replacement character. + * @param {'first'|'never'|'always'} [config.prependScheme='always'] The metaspace prepending scheme. + */ + constructor(config: MetaspacePreTokenizerConfig) { + super(); + + this.addPrefixSpace = config.addPrefixSpace; + this.replacement = config.replacement; + this.strRep = config.strRep || this.replacement; + this.prependScheme = config.prependScheme ?? 'always'; + } + + /** + * This method takes a string, replaces spaces with the replacement character, + * adds a prefix space if requested, and returns a new list of tokens. + * @param text The text to pre-tokenize. + * @param options The options for the pre-tokenization. + * @param options.sectionIndex The index of the section to pre-tokenize. + * @returns A new list of pre-tokenized tokens. + */ + preTokenizeText( + text: string, + { sectionIndex: sectionIndex = undefined }: PreTokenizeOptions = {} + ) { + let normalized = text.replaceAll(' ', this.strRep); + + if ( + // We add a prefix space if: + // (1) The addPrefixSpace option is enabled and the normalized token does not already start + // with the replacement character. + this.addPrefixSpace && + !normalized.startsWith(this.replacement) && + // and (2) either: + // (a) prependScheme is 'always' + // (b) prependScheme is 'first' and this is the first section + (this.prependScheme === 'always' || (this.prependScheme === 'first' && sectionIndex === 0)) + ) { + normalized = this.strRep + normalized; + } + return [normalized]; + } +} + +/** + * A pre-tokenizer that applies a sequence of pre-tokenizers to the input text. + * @extends PreTokenizer + */ +class PreTokenizerSequence extends PreTokenizer { + tokenizers: PreTokenizer[]; + /** + * Creates an instance of PreTokenizerSequence. + * @param {Object} config The configuration object for the pre-tokenizer sequence. + * @param {Object[]} config.pretokenizers An array of pre-tokenizer configurations. + */ + constructor(config: PreTokenizerSequenceConfig) { + super(); + this.tokenizers = config.pretokenizers.map((x) => PreTokenizer.fromConfig(x)); + } + + /** + * Applies each pre-tokenizer in the sequence to the input text in turn. + * @param text The text to pre-tokenize. + * @param options Additional options for the pre-tokenization logic. + * @returns The pre-tokenized text. + */ + preTokenizeText(text: string, options: PreTokenizeOptions) { + // Use reduce to apply each tokenizer to the text + return this.tokenizers.reduce( + (preTokenizedText, tokenizer) => { + return tokenizer.preTokenize(preTokenizedText, options); + }, + [text] + ); + } +} + +/** + * Splits on word boundaries (using the following regular expression: `\w+|[^\w\s]+`). + */ +class WhitespacePreTokenizer extends PreTokenizer { + /** + * Creates an instance of WhitespacePreTokenizer. + * @param config The configuration object for the pre-tokenizer. + */ + constructor() { + super(); + } + /** + * Pre-tokenizes the input text by splitting it on word boundaries. + * @param text The text to be pre-tokenized. + * @param options Additional options for the pre-tokenization logic. + * @returns An array of tokens produced by splitting the input text on whitespace. + */ + preTokenizeText(text: string, _options: unknown) { + return text.match(/\w+|[^\w\s]+/g) || []; + } +} + +/** + * Splits a string of text by whitespace characters into individual tokens. + * @extends PreTokenizer + */ +class WhitespaceSplit extends PreTokenizer { + /** + * Creates an instance of WhitespaceSplit. + * @param config The configuration object for the pre-tokenizer. + */ + constructor() { + super(); + } + /** + * Pre-tokenizes the input text by splitting it on whitespace characters. + * @param text The text to be pre-tokenized. + * @param options Additional options for the pre-tokenization logic. + * @returns An array of tokens produced by splitting the input text on whitespace. + */ + preTokenizeText(text: string, _options: unknown) { + return whitespaceSplit(text); + } +} + +const SPECIAL_TOKEN_ATTRIBUTES = [ + 'bos_token', + 'eos_token', + 'unk_token', + 'sep_token', + 'pad_token', + 'cls_token', + 'mask_token' + // additional_special_tokens (TODO) +]; + +/** + * + * Helper function for padding values of an object, which are each arrays. + * NOTE: No additional checks are made here for validity of arguments. + * @param item The input object. + * @param length The length to pad to. + * @param valueFn Determine the value to fill the array, based on its key. + * @param side Which side to pad the array. + */ +function padHelper( + item: Record, + length: number, + valueFn: (key: string) => T, + side: 'right' | 'left' +) { + for (const key of Object.keys(item)) { + const diff = length - item[key].length; + const value = valueFn(key); + + const padData = new Array(diff).fill(value); + item[key] = + side === 'right' ? mergeArrays(item[key], padData) : mergeArrays(padData, item[key]); + } +} + +/** + * Helper function for truncating values of an object, which are each arrays. + * NOTE: No additional checks are made here for validity of arguments. + * @param item The input object. + * @param length The length to truncate to. + */ +function truncateHelper(item: Record, length: number) { + // Setting .length to a lower value truncates the array in-place: + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/length + for (const key of Object.keys(item)) { + item[key].length = length; + } +} + +interface DecodeArgs { + skipSpecialTokens?: boolean; + cleanUpTokenizationSpaces?: boolean; +} + +type BatchEncodingItem = number[] | number[][] | Tensor; + +interface BatchEncoding { + inputIds: BatchEncodingItem; + attentionMask: BatchEncodingItem; + tokenTypeIds?: BatchEncodingItem; +} + +interface Message { + role: string; + content: string; +} + +export class PreTrainedTokenizer extends Callable< + [ + string | string[], + { + textPair?: string | null; + addSpecialTokens?: boolean; + padding?: boolean | 'max_length'; + truncation?: boolean | null; + maxLength?: number | null; + returnTensor?: boolean; + returnTokenTypeIds?: boolean | null; + }? + ], + BatchEncoding +> { + config: TokenizerConfig; + normalizer!: ((text: string) => string) | Normalizer | null; + preTokenizer!: ((text: string, options?: PreTokenizeOptions) => string[]) | PreTokenizer | null; + model!: TokenizerModel; + postProcessor!: + | (( + tokens: string[], + tokensPair?: string[] | null, + options?: PostProcessorOptions + ) => PostProcessorResult) + | PostProcessor + | null; + decoder!: ((tokens: string[]) => string) | Decoder | null; + specialTokens: string[]; + allSpecialIds: number[]; + addedTokens: AddedToken[]; + additionalSpecialTokens: string[]; + addedTokensSplitter: DictionarySplitter; + addedTokensMap: Map; + maskToken?: string | null; + maskTokenId?: number; + padToken?: string | null; + padTokenId?: number; + sepToken?: string | null; + sepTokenId?: number; + unkToken?: string | null; + unkTokenId?: number; + bosToken?: string | null; + bosTokenId?: number; + eosToken?: string | null; + eosTokenId?: number; + modelMaxLength!: number; + removeSpace!: boolean; + cleanUpTokenizationSpaces!: boolean; + paddingSide: 'left' | 'right' = 'right'; + addBoxToken?: boolean; + addEosToken?: boolean; + chatTemplate: null | Record | Array<{ name: string; template: string }>; + returnTokenTypeIds = false; + private compiledTemplateCache: Map; + constructor(tokenizerJSON: TokenizerJSON, tokenizerConfig: TokenizerConfig) { + super(); + this.config = tokenizerConfig; + this.normalizer = tokenizerJSON.normalizer + ? Normalizer.fromConfig(tokenizerJSON.normalizer) + : null; + this.preTokenizer = tokenizerJSON.preTokenizer + ? PreTokenizer.fromConfig(tokenizerJSON.preTokenizer) + : null; + this.model = TokenizerModel.fromConfig(tokenizerJSON.model, tokenizerConfig); + this.postProcessor = tokenizerJSON.postProcessor + ? PostProcessor.fromConfig(tokenizerJSON.postProcessor) + : null; + this.decoder = tokenizerJSON.decoder ? Decoder.fromConfig(tokenizerJSON.decoder) : null; + this.specialTokens = []; + this.allSpecialIds = []; + this.addedTokens = []; + for (const addedToken of tokenizerJSON.addedTokens) { + const token = new AddedToken(addedToken); + this.addedTokens.push(token); + this.model.tokensToIds.set(token.content, token.id); + this.model.vocab[token.id] = token.content; + if (token.special) { + this.specialTokens.push(token.content); + this.allSpecialIds.push(token.id); + } + } + this.additionalSpecialTokens = tokenizerConfig.additionalSpecialTokens ?? []; + this.specialTokens.push(...this.additionalSpecialTokens); + this.specialTokens = [...new Set(this.specialTokens)]; + if (this.decoder) { + (this.decoder as Decoder).addedTokens = this.addedTokens; + (this.decoder as Decoder).endOfWordSuffix = this.model.endOfWordSuffix; + } + this.addedTokensSplitter = new DictionarySplitter(this.addedTokens.map((x) => x.content)); + this.addedTokensMap = new Map(this.addedTokens.map((x) => [x.content, x])); + this.maskToken = this.getToken('mask_token'); + this.maskTokenId = this.model.tokensToIds.get(this.maskToken as string); + this.padToken = this.getToken('pad_token', 'eos_token'); + this.padTokenId = this.model.tokensToIds.get(this.padToken as string); + this.sepToken = this.getToken('sep_token'); + this.sepTokenId = this.model.tokensToIds.get(this.sepToken as string); + this.unkToken = this.getToken('unk_token'); + this.unkTokenId = this.model.tokensToIds.get(this.unkToken as string); + this.bosToken = this.getToken('bos_token'); + this.bosTokenId = this.model.tokensToIds.get(this.bosToken as string); + this.eosToken = this.getToken('eos_token'); + this.eosTokenId = this.model.tokensToIds.get(this.eosToken as string); + this.modelMaxLength = tokenizerConfig.modelMaxLength as number; + this.removeSpace = tokenizerConfig.removeSpace as boolean; + this.cleanUpTokenizationSpaces = (tokenizerConfig.cleanUpTokenizationSpaces ?? true) as boolean; + if (tokenizerConfig.paddingSide) { + this.paddingSide = tokenizerConfig.paddingSide as 'left' | 'right'; + } + this.addBoxToken = tokenizerConfig.addBosToken as boolean; + this.addEosToken = tokenizerConfig.addEosToken as boolean; + this.chatTemplate = tokenizerConfig.chatTemplate ?? null; + if (Array.isArray(this.chatTemplate)) { + const chatTemplate: Record = Object.create(null); + for (const { name, template } of this.chatTemplate) { + if (typeof name !== 'string' || typeof template !== 'string') { + throw new Error( + 'Chat template must be a list of objects with "name" and "template" properties' + ); + } + chatTemplate[name] = template; + } + this.chatTemplate = chatTemplate; + } + this.compiledTemplateCache = new Map(); + } + getToken(...keys: string[]): string | null { + for (const key of keys) { + const item = this.config[key]; + if (!item) continue; + if (typeof item === 'object') { + const maybe = item as { type?: string; content?: string }; + if (maybe.type === 'AddedToken' && typeof maybe.content === 'string') { + return maybe.content; + } + throw Error(`Unknown token: ${String(item)}`); + } else { + return item as string; + } + } + return null; + } + + static async fromPretrained(tokenizerName: string): Promise { + const info = await loadTokenizer(tokenizerName); + return new this(...info); + } + + /** + * Encode/tokenize the given text(s). + * @param text The text to tokenize. + * @param options An optional object containing the following properties: + * @param options.textPair A second sequence to be encoded with the first. + * @param options.padding Whether to pad the input sequences. + * @param options.addSpecialTokens Whether or not to add the special tokens associated with the corresponding model. + * @param options.truncation Whether to truncate the input sequences. + * @param options.maxLength Maximum length of the returned list and optionally padding length. + * @param options.returnTensor Whether to return the results as Tensors or arrays. + * @param options.returnTokenTypeIds Whether to return the token type ids. + * @returns Object to be passed to the model. + */ + protected call( + text: string | string[], + { + textPair = null, + addSpecialTokens = true, + padding = false, + truncation = null, + maxLength = null, + returnTensor = true, + returnTokenTypeIds = null + }: { + textPair?: string | null; + addSpecialTokens?: boolean; + padding?: boolean | 'max_length'; + truncation?: boolean | null; + maxLength?: number | null; + returnTensor?: boolean; + returnTokenTypeIds?: boolean | null; + } = {} + ): BatchEncoding { + const isBatched = Array.isArray(text); + + let encodedTokens; + + if (isBatched) { + if (text.length === 0) { + throw Error('text array must be non-empty'); + } + encodedTokens = text.map((x) => + this.encodePlus(x, { + addSpecialTokens: addSpecialTokens, + returnTokenTypeIds: returnTokenTypeIds + }) + ); + } else { + if (text === null || text === undefined) { + throw Error('text may not be null or undefined'); + } + + if (Array.isArray(textPair)) { + throw Error( + 'When specifying `textPair`, since `text` is a string, `textPair` must also be a string (i.e., not an array).' + ); + } + + // For single input, we just wrap in an array, and then unwrap later. + encodedTokens = [ + this.encodePlus(text, { + addSpecialTokens: addSpecialTokens, + returnTokenTypeIds: returnTokenTypeIds + }) + ]; + } + // At this point, `encodedTokens` is batched, of shape [batchSize, tokens]. + // However, array may be jagged. So, we may need pad to maxLength. + if (maxLength === null) { + maxLength = this.modelMaxLength; + } else if (truncation === null) { + if (padding === true) { + console.warn( + '`maxLength` is ignored when `padding: true` and there is no truncation strategy. ' + + "To pad to max length, use `padding: 'maxLength'`." + ); + maxLength = this.modelMaxLength; + } else if (padding === false) { + console.warn( + 'Truncation was not explicitly activated but `maxLength` is provided a specific value, please use `truncation: true` to explicitly truncate examples to max length.' + ); + truncation = true; + } + } + + // padding: 'maxLength' doesn't require any additional calculation + // but padding: true has to calculate maxLength from the sequences + if (padding === true) { + maxLength = Math.min( + max(encodedTokens.map((x) => x.inputIds.length))[0], + maxLength ?? Infinity + ); + } + + // Ensure it is less than model max length + maxLength = Math.min(maxLength, this.modelMaxLength ?? Infinity); + + if (padding || truncation) { + // Perform padding and/or truncation + for (let i = 0; i < encodedTokens.length; ++i) { + if (encodedTokens[i].inputIds.length === maxLength) { + continue; + } else if (encodedTokens[i].inputIds.length > maxLength) { + // possibly truncate + if (truncation) { + truncateHelper(encodedTokens[i], maxLength); + } + } else { + // t.length < maxLength + // possibly pad + if (padding) { + padHelper( + encodedTokens[i], + maxLength, + (key) => (key === 'inputIds' ? this.padTokenId : 0), + this.paddingSide + ); + } + } + } + } + + const result: Record = {}; + + if (returnTensor) { + if (!(padding && truncation)) { + // Not, guaranteed that all items have same length, so + // we perform additional check + + if ( + encodedTokens.some((x) => { + for (const key of Object.keys(x)) { + if ( + (x as Record)[key].length !== + (encodedTokens[0] as Record)[key]?.length + ) { + return true; + } + } + return false; + }) + ) { + throw Error( + 'Unable to create tensor, you should probably activate truncation and/or padding ' + + "with 'padding=true' and 'truncation=true' to have batched tensors with the same length." + ); + } + } + + // Now we actually convert to tensor + // NOTE: In the same way as the python library, we return a batched tensor, regardless of + // whether we have a single input or multiple inputs. + const dims = [encodedTokens.length, encodedTokens[0].inputIds.length]; + + for (const key of Object.keys(encodedTokens[0])) { + result[key] = tensor( + Int32Array.from( + encodedTokens + .flatMap( + (x) => + (x as Record)[key] as (bigint | boolean | number | string)[] + ) + .map(Number) + ), + { shape: dims, dtype: int32 } + ); + } + } else { + for (const key of Object.keys(encodedTokens[0])) { + result[key] = encodedTokens.map((x) => (x as Record)[key]); + } + + // If not returning a tensor, we match the input type + if (!isBatched) { + // Input was not batched, so we unwrap + for (const key of Object.keys(result)) { + result[key] = (result[key] as unknown[])[0]; + } + } + } + + return result as unknown as BatchEncoding; + } + + /** + * Encodes a single text using the preprocessor pipeline of the tokenizer. + * + * @param {string|null} text The text to encode. + * @returns {string[]|null} The encoded tokens. + */ + private encodeText(text: string | null): string[] | null { + if (text === null) return null; + + // Actual function which does encoding, for a single text + // First, we take care of special tokens. Needed to avoid issues arising from + // normalization and/or pretokenization (which may not preserve special tokens) + const sections = this.addedTokensSplitter.split(text); + + // Process left/right stripping of added tokens + for (let i = 0; i < sections.length; ++i) { + const addedToken = this.addedTokensMap.get(sections[i]); + if (addedToken) { + if (addedToken.lstrip && i > 0) { + sections[i - 1] = sections[i - 1].trimEnd(); + } + if (addedToken.rstrip && i < sections.length - 1) { + sections[i + 1] = sections[i + 1].trimStart(); + } + } + } + + const tokens = sections.flatMap((x, sectionIndex) => { + if (x.length === 0) return []; + if (this.addedTokensMap.has(x)) return [x]; // Return added tokens unchanged + + if (this.removeSpace === true) { + x = x.trim().split(/\s+/).join(' '); + } + + if (this.normalizer !== null) { + x = this.normalizer(x); + } + + // If, after normalization, this section is empty (e.g., trimming whitespace), + // we return an empty array + if (x.length === 0) { + return []; + } + + const sectionTokens = + this.preTokenizer !== null + ? this.preTokenizer(x, { + sectionIndex: sectionIndex + }) + : [x]; + + const tokens = this.model(sectionTokens); + + return tokens; + }); + + return tokens; + } + + /** + * Encodes a single text or a pair of texts using the model's tokenizer. + * + * @param text The text to encode. + * @param options An optional object containing the following properties: + * @param options.textPair The optional second text to encode. + * @param options.addSpecialTokens Whether or not to add the special tokens associated with the corresponding model. + * @param options.returnTokenTypeIds Whether to return tokenTypeIds. + * @returns An object containing the encoded text. + */ + private encodePlus( + text: string, + { + textPair = null, + addSpecialTokens = true, + returnTokenTypeIds = null + }: { + textPair?: string | null; + addSpecialTokens?: boolean; + returnTokenTypeIds?: boolean | null; + } = {} + ) { + const { tokens, tokenTypeIds } = this.tokenizeHelper(text, { + pair: textPair, + addSpecialTokens + }); + + const inputIds = this.model.convertTokensToIds(tokens); + + const result = { + inputIds: inputIds, + attentionMask: new Array(inputIds.length).fill(1) + }; + if ((returnTokenTypeIds ?? this.returnTokenTypeIds) && tokenTypeIds) { + (result as { tokenTypeIds?: number[] }).tokenTypeIds = tokenTypeIds; + } + return result; + } + + /** + * Internal helper function to tokenize a text, and optionally a pair of texts. + * @param text The text to tokenize. + * @param options An optional object containing the following properties: + * @param options.pair The optional second text to tokenize. + * @param options.addSpecialTokens Whether or not to add the special tokens associated with the corresponding model. + * @returns An object containing the tokens and optionally the token type IDs. + */ + private tokenizeHelper( + text: string, + { + pair = null, + addSpecialTokens = false + }: { pair?: string | null; addSpecialTokens?: boolean } = {} + ) { + const tokens = this.encodeText(text); + const tokens2 = this.encodeText(pair); + + return this.postProcessor + ? this.postProcessor(tokens ?? [], tokens2 ?? null, { addSpecialTokens }) + : { tokens: mergeArrays(tokens ?? [], tokens2 ?? []) }; + } + + /** + * Converts a string into a sequence of tokens. + * @param text The sequence to be encoded. + * @param options An optional object containing the following properties: + * @param options.pair A second sequence to be encoded with the first. + * @param options.addSpecialTokens Whether or not to add the special tokens associated with the corresponding model. + * @returns The list of tokens. + */ + tokenize(text: string, { pair = null, addSpecialTokens = false } = {}) { + return this.tokenizeHelper(text, { pair, addSpecialTokens }).tokens; + } + + /** + * Encodes a single text or a pair of texts using the model's tokenizer. + * + * @param text The text to encode. + * @param options An optional object containing the following properties: + * @param options.addSpecialTokens Whether or not to add the special tokens associated with the corresponding model. + * @param options.returnTokenTypeIds Whether to return tokenTypeIds. + * @returns An array of token IDs representing the encoded text(s). + */ + encode(text: string, { addSpecialTokens = true, returnTokenTypeIds = null } = {}) { + return this.encodePlus(text, { + addSpecialTokens, + returnTokenTypeIds + }).inputIds; + } + + /** + * Decode a batch of tokenized sequences. + * @param batch List of tokenized input sequences. + * @param decodeArgs (Optional) Object with decoding arguments. + * @returns List of decoded sequences. + */ + batchDecode(batch: number[][], decodeArgs: DecodeArgs = {}) { + return batch.map((x) => this.decode(x, decodeArgs)); + } + + /** + * Decodes a sequence of token IDs back to a string. + * + * @param tokenIds List of token IDs to decode. + * @param decodeArgs (Optional) Object with decoding arguments. + * + * @returns The decoded string. + * @throws If `tokenIds` is not a non-empty array of integers. + */ + decode(tokenIds: number[], decodeArgs: DecodeArgs = {}) { + if ( + !Array.isArray(tokenIds) || + tokenIds.length === 0 || + !(Number.isInteger(tokenIds[0]) || typeof tokenIds[0] === 'bigint') + ) { + throw Error('tokenIds must be a non-empty array of integers.'); + } + + return this.decodeSingle(tokenIds, decodeArgs); + } + + /** + * Decode a single list of token ids to a string. + * @param tokenIds List of token ids to decode + * @param decodeArgs Optional arguments for decoding + * @param [decodeArgs.skipSpecialTokens=false] Whether to skip special tokens during decoding + * @param [decodeArgs.cleanUpTokenizationSpaces=null] Whether to clean up tokenization spaces + * during decoding. If null, the value is set to `this.decoder.cleanup` if it exists, falling + * back to `this.cleanUpTokenizationSpaces` if it exists, falling back to `true`. + * @returns The decoded string + */ + decodeSingle(tokenIds: number[], { skipSpecialTokens = false }: DecodeArgs = {}) { + let tokens = this.model.convertIdsToTokens(tokenIds); + if (skipSpecialTokens) { + tokens = tokens.filter((x) => !this.specialTokens.includes(x)); + } + + // If `this.decoder` is null, we just join tokens with a space: + // https://github.com/huggingface/tokenizers/blob/8edec536a737cb04494b454805be16c020abb14f/tokenizers/src/tokenizer/mod.rs#L835 + let decoded = this.decoder ? this.decoder(tokens) : tokens.join(' '); + + // Slight hack, but prevents having to pass `skipSpecialTokens` to each call to `decode`, which + // would lead to code duplication. + if (this.decoder && 'endOfWordSuffix' in this.decoder && this.decoder.endOfWordSuffix) { + decoded = decoded.replaceAll(this.decoder.endOfWordSuffix, ' '); + if (skipSpecialTokens) { + decoded = decoded.trim(); + } + } + + return decoded; + } + + /** + * Retrieve the chat template string used for tokenizing chat messages. This template is used + * internally by the `applyChatTemplate` method and can also be used externally to retrieve the + * model's chat template for better generation tracking. + * + * @param options An optional object containing the following properties: + * @param options.chatTemplate A Jinja template or the name of a template to use for this + * conversion. It is usually not necessary to pass anything to this argument, as the model's + * template will be used by default. + * @param options.tools A list of tools (callable functions) that will be accessible to the model. + * If the template does not support function calling, this argument will have no effect. Each + * tool should be passed as a JSON Schema, giving the name, description and argument types for + * the tool. See our + * [chat templating guide](https://huggingface.co/docs/transformers/main/en/chat_templating#automated-function-conversion-for-tool-use) + * for more information. + * @returns The chat template string. + */ + getChatTemplate({ + chatTemplate = null, + tools = null + }: { chatTemplate?: string | null; tools?: string[] | null } = {}): string { + // First, handle the cases when the model has a dict of multiple templates + if (this.chatTemplate && typeof this.chatTemplate === 'object') { + const templateDict = this.chatTemplate; + + if (chatTemplate !== null && Object.hasOwn(templateDict, chatTemplate)) { + // The user can pass the name of a template to the chat template argument instead of an + // entire template + chatTemplate = (templateDict as Record)[chatTemplate]; + } else if (chatTemplate === null) { + if (tools !== null && 'toolUse' in templateDict) { + chatTemplate = templateDict['toolUse']; + } else if ('default' in templateDict) { + chatTemplate = templateDict['default']; + } else { + throw Error( + `This model has multiple chat templates with no default specified! Please either pass` + + ` a chat template or the name of the template you wish to use to the 'chatTemplate'` + + ` argument. Available template names are ${Object.keys(templateDict).sort()}.` + ); + } + } + } else if (chatTemplate === null) { + // These are the cases when the model has a single template + // priority: `chatTemplate` argument > `tokenizer.chatTemplate` + if (this.chatTemplate) { + chatTemplate = this.chatTemplate; + } else { + throw Error( + 'Cannot use applyChatTemplate() because tokenizer.chatTemplate is not set and no template ' + + 'argument was passed! For information about writing templates and setting the ' + + 'tokenizer.chatTemplate attribute, please see the documentation at ' + + 'https://huggingface.co/docs/transformers/main/en/chat_templating' + ); + } + } + return chatTemplate; + } + + /** + * Converts a list of message objects with `"role"` and `"content"` keys to a list of token + * ids. This method is intended for use with chat models, and will read the tokenizer's chat_template attribute to + * determine the format and control tokens to use when converting. + * + * See [here](https://huggingface.co/docs/transformers/chat_templating) for more information. + * + * @param conversation A list of message objects with `"role"` and `"content"` keys, + * representing the chat history so far. + * @param options An optional object containing the following properties: + * @param options.chatTemplate A Jinja template to use for this conversion. If + * this is not passed, the model's chat template will be used instead. + * @param options.tools A list of tools (callable functions) that will be accessible to the model. + * If the template does not support function calling, this argument will have no effect. Each + * tool should be passed as a JSON Schema, giving the name, description and argument types for + * the tool. See our + * [chat templating guide](https://huggingface.co/docs/transformers/main/en/chat_templating#automated-function-conversion-for-tool-use) + * for more information. + * @param options.documents A list of dicts representing documents that will be accessible to the model if it is performing RAG + * (retrieval-augmented generation). If the template does not support RAG, this argument will have no + * effect. We recommend that each document should be a dict containing "title" and "text" keys. Please + * see the RAG section of the [chat templating guide](https://huggingface.co/docs/transformers/main/en/chat_templating#arguments-for-RAG) + * for examples of passing documents with chat templates. + * @param options.addGenerationPrompt Whether to end the prompt with the token(s) that indicate + * the start of an assistant message. This is useful when you want to generate a response from the + * model. Note that this argument will be passed to the chat template, and so it must be supported + * in the template for this argument to have any effect. + * @param options.tokenize Whether to tokenize the output. If false, the output will be a string. + * @param options.padding Whether to pad sequences to the maximum length. Has no effect if tokenize is false. + * @param options.truncation Whether to truncate sequences to the maximum length. Has no effect if tokenize is false. + * @param options.maxLength Maximum length (in tokens) to use for padding or truncation. Has no effect if tokenize is false. + * If not specified, the tokenizer's `max_length` attribute will be used as a default. + * @param options.returnTensor Whether to return the output as a Tensor or an Array. Has no effect if tokenize is false. + * @param options.returnDict Whether to return a dictionary with named outputs. Has no effect if tokenize is false. + * @param options.tokenizerKwargs Additional options to pass to the tokenizer. + * @returns The tokenized output. + */ + applyChatTemplate( + conversation: Message[], + { + tools = null, + documents = null, + chatTemplate = null, + addGenerationPrompt = false, + tokenize = true, + padding = false, + truncation = false, + maxLength = null, + returnTensor = true, + returnDict = false, + tokenizerKwargs = {}, + ...kwargs + }: { + tools?: string[] | null; + documents?: string[] | null; + chatTemplate?: string | null; + addGenerationPrompt?: boolean; + tokenize?: boolean; + padding?: boolean; + truncation?: boolean; + maxLength?: number | null; + returnTensor?: boolean; + returnDict?: boolean; + tokenizerKwargs?: Record; + } = {} + ) { + chatTemplate = this.getChatTemplate({ chatTemplate, tools }); + + if (typeof chatTemplate !== 'string') { + throw Error(`chat_template must be a string, but got ${typeof chatTemplate}`); + } + + // Compilation function uses a cache to avoid recompiling the same template + let compiledTemplate = this.compiledTemplateCache.get(chatTemplate); + if (compiledTemplate === undefined) { + compiledTemplate = new Template(chatTemplate); + this.compiledTemplateCache.set(chatTemplate, compiledTemplate); + } + + const specialTokensMap = Object.create(null); + for (const key of SPECIAL_TOKEN_ATTRIBUTES) { + const value = this.getToken(key); + if (value) { + specialTokensMap[key] = value; + } + } + + const rendered = compiledTemplate.render({ + messages: conversation, + addGenerationPrompt, + tools, + documents, + ...specialTokensMap, + ...kwargs + }); + + if (tokenize) { + const out = this.call(rendered, { + addSpecialTokens: false, + padding, + truncation, + maxLength, + returnTensor, + ...tokenizerKwargs + }); + return returnDict ? out : out.inputIds; + } + + return rendered; + } +} + +export function max(arr: T) { + if (arr.length === 0) throw Error('Array must not be empty'); + let max = arr[0]; + let indexOfMax = 0; + for (let i = 1; i < arr.length; ++i) { + if (arr[i] > max) { + max = arr[i]; + indexOfMax = i; + } + } + return [max, indexOfMax] as T extends bigint[] ? [bigint, number] : [number, number]; +} + +function mergeArrays(...arrs: T[]): T { + return Array.prototype.concat.apply([], arrs) as T; +} + +type TrieNode = { + /** + * If this node marks the end of a word, this property will + * contain the complete word. Otherwise, it's undefined. + */ + end?: string; + + /** + * An index signature to represent child nodes. Each key is a + * character, and each value is the next TrieNode in the sequence. + * The value is a union to satisfy TypeScript's index signature rules. + */ + [key: string]: TrieNode | string | undefined; +}; + +/** + * A data structure which uses a trie to split a string into tokens based on a dictionary. + * It can also use a regular expression to preprocess the input text before splitting. + * + * NOTE: To ensure multi-byte characters are handled correctly, we operate at byte-level instead of character-level. + */ +class DictionarySplitter { + trie: TrieNode; + /** + * @param dictionary The dictionary of words to use for splitting. + */ + constructor(dictionary: string[]) { + this.trie = this.buildTrie(dictionary); + } + + /** + * Builds a trie from the given dictionary. + * @param dictionary The dictionary of words to build the trie from. + * @returns The root node of the trie. + */ + private buildTrie(dictionary: string[]) { + const trie: TrieNode = Object.create(null); + for (const word of dictionary) { + let node = trie; + for (let i = 0; i < word.length; ++i) { + node = (node[word[i]] ??= Object.create(null)) as TrieNode; + } + node.end = word; + } + return trie; + } + + /** + * Splits the input text into tokens based on the dictionary. + * @param {string} text The input text to split. + * @returns {string[]} An array of tokens. + */ + split(text: string): string[] { + const result = []; + const n = text.length; + let start = 0; + let i = 0; + + while (i < n) { + let node = this.trie; + let match = null; + let j = i; + + while (j < n && (node = node[text[j]] as TrieNode)) { + if (node.end) { + // Always keep the last (i.e., longest) match. + match = node.end; + } + ++j; + } + + if (match) { + if (i > start) { + result.push(text.slice(start, i)); + } + result.push(match); + i += match.length; + start = i; + } else { + ++i; + } + } + if (start < n) { + result.push(text.slice(start)); + } + return result; + } +} + +/** + * Efficient Heap-based Implementation of a Priority Queue. + * It uses an array-based binary heap, where the root is at index `0`, and the + * children of node `i` are located at indices `2i + 1` and `2i + 2`, respectively. + * + * Adapted from the following sources: + * - https://stackoverflow.com/a/42919752/13989043 (original) + * - https://github.com/belladoreai/llama-tokenizer-js (minor improvements) + */ +class PriorityQueue { + private heap: T[]; + private comparator: (a: T, b: T) => boolean; + private maxSize: number; + /** + * Create a new PriorityQueue. + * @param comparator Comparator function to determine priority. Defaults to a MaxHeap. + */ + constructor(comparator = (a: T, b: T) => a > b, maxSize = Infinity) { + this.heap = []; + this.comparator = comparator; + this.maxSize = maxSize; + } + + /** + * The size of the queue + */ + get size() { + return this.heap.length; + } + + /** + * Check if the queue is empty. + * @returns `true` if the queue is empty, `false` otherwise. + */ + isEmpty() { + return this.size === 0; + } + + /** + * Return the element with the highest priority in the queue. + * @returns The highest priority element in the queue. + */ + peek() { + return this.heap[0]; + } + + /** + * Add one or more elements to the queue. + * @param values The values to push into the queue. + * @returns The new size of the queue. + */ + push(...values: T[]) { + return this.extend(values); + } + + /** + * Add multiple elements to the queue. + * @param values The values to push into the queue. + * @returns The new size of the queue. + */ + extend(values: T[]) { + for (const value of values) { + if (this.size < this.maxSize) { + this.heap.push(value); + this.siftUp(); + } else { + // Get index of value with the lowest priority + const smallest = this.smallest(); + + // If the new value has higher priority than the smallest value in the heap + // then replace the smallest value with the new value and update the heap + if (this.comparator(value, this.heap[smallest])) { + this.heap[smallest] = value; + this.siftUpFrom(smallest); + } + } + } + return this.size; + } + + /** + * Remove and return the element with the highest priority in the queue. + * @returns The element with the highest priority in the queue. + */ + pop() { + const poppedValue = this.peek(); + const bottom = this.size - 1; + if (bottom > 0) { + this.swap(0, bottom); + } + this.heap.pop(); + this.siftDown(); + return poppedValue; + } + + /** + * Replace the element with the highest priority in the queue with a new value. + * @param value The new value. + * @returns The replaced value. + */ + replace(value: T) { + const replacedValue = this.peek(); + this.heap[0] = value; + this.siftDown(); + return replacedValue; + } + + /** + * Compute the index for the parent of the node at index `i`. + * @param i The index of the node to get the parent of. + * @returns The index of the parent node. + */ + private parent(i: number) { + return ((i + 1) >>> 1) - 1; + } + + /** + * Compute the index for the left child of the node at index `i`. + * @param i The index of the node to get the left child of. + * @returns The index of the left child. + * + */ + private left(i: number) { + return (i << 1) + 1; + } + + /** + * Compute the index for the right child of the node at index `i`. + * @param i The index of the node to get the right child of. + * @returns The index of the right child. + */ + private right(i: number) { + return (i + 1) << 1; + } + + /** + * Check if the element at index `i` is greater than the element at index `j`. + * @param i The index of the first element to compare. + * @param j The index of the second element to compare. + * @returns `true` if the element at index `i` is greater than the element at index `j`, `false` otherwise. + * + */ + private greater(i: number, j: number) { + return this.comparator(this.heap[i], this.heap[j]); + } + + /** + * Swap the elements at indices `i` and `j`. + * @param i The index of the first element to swap. + * @param j The index of the second element to swap. + * + */ + private swap(i: number, j: number) { + const temp = this.heap[i]; + this.heap[i] = this.heap[j]; + this.heap[j] = temp; + } + + /** + * Maintain the heap property by updating positions in the heap, + * starting at the last element and moving up the heap. + */ + private siftUp() { + this.siftUpFrom(this.size - 1); + } + + /** + * Helper function to sift up from a given node. + * @param node The index of the node to start sifting up from. + */ + private siftUpFrom(node: number) { + while (node > 0 && this.greater(node, this.parent(node))) { + this.swap(node, this.parent(node)); + node = this.parent(node); + } + } + + /** + * Maintain the heap property by updating positions in the heap, + * starting at the first element and moving down the heap. + */ + private siftDown() { + let node = 0; + while ( + (this.left(node) < this.size && this.greater(this.left(node), node)) || + (this.right(node) < this.size && this.greater(this.right(node), node)) + ) { + const maxChild = + this.right(node) < this.size && this.greater(this.right(node), this.left(node)) + ? this.right(node) + : this.left(node); + this.swap(node, maxChild); + node = maxChild; + } + } + + /** + * Get the index of the smallest element in the heap. Since we use an array-based heap, + * the index can be computed without needing to traverse the heap. + */ + private smallest(): number { + return 2 ** Math.floor(Math.log2(this.size)) - 1; + } +} + +/** + * A simple Least Recently Used (LRU) cache implementation in JavaScript. + * This cache stores key-value pairs and evicts the least recently used item + * when the capacity is exceeded. + */ +class LRUCache { + capacity: number; + cache: Map; + /** + * Creates an LRUCache instance. + * @param capacity The maximum number of items the cache can hold. + */ + constructor(capacity: number) { + this.capacity = capacity; + this.cache = new Map(); + } + + /** + * Retrieves the value associated with the given key and marks the key as recently used. + * @param key The key to retrieve. + * @returns The value associated with the key, or undefined if the key does not exist. + */ + get(key: Key) { + if (!this.cache.has(key)) return undefined; + const value = this.cache.get(key); + this.cache.delete(key); + this.cache.set(key, value as Value); + return value; + } + + /** + * Inserts or updates the key-value pair in the cache. + * If the key already exists, it is updated and marked as recently used. + * If the cache exceeds its capacity, the least recently used item is evicted. + * @param key The key to add or update. + * @param value The value to associate with the key. + */ + put(key: Key, value: Value) { + if (this.cache.has(key)) { + this.cache.delete(key); + } + this.cache.set(key, value); + if (this.cache.size > this.capacity) { + this.cache.delete(this.cache.keys().next().value as Key); + } + } + + /** + * Clears the cache. + */ + clear() { + this.cache.clear(); + } +} + +export function decodeSingle( + value: number, + tokenizer: PreTrainedTokenizer | ToyTokenizer | null +): string { + if (tokenizer instanceof PreTrainedTokenizer) { + return tokenizer + .decodeSingle([value]) + .replaceAll('<|end_of_text|>', '▶️📄') + .replaceAll('<|im_start|>', '▶️💬') + .replaceAll('<|im_end|>', '⏹️💬'); + } return tokenizer?.ids?.[value] || `<${value}>`; } diff --git a/examples/piston-train-toy/src/lib/train/types.ts b/examples/piston-train-toy/src/lib/train/types.ts index 18b340ce..ea25c071 100644 --- a/examples/piston-train-toy/src/lib/train/types.ts +++ b/examples/piston-train-toy/src/lib/train/types.ts @@ -1,8 +1,14 @@ import type { DataLoader } from '@piston-ml/piston-web'; +import type { + NaturalLanguageAutoregressiveBatch, + NaturalLanguageBidirectionalBatch, + NaturalLanguageDataset +} from './data/natural'; import type { ToyAutoregressiveBatch, ToyBidirectionalBatch, + ToyDatasetLike, ToyEncoderDecoderBatch, ToySequence } from './data/toy/dataset'; @@ -15,6 +21,11 @@ import type { type CollateFn = (batch: B) => T; type ToyCollateInput = ToySequence[]; +type NaturalCollateInput = number[][]; + +export type NaturalBatchType = + | NaturalLanguageBidirectionalBatch + | NaturalLanguageAutoregressiveBatch; export type ToyBatchType = | ToyBidirectionalBatch @@ -35,8 +46,14 @@ export type ToyEncoderDecoderCollateFnType = CollateFn< export type EncoderDecoderCollateFnType = CollateFn>; +export type NaturalCollateFnType = CollateFn>; export type ToyCollateFnType = CollateFn>; +export type PistonCollateFnType = NaturalCollateFnType | ToyCollateFnType; +export type NaturalDataloaderType = DataLoader>; export type ToyDataloaderType = DataLoader>; +export type PistonDataloaderType = ToyDataloaderType | NaturalDataloaderType; export type GeneratableModel = DecoderTransformer | EncoderTransformer | EncoderDecoderTransformer; + +export type PistonDatasetType = ToyDatasetLike | NaturalLanguageDataset; diff --git a/examples/piston-train-toy/src/lib/train/utils/model.ts b/examples/piston-train-toy/src/lib/train/utils/model.ts index d4526778..6660e96b 100644 --- a/examples/piston-train-toy/src/lib/train/utils/model.ts +++ b/examples/piston-train-toy/src/lib/train/utils/model.ts @@ -3,9 +3,24 @@ import type { Random } from 'random-js'; import { DataLoader, Tensor } from '@piston-ml/piston-web'; import type { Config } from '../../workspace/config'; -import type { ToyBatchType, ToyCollateFnType, ToyDataloaderType } from '../types'; +import type { + NaturalBatchType, + NaturalCollateFnType, + NaturalDataloaderType, + PistonCollateFnType, + PistonDataloaderType, + PistonDatasetType, + ToyBatchType, + ToyCollateFnType, + ToyDataloaderType +} from '../types'; import { type CollateWrapFunction } from '../data'; +import { + naturalLanguageAutoregressiveCollate, + naturalLanguageBidirectionalCollate, + NaturalLanguageDataset +} from '../data/natural'; import { toyDatasetAutoregressiveCollate, toyDatasetBidirectionalCollate, @@ -23,7 +38,7 @@ type EncoderDecoderBlockSize = { source: number; target: number }; /** * Calculate the vocabulary size based on the dataset and configuration */ -export function calculateVocabSize(dataset: ToyDatasetLike): number { +export function calculateVocabSize(dataset: PistonDatasetType): number { return 'vocabSize' in dataset ? (dataset.vocabSize as number) : Object.keys(dataset.tokenizer.vocab).length; @@ -31,9 +46,13 @@ export function calculateVocabSize(dataset: ToyDatasetLike): number { export function calculateBlockSize( config: Config, - dataloader: ToyDataloaderType + dataloader: PistonDataloaderType ): number | EncoderDecoderBlockSize { - // Infer lengths without advancing iteration using deterministic index-based generation + if (dataloader.dataset instanceof NaturalLanguageDataset) { + return config.data.natural.contextSize; + } + + // For toy datasets, infer lengths without advancing iteration using deterministic index-based generation const toy = dataloader.dataset as ToyDatasetLike; const sample = toy.generateSequenceAt(0); @@ -58,16 +77,52 @@ export function calculateBlockSize( } // Overloads for strong typing based on dataset kind +export function createCollateFn( + config: Config, + dataset: NaturalLanguageDataset, + maskGenerator: Random | null, + wrapFunction?: CollateWrapFunction | null +): NaturalCollateFnType; export function createCollateFn( config: Config, dataset: ToyDatasetLike, maskGenerator: Random | null, wrapFunction?: CollateWrapFunction | null -): ToyCollateFnType { +): ToyCollateFnType; +export function createCollateFn( + config: Config, + dataset: PistonDatasetType, + maskGenerator: Random | null, + wrapFunction?: CollateWrapFunction | null +): PistonCollateFnType; +export function createCollateFn( + config: Config, + dataset: PistonDatasetType, + maskGenerator: Random | null, + wrapFunction?: CollateWrapFunction | null +): PistonCollateFnType { const isEncoderOnly = config.model.topology === 'encoder'; const isEncoderDecoder = config.model.topology === 'encoder-decoder'; const collateOptions = wrapFunction !== undefined ? { wrapFunction } : {}; - if (isEncoderOnly) { + if (dataset instanceof NaturalLanguageDataset) { + if (isEncoderDecoder) { + throw new Error('Encoder-decoder is not supported for natural language datasets.'); + } + if (isEncoderOnly) { + return (batch: number[][]) => + naturalLanguageBidirectionalCollate(batch as number[][], { + maskRatio: config.data.maskRatio, + generator: maskGenerator!, + maskTokenId: dataset.maskId! as number, + ...collateOptions + }); + } else { + return (batch: number[][]) => + naturalLanguageAutoregressiveCollate(batch as number[][], { + ...collateOptions + }); + } + } else if (isEncoderOnly) { return (batch: ToySequence[]) => toyDatasetBidirectionalCollate(batch as ToySequence[], dataset, { maskPrompt: config.data.trainOnPrompt, @@ -89,15 +144,42 @@ export function createCollateFn( } } +export function createDataloader( + config: Config, + dataset: NaturalLanguageDataset, + generator: Random, + wrapFunction?: CollateWrapFunction | null +): [NaturalDataloaderType, NaturalCollateFnType]; export function createDataloader( config: Config, dataset: ToyDatasetLike, generator: Random, wrapFunction?: CollateWrapFunction | null -): [ToyDataloaderType, ToyCollateFnType] { - const toyDataset = dataset as ToyDatasetLike; - const collateFn = createCollateFn(config, toyDataset, generator, wrapFunction); - return [new DataLoader>(toyDataset, { collateFn }), collateFn]; +): [ToyDataloaderType, ToyCollateFnType]; +export function createDataloader( + config: Config, + dataset: PistonDatasetType, + generator: Random, + wrapFunction?: CollateWrapFunction | null +): + | [NaturalDataloaderType, NaturalCollateFnType] + | [ToyDataloaderType, ToyCollateFnType]; +export function createDataloader( + config: Config, + dataset: PistonDatasetType, + generator: Random, + wrapFunction?: CollateWrapFunction | null +): + | [NaturalDataloaderType, NaturalCollateFnType] + | [ToyDataloaderType, ToyCollateFnType] { + if (dataset instanceof NaturalLanguageDataset) { + const collateFn = createCollateFn(config, dataset, generator, wrapFunction); + return [new DataLoader>(dataset, { collateFn }), collateFn]; + } else { + const toyDataset = dataset as ToyDatasetLike; + const collateFn = createCollateFn(config, toyDataset, generator, wrapFunction); + return [new DataLoader>(toyDataset, { collateFn }), collateFn]; + } } /** diff --git a/examples/piston-train-toy/src/lib/workspace/config.svelte.ts b/examples/piston-train-toy/src/lib/workspace/config.svelte.ts index 02b8983f..27c8543e 100644 --- a/examples/piston-train-toy/src/lib/workspace/config.svelte.ts +++ b/examples/piston-train-toy/src/lib/workspace/config.svelte.ts @@ -1,8 +1,7 @@ import type { Config, ModelType } from '$lib/workspace/config'; -import { DATASET_CONFIG_METADATA } from '$lib/train/data'; +import { buildDataset, DATASET_CONFIG_METADATA } from '$lib/train/data'; import { getCollatedSampleData } from '$lib/train/data/collate'; -import { buildToyDataset } from '$lib/train/data/toy'; import { TOY_DATASET_CONFIG_DEFAULTS } from '$lib/train/data/toy/config'; import { calculateBlockSize, calculateVocabSize, createDataloader } from '$lib/train/utils/model'; import { seededRandom } from '$lib/train/utils/random'; @@ -39,7 +38,11 @@ const CONFIG_DEFAULTS: Config = { specialTokens: { includeEos: true }, - datasets: TOY_DATASET_CONFIG_DEFAULTS + datasets: TOY_DATASET_CONFIG_DEFAULTS, + natural: { + contextSize: 32, + vocabSize: 1024 + } }, model: { topology: 'decoder', @@ -391,25 +394,26 @@ function ensureDatasetSupportsModelType() { function datasetFromConfig(config: Config) { ensureDatasetSupportsModelType(); const generator = seededRandom(); - const dataset = buildToyDataset(config, generator); + const dataset = buildDataset(config, generator, 'train'); const [dataloader, collateFn] = createDataloader(config, dataset, generator, null); const blockSize = calculateBlockSize(config, dataloader); const vocabSize = calculateVocabSize(dataset); const collatedData = getCollatedSampleData(dataset, collateFn, 4); - const firstSample = collatedData.collated[0]; - return { dataset, vocabSize, blockSize, tokenizer: dataset.tokenizer, - sampleData: { - hasPrompt: 'prompt' in firstSample && (firstSample.prompt?.length ?? 0) > 0, - samples: collatedData.samples, - collated: collatedData.collated - } + sampleData: collatedData.then((data) => { + const firstSample = data.collated[0]; + return { + hasPrompt: 'prompt' in firstSample && (firstSample.prompt?.length ?? 0) > 0, + samples: data.samples, + collated: data.collated + }; + }) }; } diff --git a/examples/piston-train-toy/src/lib/workspace/config.ts b/examples/piston-train-toy/src/lib/workspace/config.ts index 4c77b48a..35d742df 100644 --- a/examples/piston-train-toy/src/lib/workspace/config.ts +++ b/examples/piston-train-toy/src/lib/workspace/config.ts @@ -51,6 +51,10 @@ export interface DataConfig { specialTokens: { includeEos: boolean; }; + natural: { + contextSize: number; + vocabSize: 'char' | 512 | 1024 | 2048 | 4096 | 8192 | 16384 | 32768 | 65536; + }; } export interface PositionEncodingConfig { diff --git a/examples/piston-train-toy/vite.config.ts b/examples/piston-train-toy/vite.config.ts index 9e9e4b01..efa45515 100644 --- a/examples/piston-train-toy/vite.config.ts +++ b/examples/piston-train-toy/vite.config.ts @@ -1,5 +1,6 @@ import { sveltekit } from '@sveltejs/kit/vite'; import tailwindcss from '@tailwindcss/vite'; +import fs from 'node:fs/promises'; import path from 'path'; import { fileURLToPath } from 'url'; import { defineConfig } from 'vite'; @@ -8,8 +9,25 @@ import wasm from 'vite-plugin-wasm'; // Get the project root directory const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..'); +// Remove large static subfolders we don't want to ship +const pruneStaticDirs = () => { + return { + name: 'prune-static-dirs', + // After Vite writes bundles (client/server into .svelte-kit/output/*), + // remove unwanted static folders from client output to avoid copying into final build + async writeBundle() { + const root = path.dirname(fileURLToPath(import.meta.url)); + const clientOut = path.resolve(root, '.svelte-kit', 'output', 'client'); + const targets = [path.resolve(clientOut, 'tokenized'), path.resolve(clientOut, 'tokenizer')]; + await Promise.all( + targets.map((p) => fs.rm(p, { recursive: true, force: true }).catch(() => {})) + ); + } + }; +}; + export default defineConfig((_) => ({ - plugins: [tailwindcss(), sveltekit(), wasm()], + plugins: [tailwindcss(), sveltekit(), wasm(), pruneStaticDirs()], worker: { format: 'es', plugins: () => [wasm(), sveltekit()] diff --git a/examples/piston-train-toy/wrangler.jsonc b/examples/piston-train-toy/wrangler.jsonc index 68aa788d..871bccbb 100644 --- a/examples/piston-train-toy/wrangler.jsonc +++ b/examples/piston-train-toy/wrangler.jsonc @@ -4,4 +4,9 @@ "assets": { "directory": "./build/" }, + "exclude": [ + "./static/tokenized", + "./static/tokenizer", + "./data" + ] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8c975b08..fc681702 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -25,6 +25,9 @@ importers: examples/piston-train-toy: dependencies: + '@huggingface/jinja': + specifier: ^0.5.1 + version: 0.5.1 '@piston-ml/piston-web': specifier: workspace:^ version: link:../../packages/web @@ -1154,6 +1157,10 @@ packages: resolution: {integrity: sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@huggingface/jinja@0.5.1': + resolution: {integrity: sha512-yUZLld4lrM9iFxHCwFQ7D1HW2MWMwSbeB7WzWqFYDWK+rEb+WldkLdAJxUPOmgICMHZLzZGVcVjFh3w/YGubng==} + engines: {node: '>=18'} + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -7412,6 +7419,8 @@ snapshots: '@eslint/core': 0.16.0 levn: 0.4.1 + '@huggingface/jinja@0.5.1': {} + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.6': From 3824cd62f5af57009f7ec236bbec89b368639150 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Mon, 27 Oct 2025 21:03:59 -0600 Subject: [PATCH 539/590] Muon formatting --- packages/web/src/optim/muon.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/web/src/optim/muon.ts b/packages/web/src/optim/muon.ts index 1c59ff3c..8aa8d0a3 100644 --- a/packages/web/src/optim/muon.ts +++ b/packages/web/src/optim/muon.ts @@ -205,7 +205,13 @@ export class Muon extends Optimizer this.state.set(param, state); } - const update = muonUpdate(grad, state.momentumBuffer as Tensor, momentum, nsSteps, nesterov); + const update = muonUpdate( + grad, + state.momentumBuffer as Tensor, + momentum, + nsSteps, + nesterov, + ); // Apply weight decay if (weightDecay !== 0) { From bbbe4438f48bebe2cdff4458a12b8a165ec2fb42 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Mon, 27 Oct 2025 21:09:27 -0600 Subject: [PATCH 540/590] Add initial likelihood-based validation --- .../lib/components/controls/Controls.svelte | 55 +++++ .../components/controls/ControlsNote.svelte | 49 ++++ .../controls/DatasetControls.svelte | 14 ++ .../src/lib/train/data/filter.ts | 145 ++++++++++++ .../src/lib/train/data/pipeline.ts | 37 ++- .../piston-train-toy/src/lib/train/session.ts | 113 ++++++++- .../piston-train-toy/src/lib/train/types.ts | 6 + .../src/lib/train/validation.ts | 219 ++++++++++++++++++ .../src/lib/workspace/config.svelte.ts | 7 + .../src/lib/workspace/config.ts | 9 + .../src/lib/workspace/ui.svelte.ts | 30 +++ .../src/lib/workspace/workers.svelte.ts | 11 +- .../src/routes/tabs/Metrics.svelte | 18 +- 13 files changed, 695 insertions(+), 18 deletions(-) create mode 100644 examples/piston-train-toy/src/lib/components/controls/ControlsNote.svelte create mode 100644 examples/piston-train-toy/src/lib/train/data/filter.ts create mode 100644 examples/piston-train-toy/src/lib/train/validation.ts diff --git a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte index be5372dc..1c99eab7 100644 --- a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte +++ b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte @@ -315,6 +315,38 @@ onReset={() => resetConfigToDefaults('training.batchSize')} /> + resetConfigToDefaults('training.validation.present')} + > + resetConfigToDefaults('training.validation.valSteps')} + /> + resetConfigToDefaults('training.validation.batchSize')} + /> + + diff --git a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte index 60498768..47a0a560 100644 --- a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte +++ b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte @@ -502,6 +502,13 @@ onReset={() => resetConfigToDefaults('training.batchSize')} /> + resetConfigToDefaults('training.enableVisualization')} + /> + {/if} + {#if config.visualization.target === 'validation'} + Visualizing Selected Example + {/if}
    {#if !completionsData} @@ -650,6 +658,7 @@
    {:else} {@const visibleCompletionsNumberWidth = visibleCompletions.length.toString().length} + {@const focus = hoveredFocus ?? config.visualization.selectedValidation}
    (hoveredFocus = null)} + onclick={() => + updateVisualizerSelectedValidation({ + exampleIndex: index, + tokenIndex: 0 + })} + onkeydown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + updateVisualizerSelectedValidation({ + exampleIndex: index, + tokenIndex: 0 + }); + } + }} > {#if showEncoderGrid}
    @@ -751,6 +775,11 @@ tokenIndex={sequenceTokenIndex} onHover={(ei, ti) => (hoveredFocus = { exampleIndex: ei, tokenIndex: ti })} onLeave={() => (hoveredFocus = null)} + onSelect={(ei, ti) => + updateVisualizerSelectedValidation({ + exampleIndex: ei, + tokenIndex: ti + })} /> {:else} (hoveredFocus = { exampleIndex: ei, tokenIndex: ti })} onLeave={() => (hoveredFocus = null)} + onSelect={(ei, ti) => + updateVisualizerSelectedValidation({ + exampleIndex: ei, + tokenIndex: ti + })} /> {/if} {/each} @@ -792,6 +826,11 @@ disabled={isPrompt} onHover={(ei, ti) => (hoveredFocus = { exampleIndex: ei, tokenIndex: ti })} onLeave={() => (hoveredFocus = null)} + onSelect={(ei, ti) => + updateVisualizerSelectedValidation({ + exampleIndex: ei, + tokenIndex: ti + })} /> {/each} {/if} diff --git a/examples/piston-train-toy/src/lib/train/capture.ts b/examples/piston-train-toy/src/lib/train/capture.ts new file mode 100644 index 00000000..6bf45843 --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/capture.ts @@ -0,0 +1,205 @@ +import type { BaseStepData } from '$lib/workspace/runs.svelte'; + +import { + CapturePlan, + type CaptureResult, + CaptureSession, + Module, + pin, + Tensor, + type TensorQuery +} from '@piston-ml/piston-web'; + +import type { + BidirectionalBatchType, + EncoderDecoderBatchType, + GeneratableModel, + NaturalCollateFnType, + PistonCollateFnType, + ToyCollateFnType +} from './types'; +import type { ValidationExamples } from './validation'; + +import { + DecoderTransformer, + EncoderDecoderTransformer, + EncoderTransformer +} from './model/transformer'; + +export interface CaptureMatch { + matchId: number; + buffer?: GPUBuffer | null; + type: 'module' | 'parameter' | 'op'; + op?: string; + parameter?: string; + moduleSite?: 'input' | 'output'; + batchIndex?: number; + source: TensorQuery; + queryIndex?: number; + path: string; + shape: number[]; + tensor: Tensor; + mean?: number; + variance?: number; +} + +export type RemoteCaptureMatch = Omit; + +export type CaptureStep = BaseStepData & { + type: 'capture'; + matches: CaptureMatch[]; +}; + +export type RemoteCaptureStep = BaseStepData & { + type: 'capture'; + matches: RemoteCaptureMatch[]; +}; + +export function createCapturePlan(query: string, module: Module): CapturePlan | null { + const capturePlan = new CapturePlan(); + try { + return capturePlan.parseScript(query).hookModule(module); + } catch (e) { + console.warn('Failed to create CapturePlan', e); + console.warn(capturePlan.getDiagnostics()); + return null; + } +} + +export function processCaptureResults( + results: CaptureResult[], + batchIndex: number = 0 +): CaptureMatch[] { + const processedMatches: (CaptureMatch | null)[] = []; + + for (const result of results) { + for (const [path, matches] of Object.entries(result.matches)) { + for (const match of matches) { + // For now we just ignore array-tensors until we know what to do with them + if (match.bufferTensor !== undefined && !Array.isArray(match.bufferTensor)) { + const t = match.bufferTensor; + // Alas, debugTensor doesn't seem to work in all scenarios, including when doing + // validation. Seems like we don't copy the buffer to the GPU at the right time. + // const effectiveTensor = match.type === 'parameter' ? t._clone() : t._clone().debugTensor; + const effectiveTensor = t._clone(); + processedMatches.push({ + matchId: t.id, + type: match.type, + ...(match.type !== 'parameter' ? { batchIndex } : {}), + source: result.source, + queryIndex: match.queryIndex ?? undefined, + op: (match as { op?: string }).op, + parameter: (match as { parameter?: string }).parameter, + moduleSite: (match as { site?: 'input' | 'output' }).site, + path: path, + shape: t.shape, + tensor: pin(effectiveTensor) + }); + } + } + } + } + + return processedMatches.filter((m): m is CaptureMatch => m !== null); +} + +export function makeCaptureMatchRemote( + match: RemoteCaptureMatch & { tensor?: unknown; buffer?: unknown } +): RemoteCaptureMatch { + const { buffer: _1, tensor: _2, ...rest } = match; + return rest; +} + +export async function runValidationExampleForCapture( + model: GeneratableModel, + sequences: ValidationExamples, + collateFn: PistonCollateFnType, + batchIndex: number +): Promise { + model.eval(); + + let valLoss: number | null = null; + try { + let collated; + if ('toySequences' in sequences) { + collated = (collateFn as ToyCollateFnType)([sequences.toySequences[batchIndex]]); + } else { + collated = (collateFn as NaturalCollateFnType)([ + sequences.naturalSequences[batchIndex] + ]); + } + + let loss: Tensor | null = null; + let modelName = ''; + if (model instanceof DecoderTransformer) { + const [inputs, targets] = collated.tensors; + [, loss] = model.forward(await inputs.to('gpu'), { + targets: await targets.to('gpu') + }); + modelName = 'decoder-only'; + } else if (model instanceof EncoderDecoderTransformer) { + const [encoderInputs, decoderInputs, decoderTargets] = ( + collated as EncoderDecoderBatchType + ).tensors; + [, loss] = model.forward(await encoderInputs.to('gpu'), await decoderInputs.to('gpu'), { + targets: await decoderTargets.to('gpu') + }); + modelName = 'encoder-decoder'; + } else if (model instanceof EncoderTransformer) { + // Encoder-only: compute MLM loss over masked tokens + const [inputs, labels, attentionMask] = (collated as BidirectionalBatchType).tensors; + modelName = 'encoder-only'; + [, , , loss] = model.forward(await inputs.to('gpu'), { + attentionMask: await attentionMask.to('gpu'), + targets: await labels.to('gpu') + }); + } else { + throw new Error('Unsupported model for validation'); + } + + if (!loss) { + throw new Error(`No loss tensor returned from ${modelName} model during validation`); + } + valLoss = await (await loss.to('cpu')).item(); + if (valLoss === null) { + throw new Error(`Validation loss item is null for ${modelName} model`); + } + // We do a backward pass to make sure we can capture the gradients + loss.backward(); + } finally { + model.train(); + } +} + +export type CapturePlanConfig = { + enabled: boolean; + script?: string | null; +}; + +export class CaptureManager { + private _plan: CapturePlan | null = null; + + build(model: Module, config: CapturePlanConfig): void { + this._plan = null; + if (!config.enabled) return; + if (!config.script) return; + this._plan = createCapturePlan(config.script, model); + } + + createSession(): CaptureSession | null { + return this._plan ? this._plan.createSession() : null; + } + + finalize(session: CaptureSession, batchIndex: number = 0): CaptureMatch[] { + const results = session.finalize(); + return processCaptureResults(results, batchIndex); + } + + get queries(): unknown[] { + return this._plan?.queries ?? []; + } + + get plan(): CapturePlan | null { + return this._plan; + } +} diff --git a/examples/piston-train-toy/src/lib/train/moduleWorker.ts b/examples/piston-train-toy/src/lib/train/moduleWorker.ts index b703e16d..123482ce 100644 --- a/examples/piston-train-toy/src/lib/train/moduleWorker.ts +++ b/examples/piston-train-toy/src/lib/train/moduleWorker.ts @@ -6,6 +6,7 @@ import { TrainingSession } from './session'; import { inspectModel } from './utils/model'; let session: TrainingSession | undefined; +let pendingVisualizerCanvas: { canvas: OffscreenCanvas; labelPaddingCssPx: number } | null = null; // Console Interception const originalConsole = { @@ -183,6 +184,59 @@ self.addEventListener('message', async (event) => { await session.step({ manual: true }); break; } + case 'visualizer.updateScript': { + try { + const { script, example } = data as { script: string; example: string }; + session?.setVisualizationScript(example, script ?? null); + self.postMessage({ type: 'visualizer.ready' }); + } catch (e) { + console.error('Failed to update visualizer script', e); + self.postMessage({ type: 'visualizer.error', message: String(e) }); + } + break; + } + case 'visualizer.canvas': { + try { + const payload = data as { canvas: OffscreenCanvas; labelPaddingCssPx?: number }; + const labelPaddingCssPx = payload.labelPaddingCssPx ?? 0; + pendingVisualizerCanvas = { canvas: payload.canvas, labelPaddingCssPx }; + if (session) { + session.initVisualizerCanvas(payload.canvas, labelPaddingCssPx); + } + self.postMessage({ type: 'visualizer.ready' }); + } catch (e) { + console.error('Visualizer init failed', e); + self.postMessage({ type: 'visualizer.error', message: String(e) }); + } + break; + } + case 'visualizer.resize': { + try { + const { width } = data as { width: number }; + session?.resizeVisualizer(width); + } catch (e) { + console.error('Visualizer resize failed', e); + } + break; + } + case 'visualizer.setTarget': { + try { + const { target } = data as { target: 'train' | 'validation' }; + session?.setVisualizationTarget(target); + } catch (e) { + console.error('Visualizer set target failed', e); + } + break; + } + case 'visualizer.setSelectedValidation': { + try { + const { exampleIndex, tokenIndex } = data as { exampleIndex: number; tokenIndex: number }; + session?.setVisualizationSelectedValidation({ exampleIndex, tokenIndex }); + } catch (e) { + console.error('Visualizer set selected validation failed', e); + } + break; + } case 'start': try { const { runId: runIdFromData, config } = data as { @@ -193,6 +247,18 @@ self.addEventListener('message', async (event) => { console.info(`Starting training for run ${runIdFromData}`); session = new TrainingSession(runIdFromData, config, postEvent); + if (pendingVisualizerCanvas) { + try { + session.initVisualizerCanvas( + pendingVisualizerCanvas.canvas, + pendingVisualizerCanvas.labelPaddingCssPx + ); + self.postMessage({ type: 'visualizer.ready' }); + } catch (e) { + console.error('Visualizer init (deferred) failed', e); + self.postMessage({ type: 'visualizer.error', message: String(e) }); + } + } startTraining(); } catch (error: unknown) { console.error('Training error:', error); @@ -217,6 +283,7 @@ self.addEventListener('message', async (event) => { type: 'modelInspection', requestId, parameterCount: result.parameterCount, + modelIndex: result.modelIndex, vocabSize: result.vocabSize, blockSize: result.blockSize }); diff --git a/examples/piston-train-toy/src/lib/train/protocol.ts b/examples/piston-train-toy/src/lib/train/protocol.ts index 86ae5028..d0572278 100644 --- a/examples/piston-train-toy/src/lib/train/protocol.ts +++ b/examples/piston-train-toy/src/lib/train/protocol.ts @@ -1,5 +1,6 @@ import type { Config } from '$lib/workspace/config'; import type { StepData } from '$lib/workspace/runs.svelte'; +import type { IndexState } from '@piston-ml/piston-web'; type WithRunId = { runId: string }; @@ -12,6 +13,20 @@ export type WorkerCommand = | { type: 'resume' } | { type: 'step' } | { type: 'stop' } + | { + type: 'visualizer.updateScript'; + data: { example: string; script: string | null }; + } + | { + type: 'visualizer.canvas'; + data: { canvas: OffscreenCanvas; labelPaddingCssPx?: number }; + } + | { type: 'visualizer.resize'; data: { width: number } } + | { type: 'visualizer.setTarget'; data: { target: 'train' | 'validation' } } + | { + type: 'visualizer.setSelectedValidation'; + data: { exampleIndex: number; tokenIndex: number }; + } | { type: 'inspectModel'; data: { requestId: string; config: Config } }; type ReadyWorkerEvent = { @@ -40,6 +55,15 @@ export type RunWorkerEventWithoutRunId = data: { [metricName: string]: Omit }; metadata?: { step?: number }; } + | { + type: 'capture'; + step: number; + boxes: unknown[]; + statsById: Record; + width: number; + height: number; + queries: unknown[]; + } | { type: 'paused' } | { type: 'resumed' } | { type: 'complete' } @@ -53,10 +77,13 @@ export type WorkerEvent = | LogWorkerEvent | ErrorWorkerEvent | RunWorkerEvent + | { type: 'visualizer.ready' } + | { type: 'visualizer.error'; message: string } | { type: 'modelInspection'; requestId: string; parameterCount: number; vocabSize: number; + modelIndex: IndexState; } | { type: 'modelInspectionError'; requestId: string; message: string }; diff --git a/examples/piston-train-toy/src/lib/train/session.ts b/examples/piston-train-toy/src/lib/train/session.ts index 27f702f8..22222934 100644 --- a/examples/piston-train-toy/src/lib/train/session.ts +++ b/examples/piston-train-toy/src/lib/train/session.ts @@ -1,6 +1,7 @@ import type { Config } from '$lib/workspace/config'; import type { StepData } from '$lib/workspace/runs.svelte'; +import { getEffectiveVisualizationScript } from '$lib/workspace/visualizationExamples'; import { CosineAnnealingLR, ExponentialLR, @@ -11,6 +12,7 @@ import { } from '@piston-ml/piston-web'; import * as piston from '@piston-ml/piston-web'; +import type { CaptureMatch, RemoteCaptureStep } from './capture'; import type { BuiltData } from './data/pipeline'; import type { ToyAutoregressiveBatch, @@ -21,6 +23,7 @@ import type { import type { RunWorkerEvent, RunWorkerEventWithoutRunId, WorkerEvent } from './protocol'; import type { GeneratableModel, PistonCollateFnType, PistonDatasetType } from './types'; +import { CaptureManager, makeCaptureMatchRemote, runValidationExampleForCapture } from './capture'; import { buildDataset, tensorWrap } from './data'; import { filterDatasetByHeldoutSamples } from './data/filter'; import { NaturalLanguageDataset } from './data/natural'; @@ -55,6 +58,7 @@ import { type ValidationExamples, type ValidationStep } from './validation'; +import { Visualizer } from './visualizer'; // @ts-expect-error polyfill Symbol.dispose ||= Symbol.for('Symbol.dispose'); @@ -67,6 +71,18 @@ export class TrainingSession { private paused = false; private resolvePause: (() => void) | null = null; + private visualizer: Visualizer | null = null; + private visualizerDevice: GPUDevice | null = null; + private visualizerCanvas: OffscreenCanvas | null = null; + private visualizerLabelPaddingCssPx: number = 0; + private readonly currentVisualizationSelectedValidation: { + exampleIndex: number; + tokenIndex: number; + } = { + exampleIndex: 0, + tokenIndex: 0 + }; + private model!: GeneratableModel; private optimizer!: piston.Optimizer; private scheduler: LRScheduler | undefined; @@ -80,6 +96,8 @@ export class TrainingSession { private lastLogStep: number | null = null; private stepCount: number = 0; + private captureManager: CaptureManager | null = null; + private dataPipeline!: BuiltData; private validationExamples: ValidationExamples | null = null; @@ -109,6 +127,91 @@ export class TrainingSession { this.post({ type: 'resumed' }); } + setVisualizationScript(example: string, script: string | null) { + // If a model is already active, rebuild the plan immediately; otherwise setup() will build it + if (this.model) { + const newCaptureManager = new CaptureManager(); + newCaptureManager.build(this.model, { + enabled: this.config.training.enableVisualization, + script: script ?? undefined + }); + if (newCaptureManager.plan) { + this.captureManager = newCaptureManager; + this.config.visualization.example = example; + this.config.visualization.script = script; + } + } + } + + setVisualizationTarget(target: 'train' | 'validation') { + this.config.visualization.target = target; + } + + setVisualizationSelectedValidation({ + exampleIndex, + tokenIndex + }: { + exampleIndex: number; + tokenIndex: number; + }) { + this.currentVisualizationSelectedValidation.exampleIndex = exampleIndex; + this.currentVisualizationSelectedValidation.tokenIndex = tokenIndex; + } + + initVisualizerCanvas(canvas: OffscreenCanvas, labelPaddingCssPx: number = 0) { + // Dispose old visualizer if any + if (this.visualizer) { + this.visualizer.dispose(); + this.visualizer = null; + } + + this.visualizerCanvas = canvas; + this.visualizerLabelPaddingCssPx = labelPaddingCssPx; + const pistonDevice = piston.gpu.asWebGPUDevice(); + if (!pistonDevice) { + throw new Error('Failed to get WebGPU device from piston.gpu'); + } + this.visualizerDevice = pistonDevice; + this.visualizer = new Visualizer(this.visualizerDevice); + this.visualizer.init(this.visualizerCanvas); + this.visualizer.setCssLabelPadding(this.visualizerLabelPaddingCssPx); + } + + resizeVisualizer(width: number) { + this.visualizer?.resize(width); + } + + private async onCaptureMatches(step: number, matches: CaptureMatch[]) { + try { + await piston.gpu.markStep(); + const captureStep: Omit = { + type: 'capture', + matches: matches.map(makeCaptureMatchRemote) + }; + this.logMetrics({ 'visualization/matches': captureStep }, { step }); + const result = this.visualizer + ? await this.visualizer.renderCapture(matches) + : { boxes: [], statsById: {}, width: 1, height: 1 }; + + this.post({ + type: 'capture', + queries: this.captureManager?.queries ?? [], + step, + boxes: result.boxes.map((box) => ({ ...box, match: makeCaptureMatchRemote(box.match) })), + statsById: result.statsById, + width: result.width, + height: result.height + }); + } catch (err) { + // Fall back to logging the error and continue + this.post({ + type: 'log', + level: 'warn', + message: `Visualizer render failed: ${String(err)}` + }); + } + } + private logMetrics( data: { [metricName: string]: Omit }, metadata?: { step?: number } @@ -234,6 +337,19 @@ export class TrainingSession { // We need to flatten down initialization to the constant tensors they're on top of await piston.gpu.markStep(); + // Build or refresh capture plan using CaptureManager + this.captureManager = new CaptureManager(); + const scriptToUse = this.config.training.enableVisualization + ? getEffectiveVisualizationScript( + this.config.visualization.example, + this.config.visualization.script + ) + : null; + this.captureManager.build(this.model, { + enabled: this.config.training.enableVisualization, + script: scriptToUse + }); + // Build and store the training data pipeline (iterator bound to current dataset/collate) this.dataPipeline = await buildDataPipeline( this.config, @@ -309,6 +425,13 @@ export class TrainingSession { piston.gpu.markUsageBytesStep(); const loggingStep = manual || this.stepCount % this.config.training.logSteps === 0; + + let captureSession: piston.CaptureSession | null = null; + if (loggingStep && this.captureManager && this.config.visualization.target === 'train') { + // Create session on top of weak mode for this step to capture forward ops + captureSession = this.captureManager.createSession(); + } + const weakModeUntilAfterBackward = new WeakModeIfEnabled( this.config.training.useWeakTensorReferences, { @@ -373,6 +496,16 @@ export class TrainingSession { weakModeUntilAfterBackward.pin(loss); loss.backward(); + + if (captureSession && this.onCaptureMatches) { + try { + const matches = this.captureManager!.finalize(captureSession, 0); + await this.onCaptureMatches(this.stepCount, matches); + } finally { + captureSession[Symbol.dispose](); + } + captureSession = null; + } } finally { weakModeUntilAfterBackward[Symbol.dispose](); } @@ -538,6 +671,41 @@ export class TrainingSession { 'speed/tokens_per_second': tokensPerSecond, 'speed/wall_clock_seconds': totalElapsedSeconds }; + + if ( + loggingStep && + this.captureManager && + this.validationExamples && + this.validationCollateFn && + this.onCaptureMatches && + this.config.visualization.target === 'validation' + ) { + // Create session on top of weak mode for this step to capture forward ops + captureSession = this.captureManager.createSession(); + + await runValidationExampleForCapture( + this.model, + this.validationExamples, + this.validationCollateFn, + this.currentVisualizationSelectedValidation.exampleIndex + ); + + // runValidationExampleForCapture actually ends up… training on the validation set, + // so we need to zero out the gradients here + this.optimizer.zeroGrad(true); + + try { + const matches = this.captureManager!.finalize( + captureSession!, + this.currentVisualizationSelectedValidation.exampleIndex + ); + await this.onCaptureMatches(this.stepCount, matches); + } finally { + captureSession![Symbol.dispose](); + } + captureSession = null; + } + // Log current learning rate if scheduler is present const currentLr = this.optimizer.paramGroups[0].lr; if (currentLr) { diff --git a/examples/piston-train-toy/src/lib/train/utils/model.ts b/examples/piston-train-toy/src/lib/train/utils/model.ts index 8595ffee..c3996f6a 100644 --- a/examples/piston-train-toy/src/lib/train/utils/model.ts +++ b/examples/piston-train-toy/src/lib/train/utils/model.ts @@ -1,6 +1,6 @@ import type { Random } from 'random-js'; -import { DataLoader, Tensor, weak } from '@piston-ml/piston-web'; +import { CaptureIndexMode, DataLoader, type IndexState, Tensor, weak } from '@piston-ml/piston-web'; import * as piston from '@piston-ml/piston-web'; import type { Config } from '../../workspace/config'; @@ -245,6 +245,7 @@ export function countParameters( */ export function inspectModel(config: Config): { parameterCount: number; + modelIndex: IndexState; vocabSize: number; blockSize: number; } { @@ -260,31 +261,38 @@ export function inspectModel(config: Config): { const model = createModel(config, vocabSize, blockSizeOrSizes); const parameterCount = countParameters(model); - // Run the model forward with an input from the dataloader - if (model instanceof DecoderTransformer) { - model.forward(piston.zeros([1, blockSizeOrSizes as number], { dtype: piston.int32 })); - } else if (model instanceof EncoderTransformer) { - model.forward(piston.zeros([1, blockSizeOrSizes as number], { dtype: piston.int32 })); - } else if (model instanceof EncoderDecoderTransformer) { - model.forward( - piston.zeros([1, (blockSizeOrSizes as EncoderDecoderBlockSize).source], { - dtype: piston.int32 - }), - piston.zeros([1, (blockSizeOrSizes as EncoderDecoderBlockSize).target], { - dtype: piston.int32 - }) - ); - } + let indexMode: CaptureIndexMode | null = null; + try { + indexMode = new CaptureIndexMode(model); + + // Run the model forward with an input from the dataloader + if (model instanceof DecoderTransformer) { + model.forward(piston.zeros([1, blockSizeOrSizes as number], { dtype: piston.int32 })); + } else if (model instanceof EncoderTransformer) { + model.forward(piston.zeros([1, blockSizeOrSizes as number], { dtype: piston.int32 })); + } else if (model instanceof EncoderDecoderTransformer) { + model.forward( + piston.zeros([1, (blockSizeOrSizes as EncoderDecoderBlockSize).source], { + dtype: piston.int32 + }), + piston.zeros([1, (blockSizeOrSizes as EncoderDecoderBlockSize).target], { + dtype: piston.int32 + }) + ); + } - console.debug(`Model has ${parameterCount} parameters with vocab size ${vocabSize}`); + console.debug(`Model has ${parameterCount} parameters with vocab size ${vocabSize}`); - const blockSize = isEncoderDecoder - ? Math.max( - (blockSizeOrSizes as { source: number; target: number }).source, - (blockSizeOrSizes as { source: number; target: number }).target - ) - : (blockSizeOrSizes as number); - return { parameterCount, vocabSize, blockSize }; + const blockSize = isEncoderDecoder + ? Math.max( + (blockSizeOrSizes as { source: number; target: number }).source, + (blockSizeOrSizes as { source: number; target: number }).target + ) + : (blockSizeOrSizes as number); + return { parameterCount, vocabSize, blockSize, modelIndex: indexMode!.index }; + } finally { + indexMode![Symbol.dispose](); + } }, { label: 'inspectModel' diff --git a/examples/piston-train-toy/src/lib/train/visualizer.ts b/examples/piston-train-toy/src/lib/train/visualizer.ts new file mode 100644 index 00000000..a3656dc3 --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/visualizer.ts @@ -0,0 +1,1006 @@ +import { type IRectangle, MaxRectsPacker, PACKING_LOGIC } from 'maxrects-packer'; + +import { type CaptureMatch } from './capture'; + +export type MatchBox = { + matchId: number; + x: number; + y: number; + width: number; + height: number; + tileWidth: number; + tileHeight: number; + gridRows: number; + gridCols: number; + match: CaptureMatch; +}; + +export type MatchStats = { mean: number | null; variance: number | null } | null; + +type PreparedMatch = { + match: CaptureMatch & { buffer: GPUBuffer }; + gridRows: number; + gridCols: number; + tileWidth: number; + tileHeight: number; + width: number; + height: number; + scale: number; +}; + +type PackedRect = IRectangle & { id?: number; matchId: number }; + +const PADDING_PX = 4; +const GLOBAL_PADDING_PX = 3; + +export class Visualizer { + private device: GPUDevice; + private context: GPUCanvasContext | null = null; + private canvas: OffscreenCanvas | null = null; + private canvasFormat: GPUTextureFormat = 'bgra8unorm'; + private storageFormat: GPUTextureFormat = 'rgba8unorm'; + private composeTexture: GPUTexture | null = null; + private composeView: GPUTextureView | null = null; + private blitPipeline: GPURenderPipeline | null = null; + private computePipeline: GPUComputePipeline | null = null; + private reducePipelineStage1: GPUComputePipeline | null = null; + private reducePipelineStage2: GPUComputePipeline | null = null; + private blitBindGroupLayout: GPUBindGroupLayout | null = null; + private blitSampler: GPUSampler | null = null; + private cssLabelPaddingPx: number = 0; + private targetWidth: number = 1; + private targetHeight: number = 1; + private maxTextureDim: number = 8192; + private needsResize: boolean = false; + + constructor(device: GPUDevice) { + this.device = device; + } + + init(canvas: OffscreenCanvas, format?: GPUTextureFormat) { + this.canvas = canvas; + const context = canvas.getContext('webgpu') as unknown as GPUCanvasContext | null; + if (!context) { + throw new Error('OffscreenCanvas WebGPU context not available'); + } + this.context = context; + this.canvasFormat = format ?? navigator.gpu.getPreferredCanvasFormat(); + // Use negotiated device limit (not adapter), to avoid validation errors + this.maxTextureDim = Math.max(1, this.device.limits.maxTextureDimension2D | 0); + + context.configure({ + device: this.device, + format: this.canvasFormat, + alphaMode: 'premultiplied' + }); + + this.recreateComposeTargets(); + this.ensurePipelines(); + } + + setCssLabelPadding(pixels: number) { + this.cssLabelPaddingPx = Math.max(0, Math.floor(pixels || 0)); + } + + resize(width: number) { + if (!this.canvas) return; + // Clamp to device limits to prevent creating oversized textures + const clampedW = Math.max(1, Math.min(width | 0, this.maxTextureDim)); + // We intentionally ignore incoming height and derive it from content during render. + if (this.targetWidth === clampedW) return; + this.targetWidth = clampedW; + this.needsResize = true; + } + + private recreateComposeTargets() { + if (!this.canvas) return; + this.composeTexture?.destroy(); + this.composeTexture = this.device.createTexture({ + size: { width: this.canvas.width, height: this.canvas.height }, + format: this.storageFormat, + usage: + GPUTextureUsage.RENDER_ATTACHMENT | + GPUTextureUsage.TEXTURE_BINDING | + GPUTextureUsage.COPY_SRC | + GPUTextureUsage.STORAGE_BINDING + }); + this.composeView = this.composeTexture.createView(); + } + + private ensurePipelines() { + if (!this.blitPipeline) { + this.blitBindGroupLayout = this.device.createBindGroupLayout({ + entries: [ + { binding: 0, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: 'float' } }, + { binding: 1, visibility: GPUShaderStage.FRAGMENT, sampler: { type: 'filtering' } } + ] + }); + const pipelineLayout = this.device.createPipelineLayout({ + bindGroupLayouts: [this.blitBindGroupLayout] + }); + this.blitPipeline = this.device.createRenderPipeline({ + layout: pipelineLayout, + vertex: { + module: this.device.createShaderModule({ code: BLIT_WGSL }), + entryPoint: 'vsMain' + }, + fragment: { + module: this.device.createShaderModule({ code: BLIT_WGSL }), + entryPoint: 'fsMain', + targets: [{ format: this.canvasFormat }] + }, + primitive: { topology: 'triangle-list' } + }); + this.blitSampler = this.device.createSampler({ magFilter: 'nearest', minFilter: 'nearest' }); + } + + if (!this.computePipeline) { + const layout = this.device.createBindGroupLayout({ + entries: [ + { binding: 0, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'read-only-storage' } }, + { + binding: 1, + visibility: GPUShaderStage.COMPUTE, + storageTexture: { format: this.storageFormat, access: 'write-only' } + }, + { binding: 2, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'uniform' } }, + { binding: 3, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'read-only-storage' } } + ] + }); + const pipelineLayout = this.device.createPipelineLayout({ bindGroupLayouts: [layout] }); + this.computePipeline = this.device.createComputePipeline({ + layout: pipelineLayout, + compute: { + module: this.device.createShaderModule({ code: COPY_COMPUTE_WGSL }), + entryPoint: 'main' + } + }); + } + + // Reduction pipelines + if (!this.reducePipelineStage1) { + const layout1 = this.device.createBindGroupLayout({ + entries: [ + { binding: 0, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'read-only-storage' } }, + { binding: 1, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'storage' } }, + { binding: 2, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'uniform' } } + ] + }); + const pl1 = this.device.createPipelineLayout({ bindGroupLayouts: [layout1] }); + this.reducePipelineStage1 = this.device.createComputePipeline({ + layout: pl1, + compute: { + module: this.device.createShaderModule({ code: REDUCE_STAGE1_WGSL }), + entryPoint: 'main' + } + }); + } + + if (!this.reducePipelineStage2) { + const layout2 = this.device.createBindGroupLayout({ + entries: [ + { binding: 0, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'read-only-storage' } }, + { binding: 1, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'storage' } }, + { binding: 2, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'uniform' } } + ] + }); + const pl2 = this.device.createPipelineLayout({ bindGroupLayouts: [layout2] }); + this.reducePipelineStage2 = this.device.createComputePipeline({ + layout: pl2, + compute: { + module: this.device.createShaderModule({ code: REDUCE_STAGE2_WGSL }), + entryPoint: 'main' + } + }); + } + } + + private prepareMatches(matches: CaptureMatch[]): PreparedMatch[] { + const prepared: PreparedMatch[] = []; + for (const match of matches) { + if (!match.tensor) continue; + + const dims = match.type === 'parameter' ? match.shape : match.shape.slice(1); + + let tileWidth = 1; + let tileHeight = 1; + let gridRows = 1; + let gridCols = 1; + + if (dims.length === 0) { + tileWidth = 1; + tileHeight = 1; + gridRows = 1; + gridCols = 1; + } else if (dims.length === 1) { + tileWidth = dims[0]; + tileHeight = 1; + gridRows = 1; + gridCols = 1; + } else if (dims.length === 2) { + tileHeight = dims[0]; + tileWidth = dims[1]; + gridRows = 1; + gridCols = 1; + } else { + // Lay out last two dims as 2D tile, third-from-last horizontally, + // and any remaining leading dims stacked vertically. + tileHeight = dims[dims.length - 2]; + tileWidth = dims[dims.length - 1]; + gridCols = dims[dims.length - 3]; + gridRows = dims.slice(0, Math.max(0, dims.length - 3)).reduce((a, b) => a * b, 1); + } + + const width = gridCols * tileWidth; + const height = gridRows * tileHeight; + const scale = match.source.scale ?? 1.0; + + // Use precomputed buffer if available + const buffer = match.buffer ?? match.tensor.gpuBuffer(); + match.tensor.__pistonDrop(); + if (!buffer) continue; + + prepared.push({ + match: { ...match, buffer } as CaptureMatch & { buffer: GPUBuffer }, + gridRows, + gridCols, + tileWidth, + tileHeight, + width, + height, + scale + }); + } + return prepared; + } + + private layoutMatches(prepared: PreparedMatch[]): MatchBox[] { + const boxes: MatchBox[] = []; + if (prepared.length === 0) return boxes; + + const labelCss = this.cssLabelPaddingPx | 0; + const PAD = PADDING_PX | 0; + const HALF_PAD = Math.floor(PAD / 2); + + // Fix the container width to the current canvas width; height very large so we + // never overflow horizontally. Actual drawing will be clipped to the canvas height. + const canvasW = (this.canvas?.width ?? this.targetWidth ?? 1) | 0; + const containerInnerW = Math.max(1, canvasW - (GLOBAL_PADDING_PX << 1)); + const containerH = 1 << 30; // arbitrarily tall + + // Build batch rectangles for addArray, inflating each rectangle by PADDING_PX/2 on all sides, + // and including the label vertical space above the tile. + const rects: PackedRect[] = []; + const dimsById = new Map>(); + + // Helper to count power-of-two factors for 2-adic rewrapping + const v2 = (n: number): number => { + let c = 0; + let x = Math.max(1, n | 0); + while ((x & 1) === 0) { + x >>= 1; + c++; + } + return c; + }; + + for (const p of prepared) { + const baseTileWidth = p.tileWidth | 0; + const baseTileHeight = p.tileHeight | 0; + const baseRows = p.gridRows | 0; + const baseCols = p.gridCols | 0; + const scaledW0 = Math.ceil(p.width * p.scale); + + // Compute required halving power to fit within inner container width (including padding) + const needFactor = Math.max(1, (scaledW0 + PAD) / containerInnerW); + const nNeeded = Math.max(0, Math.ceil(Math.log2(needFactor))); + + // Capacity from 2-adic factors of tile width and grid columns + const aMax = v2(baseTileWidth); + const bMax = v2(baseCols); + const nCap = aMax + bMax; + const nUse = Math.min(nNeeded, nCap); + const a = Math.min(nUse, aMax); + const b = Math.min(nUse - a, bMax); + + const adjTileWidth = Math.max(1, baseTileWidth >> a); + const adjTileHeight = Math.max(1, baseTileHeight << a); + const adjCols = Math.max(1, baseCols >> b); + const adjRows = Math.max(1, baseRows << b); + + const scaledWidth = Math.ceil(adjCols * adjTileWidth * p.scale); + const scaledHeight = Math.ceil(adjRows * adjTileHeight * p.scale); + + const reqW = Math.max(1, scaledWidth + PAD); + const reqH = Math.max(1, scaledHeight + labelCss + PAD); + const qid = p.match.queryIndex; + rects.push({ + x: 0, + y: 0, + width: reqW, + height: reqH, + // id for caller use; set to queryIndex per request + id: qid, + // preserve original match id for mapping back to dims later + matchId: p.match.matchId + }); + dimsById.set(p.match.matchId, { + width: scaledWidth, + height: scaledHeight, + match: p.match, + tileWidth: adjTileWidth, + tileHeight: adjTileHeight, + gridRows: adjRows, + gridCols: adjCols + }); + } + + // Use MaxRectsPacker to pack within fixed width and very tall height + const packer = new MaxRectsPacker(containerInnerW, containerH, 0, { + smart: false, + pot: true, + square: false, + allowRotation: false, + tag: false, + border: 0, + logic: PACKING_LOGIC.MAX_EDGE + }); + packer.addArray(rects); + const posById = new Map(); + for (const bin of packer.bins) { + for (const rect of bin.rects) { + const mid = rect.matchId; + posById.set(mid, { x: rect.x | 0, y: rect.y | 0 }); + } + } + + // Preserve original order to match prepared[i] with boxes[i] + for (const p of prepared) { + const dims = dimsById.get(p.match.matchId)!; + const pos = posById.get(p.match.matchId); + + const x = GLOBAL_PADDING_PX + ((pos?.x ?? 0) | 0) + HALF_PAD; + const y = GLOBAL_PADDING_PX + ((pos?.y ?? 0) | 0) + HALF_PAD + labelCss; + + boxes.push({ + matchId: p.match.matchId, + match: p.match, + x, + y, + width: dims.width, + height: dims.height, + tileWidth: dims.tileWidth, + tileHeight: dims.tileHeight, + gridRows: dims.gridRows, + gridCols: dims.gridCols + }); + } + + return boxes; + } + + async renderCapture(matches: CaptureMatch[]): Promise<{ + boxes: MatchBox[]; + statsById: Record; + width: number; + height: number; + }> { + if (!this.context || !this.canvas || !this.composeTexture || !this.composeView) + return { boxes: [], statsById: {}, width: 1, height: 1 }; + + // Apply pending resize (width only) before computing layout + if (this.needsResize && this.canvas) { + this.canvas.width = this.targetWidth; + // We delay height adjustment until after computing layout + this.recreateComposeTargets(); + this.needsResize = false; + } + + this.ensurePipelines(); + + // Track transient per-frame buffers to explicitly destroy after GPU work completes + const transientBuffers: GPUBuffer[] = []; + + const prepared = this.prepareMatches(matches); + const boxes = this.layoutMatches(prepared); + + // Derive canvas height from content (max y + height), clamped to device limits + let derivedHeight = 1; + for (const b of boxes) { + const bottom = (b.y | 0) + (b.height | 0); + if (bottom > derivedHeight) derivedHeight = bottom; + } + derivedHeight = Math.max( + 1, + // Add a small global padding at bottom + Math.min((derivedHeight + (GLOBAL_PADDING_PX + PADDING_PX / 2)) | 0, this.maxTextureDim) + ); + if ((this.canvas.height | 0) !== derivedHeight) { + this.canvas.height = derivedHeight; + // Recreate compose targets to match new height + this.recreateComposeTargets(); + } + + const readbacks: { buffer: GPUBuffer; matchId: number }[] = []; + + const encoder = this.device.createCommandEncoder(); + let submissionSucceeded = false; + + try { + // Clear compose target + { + const pass = encoder.beginRenderPass({ + colorAttachments: [ + { + view: this.composeView, + clearValue: { r: 0, g: 0, b: 0, a: 1 }, + loadOp: 'clear', + storeOp: 'store' + } + ] + }); + pass.end(); + } + + // For each match: copy tensor slice to a readable storage buffer, run GPU reduction to + // compute mean/variance, then run compute to paint into compose texture using the computed + // stats + for (let i = 0; i < prepared.length; i++) { + const p = prepared[i]; + const box = boxes[i]; + + const elementCount = Math.max( + 1, + box.gridRows * box.gridCols * box.tileWidth * box.tileHeight + ); + const byteSize = elementCount * 4; + + const readable = this.device.createBuffer({ + size: byteSize, + usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST + }); + transientBuffers.push(readable); + + // Copy the selected batch index for non-parameter tensors; parameters ignore batchIndex + const isParameter = p.match.type === 'parameter'; + const batchCount = isParameter ? 1 : Math.max(1, p.match.shape[0] | 0); + const requestedBatchIndex = isParameter ? 0 : (p.match.batchIndex ?? 0); + const clampedBatchIndex = isParameter + ? 0 + : Math.max(0, Math.min(batchCount - 1, requestedBatchIndex | 0)); + const offsetBytes = isParameter ? 0 : (clampedBatchIndex * elementCount * 4) >>> 0; + const maxBytes = Math.max(0, p.match.buffer.size - offsetBytes); + const copyBytes = Math.min(byteSize, maxBytes); + if (copyBytes > 0) { + encoder.copyBufferToBuffer(p.match.buffer, offsetBytes, readable, 0, copyBytes); + } + + // Reduction to compute mean/variance on GPU + const WG_SIZE = 256; + const ELEMENTS_PER_THREAD = 4; + const perGroup = WG_SIZE * ELEMENTS_PER_THREAD; + const numPartials = Math.max(1, Math.ceil(elementCount / perGroup)); + + const partialsBuffer = this.device.createBuffer({ + size: numPartials * 8, + usage: GPUBufferUsage.STORAGE + }); + transientBuffers.push(partialsBuffer); + + const finalStats = this.device.createBuffer({ + size: 12, + usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC + }); + transientBuffers.push(finalStats); + + const reduceParams = new Uint32Array([ + elementCount >>> 0, + numPartials >>> 0, + WG_SIZE >>> 0, + ELEMENTS_PER_THREAD >>> 0 + ]); + const reduceUniform = this.device.createBuffer({ + size: Math.ceil(reduceParams.byteLength / 16) * 16, + usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST + }); + transientBuffers.push(reduceUniform); + this.device.queue.writeBuffer( + reduceUniform, + 0, + reduceParams.buffer, + reduceParams.byteOffset, + reduceParams.byteLength + ); + + // Stage 1 reduction + { + const bg = this.device.createBindGroup({ + layout: this.reducePipelineStage1!.getBindGroupLayout(0), + entries: [ + { binding: 0, resource: { buffer: readable } }, + { binding: 1, resource: { buffer: partialsBuffer } }, + { binding: 2, resource: { buffer: reduceUniform } } + ] + }); + const pass = encoder.beginComputePass(); + pass.setPipeline(this.reducePipelineStage1!); + pass.setBindGroup(0, bg); + pass.dispatchWorkgroups(numPartials); + pass.end(); + } + + // Stage 2 reduction + { + const bg = this.device.createBindGroup({ + layout: this.reducePipelineStage2!.getBindGroupLayout(0), + entries: [ + { binding: 0, resource: { buffer: partialsBuffer } }, + { binding: 1, resource: { buffer: finalStats } }, + { binding: 2, resource: { buffer: reduceUniform } } + ] + }); + const pass = encoder.beginComputePass(); + pass.setPipeline(this.reducePipelineStage2!); + pass.setBindGroup(0, bg); + pass.dispatchWorkgroups(1); + pass.end(); + } + + // Copy stats to a CPU-readable buffer + const readback = this.device.createBuffer({ + size: 12, + usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST + }); + readbacks.push({ buffer: readback, matchId: p.match.matchId }); + encoder.copyBufferToBuffer(finalStats, 0, readback, 0, 12); + + // Paint compute using computed stats + const scale = Math.fround(p.scale); + const scaleU32 = new Uint32Array(new Float32Array([scale]).buffer)[0]; + const eps = Math.fround(1e-6); + const epsU32 = new Uint32Array(new Float32Array([eps]).buffer)[0]; + const uniformData = new Uint32Array([ + box.x >>> 0, + box.y >>> 0, + box.tileWidth >>> 0, + box.tileHeight >>> 0, + box.gridRows >>> 0, + box.gridCols >>> 0, + scaleU32 >>> 0, + epsU32 >>> 0 + ]); + const uniform = this.device.createBuffer({ + size: Math.ceil(uniformData.byteLength / 16) * 16, + usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST + }); + transientBuffers.push(uniform); + this.device.queue.writeBuffer( + uniform, + 0, + uniformData.buffer, + uniformData.byteOffset, + uniformData.byteLength + ); + + const bindGroup = this.device.createBindGroup({ + layout: this.computePipeline!.getBindGroupLayout(0), + entries: [ + { binding: 0, resource: { buffer: readable } }, + { binding: 1, resource: this.composeView as GPUTextureView }, + { binding: 2, resource: { buffer: uniform } }, + { binding: 3, resource: { buffer: finalStats } } + ] + }); + + const pass = encoder.beginComputePass(); + pass.setPipeline(this.computePipeline!); + pass.setBindGroup(0, bindGroup); + const renderWidth = box.width | 0; + const renderHeight = box.height | 0; + // Clip dispatch region to current compose texture bounds to avoid OOB writes + const targetW = this.canvas.width | 0; + const targetH = this.canvas.height | 0; + const clipW = Math.max(0, Math.min(renderWidth, Math.max(0, targetW - box.x))); + const clipH = Math.max(0, Math.min(renderHeight, Math.max(0, targetH - box.y))); + if (clipW <= 0 || clipH <= 0) { + pass.end(); + continue; + } + const dispatchX = Math.ceil(clipW / 8); + const dispatchY = Math.ceil(clipH / 8); + pass.dispatchWorkgroups(dispatchX, dispatchY); + pass.end(); + } + + // Blit compose texture to the current swapchain texture + const currentTexture = this.context.getCurrentTexture(); + const currentView = currentTexture.createView(); + + const blitBindGroup = this.device.createBindGroup({ + layout: this.blitBindGroupLayout!, + entries: [ + { binding: 0, resource: this.composeView as GPUTextureView }, + { binding: 1, resource: this.blitSampler as GPUSampler } + ] + }); + + { + const pass = encoder.beginRenderPass({ + colorAttachments: [ + { + view: currentView, + clearValue: { r: 0, g: 0, b: 0, a: 1 }, + loadOp: 'clear', + storeOp: 'store' + } + ] + }); + pass.setPipeline(this.blitPipeline!); + pass.setBindGroup(0, blitBindGroup); + pass.draw(6, 1, 0, 0); + pass.end(); + } + + this.device.queue.submit([encoder.finish()]); + submissionSucceeded = true; + } catch (_err) { + // If we failed before submitting GPU work, destroy transient buffers immediately + if (!submissionSucceeded) { + for (const buf of transientBuffers) { + try { + buf.destroy(); + } catch (_e) { + /* ignore */ + } + } + } + throw _err; + } + + // Wait for GPU work to complete before mapping readback buffers + await this.device.queue.onSubmittedWorkDone(); + + // Schedule explicit destruction of transient buffers once the GPU has + // finished consuming this submission. This avoids leaking GPU memory + // across frames if GC is delayed. + void this.device.queue.onSubmittedWorkDone().then(() => { + for (const buf of transientBuffers) { + try { + buf.destroy(); + } catch (_e) { + // Ignore destruction errors; resource may already be collected + } + } + }); + + const statsById: Record = {}; + for (const rb of readbacks) { + try { + await rb.buffer.mapAsync(GPUMapMode.READ); + const range = rb.buffer.getMappedRange(); + const f32 = new Float32Array(range.slice(0)); + statsById[rb.matchId] = { mean: f32[0] ?? 0, variance: f32[1] ?? 1, l2: f32[2] ?? 0 }; + rb.buffer.unmap(); + rb.buffer.destroy(); + } catch (_e) { + // Ignore mapping errors for individual buffers + try { + rb.buffer.unmap(); + } catch (__ignore) { + void __ignore; + } + try { + rb.buffer.destroy(); + } catch (__ignore2) { + void __ignore2; + } + } + } + + return { + boxes, + statsById, + width: this.canvas?.width ?? 1, + height: this.canvas?.height ?? 1 + }; + } + + dispose() { + // Destroy long-lived GPU resources and release references + try { + this.composeTexture?.destroy(); + } catch (_e) { + void _e; + } + try { + this.context?.unconfigure(); + } catch (_e) { + void _e; + } + + this.composeTexture = null; + this.composeView = null; + this.blitPipeline = null; + this.computePipeline = null; + this.blitBindGroupLayout = null; + this.blitSampler = null; + this.context = null; + this.canvas = null; + } +} + +const BLIT_WGSL = /* wgsl */ ` +struct VSOut { + @builtin(position) pos : vec4f, + @location(0) uv : vec2f, +}; + +@vertex +fn vsMain(@builtin(vertex_index) vid: u32) -> VSOut { + var positions = array( + vec2f(-1.0, -1.0), vec2f(1.0, -1.0), vec2f(-1.0, 1.0), + vec2f(-1.0, 1.0), vec2f(1.0, -1.0), vec2f(1.0, 1.0) + ); + var uvs = array( + vec2f(0.0, 1.0), vec2f(1.0, 1.0), vec2f(0.0, 0.0), + vec2f(0.0, 0.0), vec2f(1.0, 1.0), vec2f(1.0, 0.0) + ); + var out: VSOut; + out.pos = vec4f(positions[vid], 0.0, 1.0); + out.uv = uvs[vid]; + return out; +} + +@group(0) @binding(0) var img : texture_2d; +@group(0) @binding(1) var samp : sampler; + +@fragment +fn fsMain(in: VSOut) -> @location(0) vec4f { + return textureSample(img, samp, in.uv); +} +`; + +// Stage 1: parallel partials of sum and sum of squares +const REDUCE_STAGE1_WGSL = /* wgsl */ ` +@group(0) @binding(0) var src : array; +@group(0) @binding(1) var partials : array>; + +struct Params { + n : u32, + numPartials : u32, + wgSize : u32, + elemsPerThread : u32, +}; + +@group(0) @binding(2) var P : Params; + +var ssum : array; +var ssum2 : array; + +@compute @workgroup_size(256) +fn main(@builtin(local_invocation_id) lid: vec3, @builtin(workgroup_id) wid: vec3) { + let groupIndex = wid.x; + let localIndex = lid.x; + let start = groupIndex * (P.wgSize * P.elemsPerThread) + localIndex; + + var sum : f32 = 0.0; + var sum2 : f32 = 0.0; + for (var i: u32 = 0u; i < P.elemsPerThread; i = i + 1u) { + let idx = start + i * P.wgSize; + if (idx < P.n) { + let v = src[idx]; + sum = sum + v; + sum2 = sum2 + v * v; + } + } + + ssum[localIndex] = sum; + ssum2[localIndex] = sum2; + workgroupBarrier(); + + var offset : u32 = 256u / 2u; + loop { + if (offset == 0u) { break; } + if (localIndex < offset) { + ssum[localIndex] = ssum[localIndex] + ssum[localIndex + offset]; + ssum2[localIndex] = ssum2[localIndex] + ssum2[localIndex + offset]; + } + workgroupBarrier(); + offset = offset / 2u; + } + + if (localIndex == 0u) { + partials[groupIndex] = vec2(ssum[0], ssum2[0]); + } +} +`; + +// Stage 2: finalize mean and variance from partials +const REDUCE_STAGE2_WGSL = /* wgsl */ ` +@group(0) @binding(0) var partials : array>; +@group(0) @binding(1) var outStats : array; + +struct Params { + n : u32, + numPartials : u32, + wgSize : u32, + elemsPerThread : u32, +}; + +@group(0) @binding(2) var P : Params; + +var ssum : array; +var ssum2 : array; + +@compute @workgroup_size(256) +fn main(@builtin(local_invocation_id) lid: vec3) { + let localIndex = lid.x; + var sum : f32 = 0.0; + var sum2 : f32 = 0.0; + for (var i: u32 = localIndex; i < P.numPartials; i = i + 256u) { + let p = partials[i]; + sum = sum + p.x; + sum2 = sum2 + p.y; + } + ssum[localIndex] = sum; + ssum2[localIndex] = sum2; + workgroupBarrier(); + + var offset : u32 = 256u / 2u; + loop { + if (offset == 0u) { break; } + if (localIndex < offset) { + ssum[localIndex] = ssum[localIndex] + ssum[localIndex + offset]; + ssum2[localIndex] = ssum2[localIndex] + ssum2[localIndex + offset]; + } + workgroupBarrier(); + offset = offset / 2u; + } + + if (localIndex == 0u) { + let n = max(1.0, f32(P.n)); + let mean = ssum[0] / n; + let ex2 = ssum2[0] / n; + let variance = max(0.0, ex2 - mean * mean); + let l2 = sqrt(ssum2[0]); + outStats[0] = mean; + outStats[1] = variance; + outStats[2] = l2; + } +} +`; + +const COPY_COMPUTE_WGSL = /* wgsl */ ` +@group(0) @binding(0) var src : array; +@group(0) @binding(1) var outImg : texture_storage_2d; + +struct Uniforms { + originX : u32, + originY : u32, + tileW : u32, + tileH : u32, + gridRows : u32, + gridCols : u32, + scaleBits : u32, + epsBits : u32, +}; + +@group(0) @binding(2) var U : Uniforms; +@group(0) @binding(3) var stats : array; + +fn f32_from_bits(u: u32) -> f32 { + return bitcast(u); +} + +fn gamma_correct(v: vec3) -> vec3 { + let sign = select(vec3(-1.0), vec3(1.0), v >= vec3(0.0)); + let abs_v = abs(v); + + let power_term = sign * (1.055 * pow(abs_v, vec3(1.0/2.4)) - 0.055); + let linear_term = 12.92 * v; + + return select(linear_term, power_term, abs_v > vec3(0.0031308)); +} + +fn oklch_to_srgb(oklch: vec3) -> vec3 { + // Convert OKLCH to OKLab + let L = oklch.x; + let C = oklch.y; + let H = radians(oklch.z); + + let oklab = vec3( + L, + C * cos(H), + C * sin(H) + ); + + // Combined matrices from Python calculation + let oklab_to_lms = mat3x3( + 1.0000000000000000, 1.0000000000000000, 1.0000000000000000, + 0.3963377773761749, -0.1055613458156586, -0.0894841775298119, + 0.2158037573099136, -0.0638541728258133, -1.2914855480194092 + ); + + // Combined xyz_to_rgb * lms_to_xyz matrix + let xyz_rgb_lms = mat3x3( + 4.0767416360759583, -1.2684379732850317, -0.0041960761386756, + -3.3077115392580616, 2.6097573492876887, -0.7034186179359363, + 0.2309699031821043, -0.3413193760026573, 1.7076146940746117 + ); + + // Convert OKLab to LMS + let lms = oklab_to_lms * oklab; + + // Apply cubic transform + let lms_cubic = pow(lms, vec3(3.)); + + // Convert directly to RGB using combined matrix + let rgb_linear = xyz_rgb_lms * lms_cubic; + + // Apply gamma correction + return gamma_correct(rgb_linear); +} + +fn get_color(x: f32) -> vec3 { + let color1_pos = vec3f(0.0, 0.05, 20); + let color2_pos = vec3f(0.62, 0.18, 41); + let color1_neg = vec3f(0, 0.05, 247); + let color2_neg = vec3f(0.62, 0.18, 267); + + // x is now in terms of standard deviations + if (x < 0.0) { + return mix(color1_neg, color2_neg, -x / 2.8); + } else { + return mix(color1_pos, color2_pos, x / 2.8); + } +} + +@compute @workgroup_size(8, 8, 1) +fn main(@builtin(global_invocation_id) gid: vec3) { + let scale = max(0.0001, f32_from_bits(U.scaleBits)); + let eps = f32_from_bits(U.epsBits); + let mean = stats[0u]; + let variance = stats[1u]; + let l2 = stats[2u]; + + // Local pixel within this tile's box + let lx = i32(gid.x); + let ly = i32(gid.y); + + // Map local pixel to logical tensor pixel using scale + let logicalX = i32(floor(f32(lx) / scale)); + let logicalY = i32(floor(f32(ly) / scale)); + + let totalW = i32(U.tileW * U.gridCols); + let totalH = i32(U.tileH * U.gridRows); + + if (logicalX >= totalW || logicalY >= totalH) { + return; + } + + let tileCol = logicalX / i32(U.tileW); + let tileRow = logicalY / i32(U.tileH); + let pxX = logicalX % i32(U.tileW); + let pxY = logicalY % i32(U.tileH); + + let leadingIndex = tileRow * i32(U.gridCols) + tileCol; + let index = leadingIndex * i32(U.tileW) * i32(U.tileH) + pxY * i32(U.tileW) + pxX; + + let v = src[index]; + + // z-score color mapping computed on-GPU: invStd = rsqrt(var + eps) + let invStd = inverseSqrt(max(0.0, variance + eps)); + let z = (v - mean) * invStd; + // let z = v * invStd; + // let zc = clamp(z, -3.0, 3.0) / 3.0; + // let blue = max(0.0, zc); + // let red = max(0.0, -zc); + // let color = vec4(red, 0.0, blue, 1.0); + let color = vec4(oklch_to_srgb(get_color(z)), 1.0); + + // Write to destination at tile origin offset + let outX = U.originX + gid.x; + let outY = U.originY + gid.y; + textureStore(outImg, vec2u(outX, outY), color); +} +`; diff --git a/examples/piston-train-toy/src/lib/workspace/config.svelte.ts b/examples/piston-train-toy/src/lib/workspace/config.svelte.ts index 354d2bd8..c4f4191f 100644 --- a/examples/piston-train-toy/src/lib/workspace/config.svelte.ts +++ b/examples/piston-train-toy/src/lib/workspace/config.svelte.ts @@ -8,6 +8,7 @@ import { seededRandom } from '$lib/train/utils/random'; import { SvelteURL, SvelteURLSearchParams } from 'svelte/reactivity'; import { getCurrentRun, getLatestRun } from './runs.svelte'; +import { getVisualizationExampleOptions } from './visualizationExamples'; export const MODEL_TYPES = [ 'decoder', @@ -48,6 +49,7 @@ const CONFIG_DEFAULTS: Config = { sharedObjectAllocation: false, cachingEnabled: false, inplaceSupport: true, + enableVisualization: true, vramLimitMb: { present: true, value: 4096 @@ -171,6 +173,15 @@ const CONFIG_DEFAULTS: Config = { nesterov: true } }, + visualization: { + script: null, + example: 'attention-probabilities', + target: 'validation', + selectedValidation: { + exampleIndex: 0, + tokenIndex: 0 + } + }, version: 1 }; @@ -505,6 +516,21 @@ export function getMlpIntermediateSize() { } export function validateConfig() { + // There are a few things that can still slip through the cracks, so we deal with those here. + + if ( + config.visualization.selectedValidation.exampleIndex >= config.training.validation.batchSize + ) { + config.visualization.selectedValidation.exampleIndex = 0; + } + + if ( + config.visualization.example !== 'custom' && + !getVisualizationExampleOptions().some((e) => e.value === config.visualization.example) + ) { + config.visualization.example = 'all-activations'; + } + if ( config.training.validation.completions.present && config.training.validation.completions.amount === 'subset' && diff --git a/examples/piston-train-toy/src/lib/workspace/config.ts b/examples/piston-train-toy/src/lib/workspace/config.ts index 30b63000..3ee746bb 100644 --- a/examples/piston-train-toy/src/lib/workspace/config.ts +++ b/examples/piston-train-toy/src/lib/workspace/config.ts @@ -62,6 +62,7 @@ export interface TrainingConfig { sharedObjectAllocation: boolean; cachingEnabled: boolean; inplaceSupport: boolean; + enableVisualization: boolean; } export interface TransformerAttentionConfig { @@ -190,10 +191,24 @@ export interface OptimizerConfig { }; } +export interface VisualizationConfig { + // If null, the effective script comes from the selected example + script: string | null; + // Selected example id or "custom" + example: string; + // current training step or the selected validation example + target: 'train' | 'validation'; + selectedValidation: { + exampleIndex: number; + tokenIndex: number; + }; +} + export interface Config { version: number; training: TrainingConfig; data: DataConfig; model: ModelConfig; optimizer: OptimizerConfig; + visualization: VisualizationConfig; } diff --git a/examples/piston-train-toy/src/lib/workspace/runs.svelte.ts b/examples/piston-train-toy/src/lib/workspace/runs.svelte.ts index 32bef70e..8b6527ca 100644 --- a/examples/piston-train-toy/src/lib/workspace/runs.svelte.ts +++ b/examples/piston-train-toy/src/lib/workspace/runs.svelte.ts @@ -1,3 +1,4 @@ +import type { CaptureStep } from '$lib/train/capture'; import type { ValidationStep } from '$lib/train/validation'; import { generateMemorableName } from '$lib/workspace/utils'; @@ -169,6 +170,8 @@ export function log( let stepData: StepData; if (typeof value === 'number') { stepData = { step: currentStep, y: value }; + } else if ('matches' in value) { + stepData = { step: currentStep, ...value } as CaptureStep; } else { stepData = { step: currentStep, ...value } as ValidationStep; } @@ -208,6 +211,12 @@ export function getMetricGroups(metricNames: ReadonlyArray): Record { + // For now we're just special-casing this, but we might want to bring it into the metrics view + // anyway + if (metricName === 'visualization/matches') { + return; + } + const parts = metricName.split('/'); const groupName = parts.length > 1 ? parts[0] : 'default'; diff --git a/examples/piston-train-toy/src/lib/workspace/visualizationExamples.ts b/examples/piston-train-toy/src/lib/workspace/visualizationExamples.ts new file mode 100644 index 00000000..fead26d3 --- /dev/null +++ b/examples/piston-train-toy/src/lib/workspace/visualizationExamples.ts @@ -0,0 +1,124 @@ +import type { Config } from './config'; + +export type VisualizationExample = { + label: string; + script: string; + predicate?: (config: Config) => boolean; +}; + +// Kitchen Sink example content taken from previous default, kept verbatim +const KITCHEN_SINK = `// Kitchen-sink Capture Query Language example + +// +// Retained gradient of first two heads of attention probabilities: +// +// 1. index 0 in the in the ModuleList named \`layer\`—the first decoder layer… +// 2. …with any descendant where the module class name matches the regex ./.*Attention/ (note the +// preceding \`.\`), not necessarily immediately… +// 3. …\`@\` ends the module selector and begins selecting operations… +// 4. …with Softmax immediately following WhereCond… +// 5. …where we call :grad(ient) to get the gradient… +// 6. …labeling the result with :label("Self-Attention Gradients")… +// 7. …using :scale(5) to scale the result by 5x… +// 8. …and we only want the first two heads, skipping the batch dimension. +layer[0] ./.*Attention/ @ WhereCond + Softmax :grad :label("self-attention $\\sigma$ gradients") :scale(5) [:,:2] + +// +// Positive values of input of every MLP module: +// +// 1. .MLP selects all modules with the class name \`MLP\`. +// 2. We can filter the captured value with JavaScript. In this case, the input +// to the module is a list of arguments, so we return the first argument before +// selecting for positive values. The input is passed in as the value \`it\`: +.MLP :input :scale(3) :label("mlp input ≥ 0") |{ + const tensor = it[0]; + return tensor.where(tensor.ge(0), 0); +} + +// +// Wildcard selector for every bias parameter +// +// +* #bias :label("all biases") :scale(3)`; + +const ATTENTION_PROBABILITIES = `./.*Attention/ @ Softmax :label("attention probabilities") :scale(5)`; +const ATTENTION_ACTIVATIONS = `layer[0] ./.*Attention/ @ * :label("attention activations") :scale(2)`; +const ATTENTION_GRADIENTS = `layer[0] ./.*Attention/ @ * :grad :label("attention gradients") :scale(2)`; +const ATTENTION_PARAMETERS = `layer[0] ./.*Attention/ * #* :label("attention weights (descendants)") :scale(2) +layer[0] ./.*Attention/ #* :label("attention weights") :scale(2)`; + +const MLP_ACTIVATIONS = `layer[0] .MLP @ * :label("mlp activations") :scale(2)`; + +export const VISUALIZATION_EXAMPLES: Record = { + 'attention-probabilities': { + label: 'Attention Probabilities', + script: ATTENTION_PROBABILITIES + }, + 'attention-activations': { + label: 'Attention Activations', + script: ATTENTION_ACTIVATIONS + }, + 'attention-gradients': { + label: 'Attention Gradients', + script: ATTENTION_GRADIENTS + }, + 'attention-parameters': { + label: 'Attention Parameters', + script: ATTENTION_PARAMETERS + }, + 'mlp activations': { + label: 'MLP Activations', + script: MLP_ACTIVATIONS + }, + 'kitchen-sink': { label: 'Kitchen Sink', script: KITCHEN_SINK }, + 'all-activations': { + label: 'All Activations', + script: '* @ * :scale(3)' + }, + 'all-gradients': { + label: 'All Gradients', + script: '* @ * :grad :scale(3)' + }, + 'all-parameters': { + label: 'All Parameters', + script: '* # * :scale(3)' + } +}; + +export const VISUALIZATION_EXAMPLES_BY_SCRIPT = new Map( + Object.entries(VISUALIZATION_EXAMPLES).map(([id, example]) => [ + example.script, + { ...example, id } + ]) +); + +const DEFAULT_VISUALIZATION_EXAMPLE = VISUALIZATION_EXAMPLES['attention-probabilities']; + +export function getVisualizationExampleById(id: string | null | undefined): VisualizationExample { + if (!id) return DEFAULT_VISUALIZATION_EXAMPLE; + return VISUALIZATION_EXAMPLES[id] ?? DEFAULT_VISUALIZATION_EXAMPLE; +} + +export function findExampleIdMatchingScript(script: string | null | undefined): string | null { + if (script == null) return null; + const match = VISUALIZATION_EXAMPLES_BY_SCRIPT.get(script); + return match ? match.id : null; +} + +export function getEffectiveVisualizationScript( + exampleId: string | null | undefined, + customScript: string | null | undefined +): string { + return exampleId === 'custom' + ? (customScript ?? '') + : getVisualizationExampleById(exampleId).script; +} + +export function getVisualizationExampleOptions( + config: Config +): Array<{ value: string; text: string }> { + return [ + ...Object.entries(VISUALIZATION_EXAMPLES).map(([id, e]) => ({ value: id, text: e.label })), + { value: 'custom', text: 'Custom' } + ]; +} diff --git a/examples/piston-train-toy/src/lib/workspace/workers.svelte.ts b/examples/piston-train-toy/src/lib/workspace/workers.svelte.ts index 4640a3f9..d2e83543 100644 --- a/examples/piston-train-toy/src/lib/workspace/workers.svelte.ts +++ b/examples/piston-train-toy/src/lib/workspace/workers.svelte.ts @@ -1,3 +1,6 @@ +import type { MatchBox } from '$lib/train/visualizer'; +import type { IndexState, TensorQuery } from '@piston-ml/piston-web'; + import { config } from './config.svelte'; import { currentRun, log } from './runs.svelte'; import { triggerLowDiversityDatasetError, triggerVramLimitFlash } from './ui.svelte'; @@ -10,6 +13,14 @@ export const trainingState = $state<{ current: 'training' | 'paused' | 'stopped' current: 'stopped' }); +// Visualizer layout state +let visualizerBoxes = $state(null); +let visualizerQueries = $state(null); +let visualizerLayoutStep = $state(null); +let visualizerLayoutRunId = $state(null); +let visualizerRenderWidth = $state(null); +let visualizerRenderHeight = $state(null); + // UA memory measurement state (main thread only) let uaMemoryInterval: ReturnType | null = null; let lastUAMemoryBytes: number | null = null; @@ -93,6 +104,20 @@ export async function initializeWorker() { const { type, ...data } = event.data; switch (type) { + case 'visualizer.ready': + console.log('[Main] Visualizer ready'); + break; + case 'capture': + visualizerQueries = data.queries as TensorQuery[]; + visualizerBoxes = data.boxes as MatchBox[]; + visualizerLayoutStep = data.step as number; + visualizerLayoutRunId = data.runId as string; + visualizerRenderWidth = data.width as number; + visualizerRenderHeight = data.height as number; + break; + case 'visualizer.error': + console.error('[Main] Visualizer error:', data.message); + break; case 'ready': console.log('[Main] Worker is ready'); resolve(); @@ -217,6 +242,7 @@ export function workerStep() { // let parameterCount = $state(null); +let modelIndex = $state(null); let modelInspectionRequestId = $state(null); let isInspectingModel = $state(false); let modelInspectionWorker: Worker | null = $state(null); @@ -225,6 +251,10 @@ export function getParameterCount() { return parameterCount; } +export function getModelIndex() { + return modelIndex; +} + export function getIsInspectingModel() { return isInspectingModel; } @@ -269,9 +299,11 @@ export function handleModelInspectionResponse(data: { requestId: string; parameterCount: number; vocabSize: number; + modelIndex: IndexState; }) { if (data.requestId === modelInspectionRequestId) { parameterCount = data.parameterCount; + modelIndex = data.modelIndex; isInspectingModel = false; modelInspectionRequestId = null; } @@ -355,3 +387,69 @@ export function cleanupWorkers() { void releaseScreenWakeLock(); } + +// +// Visualizer APIs +// +// eslint-disable-next-line svelte/prefer-svelte-reactivity +const canvasesWithAttemptedInitialization = new Set(); +export function initializeVisualizerCanvas( + canvas: HTMLCanvasElement, + labelPaddingCssPx: number = 0 +) { + if (!trainWorker) throw new Error('Worker not initialized'); + if (canvasesWithAttemptedInitialization.has(canvas)) return; + const offscreen = canvas.transferControlToOffscreen(); + trainWorker.postMessage( + { type: 'visualizer.canvas', data: { canvas: offscreen, labelPaddingCssPx } }, + [offscreen] + ); + canvasesWithAttemptedInitialization.add(canvas); +} + +export function resizeVisualizer(width: number) { + if (!trainWorker) return; + trainWorker.postMessage({ type: 'visualizer.resize', data: { width } }); +} + +export function getVisualizerLayout() { + return { + boxes: visualizerBoxes, + queries: visualizerQueries, + step: visualizerLayoutStep, + runId: visualizerLayoutRunId, + width: visualizerRenderWidth, + height: visualizerRenderHeight + }; +} + +export function getWorkerVersion() { + return workerVersion; +} + +export function updateVisualizerScript(example: string, script: string | null) { + if (!trainWorker) return; + trainWorker.postMessage({ type: 'visualizer.updateScript', data: { example, script } }); +} + +export function updateVisualizerTarget(target: 'train' | 'validation') { + if (!trainWorker) return; + config.visualization.target = target; + trainWorker.postMessage({ type: 'visualizer.setTarget', data: { target } }); +} + +export function updateVisualizerSelectedValidation({ + exampleIndex, + tokenIndex +}: { + exampleIndex: number; + tokenIndex: number; +}) { + if (!trainWorker) return; + config.visualization.selectedValidation.exampleIndex = exampleIndex; + config.visualization.selectedValidation.tokenIndex = tokenIndex; + trainWorker.postMessage({ + type: 'visualizer.setSelectedValidation', + data: { exampleIndex, tokenIndex } + }); +} diff --git a/examples/piston-train-toy/src/routes/+page.svelte b/examples/piston-train-toy/src/routes/+page.svelte index 0334dac2..dbac2294 100644 --- a/examples/piston-train-toy/src/routes/+page.svelte +++ b/examples/piston-train-toy/src/routes/+page.svelte @@ -49,7 +49,10 @@ // Check if current config is different from the config used to start the run const shouldSuggestRestart = $derived.by(() => { if (!currentRun.current?.config || !config) return false; - return JSON.stringify(config) !== JSON.stringify(currentRun.current.config); + // We can hot-update the visualization config without restarting the run, so we exclude it + const { visualization: _1, ...newConfig } = config; + const { visualization: _2, ...runConfig } = currentRun.current.config; + return JSON.stringify(newConfig) !== JSON.stringify(runConfig); }); onMount(() => { diff --git a/examples/piston-train-toy/src/routes/tabs/Metrics.svelte b/examples/piston-train-toy/src/routes/tabs/Metrics.svelte index 3d67d14c..844ac94e 100644 --- a/examples/piston-train-toy/src/routes/tabs/Metrics.svelte +++ b/examples/piston-train-toy/src/routes/tabs/Metrics.svelte @@ -1,8 +1,10 @@
    + {#if !tourState.current.restartedExperiment} + + Tinker with the experiment setup, then click New Changes + to try out your changes. You can probably break it! + Report issues on Github . + + {/if} {#if Object.keys(metricGroups).length === 0}

    No metrics available. Start training to see charts.

    From 081584b6b60c6ed59f1087f4e2ea51e821cb1aba Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 1 Nov 2025 15:43:40 -0600 Subject: [PATCH 561/590] Add checkpointing/saving/periodic restarting --- .../src/lib/train/moduleWorker.ts | 13 +- .../src/lib/train/protocol.ts | 5 +- .../piston-train-toy/src/lib/train/session.ts | 176 +++++++++++++- .../src/lib/train/utils/checkpoint.ts | 224 ++++++++++++++++++ .../src/lib/workspace/config.svelte.ts | 3 +- .../src/lib/workspace/config.ts | 1 + .../src/lib/workspace/ui.svelte.ts | 5 + .../src/lib/workspace/workers.svelte.ts | 48 ++++ .../piston-train-toy/src/routes/+page.svelte | 5 + 9 files changed, 470 insertions(+), 10 deletions(-) create mode 100644 examples/piston-train-toy/src/lib/train/utils/checkpoint.ts diff --git a/examples/piston-train-toy/src/lib/train/moduleWorker.ts b/examples/piston-train-toy/src/lib/train/moduleWorker.ts index 123482ce..2b4736a4 100644 --- a/examples/piston-train-toy/src/lib/train/moduleWorker.ts +++ b/examples/piston-train-toy/src/lib/train/moduleWorker.ts @@ -169,6 +169,10 @@ self.addEventListener('message', async (event) => { const data: unknown = (raw as { data?: unknown }).data; switch (type) { + case 'save': { + session?.save(); + break; + } case 'pause': { session?.pause(); break; @@ -239,14 +243,19 @@ self.addEventListener('message', async (event) => { } case 'start': try { - const { runId: runIdFromData, config } = data as { + const { + runId: runIdFromData, + config, + resumeFrom + } = data as { runId: string; config: Config; + resumeFrom?: Uint8Array; }; currentExecutionSource = `[Training:${runIdFromData}]`; console.info(`Starting training for run ${runIdFromData}`); - session = new TrainingSession(runIdFromData, config, postEvent); + session = new TrainingSession(runIdFromData, config, postEvent, resumeFrom); if (pendingVisualizerCanvas) { try { session.initVisualizerCanvas( diff --git a/examples/piston-train-toy/src/lib/train/protocol.ts b/examples/piston-train-toy/src/lib/train/protocol.ts index d0572278..9f2a2fce 100644 --- a/examples/piston-train-toy/src/lib/train/protocol.ts +++ b/examples/piston-train-toy/src/lib/train/protocol.ts @@ -7,11 +7,12 @@ type WithRunId = { runId: string }; export type WorkerCommand = | { type: 'start'; - data: { runId: string; config: Config }; + data: { runId: string; config: Config; resumeFrom?: Uint8Array }; } | { type: 'pause' } | { type: 'resume' } | { type: 'step' } + | { type: 'save' } | { type: 'stop' } | { type: 'visualizer.updateScript'; @@ -64,6 +65,8 @@ export type RunWorkerEventWithoutRunId = height: number; queries: unknown[]; } + | { type: 'checkpoint'; buffer: Uint8Array } + | { type: 'restart'; buffer: Uint8Array } | { type: 'paused' } | { type: 'resumed' } | { type: 'complete' } diff --git a/examples/piston-train-toy/src/lib/train/session.ts b/examples/piston-train-toy/src/lib/train/session.ts index 22222934..14134850 100644 --- a/examples/piston-train-toy/src/lib/train/session.ts +++ b/examples/piston-train-toy/src/lib/train/session.ts @@ -34,6 +34,13 @@ import { EncoderDecoderTransformer, EncoderTransformer } from './model/transformer'; +import { + type AnySchedulerState, + buildCheckpoint, + type CheckpointDataState, + type CheckpointExtra, + splitLoadedState +} from './utils/checkpoint'; import { calculateBlockSize, calculateVocabSize, @@ -67,6 +74,7 @@ export class TrainingSession { readonly runId: string; private config: Config; private readonly post: (e: RunWorkerEventWithoutRunId) => void; + private readonly resumeFrom?: Uint8Array; private paused = false; private resolvePause: (() => void) | null = null; @@ -104,12 +112,18 @@ export class TrainingSession { private validationCollateFn: PistonCollateFnType | null = null; private validationDataset: PistonDatasetType | null = null; - constructor(runId: string, config: Config, post: (e: WorkerEvent) => void) { + constructor( + runId: string, + config: Config, + post: (e: WorkerEvent) => void, + resumeFrom?: Uint8Array + ) { this.runId = runId; this.config = config; this.post = (e: RunWorkerEventWithoutRunId) => // We only post the subset of events that have runId in the payload (post as (e: RunWorkerEvent) => void)({ ...e, runId: this.runId }); + this.resumeFrom = resumeFrom; } async pause() { @@ -127,6 +141,32 @@ export class TrainingSession { this.post({ type: 'resumed' }); } + async save() { + try { + if (this.paused) { + try { + if (!this.model) { + // Defer save until model is ready + this.post({ type: 'log', level: 'info', message: 'Save requested before model ready' }); + return; + } + await piston.gpu.markStep(); + const buffer = await this.saveLatestCheckpoint(); + this.post({ type: 'checkpoint', buffer }); + } catch (e) { + this.post({ + type: 'error', + message: String(e) + }); + } + } else { + throw new Error('Saving during training is not supported'); + } + } catch (e) { + this.post({ type: 'error', message: String(e) }); + } + } + setVisualizationScript(example: string, script: string | null) { // If a model is already active, rebuild the plan immediately; otherwise setup() will build it if (this.model) { @@ -212,6 +252,40 @@ export class TrainingSession { } } + async saveLatestCheckpoint(): Promise> { + if (!this.model) throw new Error('No model available to save'); + await piston.gpu.markStep(); + // Derive dataset state if available + let dataState: CheckpointDataState | undefined = undefined; + if (this.trainDataset) { + if (this.trainDataset instanceof NaturalLanguageDataset) { + dataState = { + blockSize: this.blockSize, + natural: this.trainDataset.exportState() + }; + } else if (this.trainDataset instanceof ToyDataset) { + dataState = { + blockSize: this.blockSize, + toy: { + cursor: this.trainDataset.cursor, + baseSeed: this.trainDataset.baseSeed, + datasetName: this.config.data.dataset + } + }; + } + } + const { tensors, extra } = buildCheckpoint( + this.model, + this.optimizer!, + this.stepCount, + this.config ? JSON.parse(JSON.stringify(this.config)) : null, + this.scheduler, + dataState, + this.startTimeMs ?? undefined + ); + return piston.save(tensors, extra); + } + private logMetrics( data: { [metricName: string]: Omit }, metadata?: { step?: number } @@ -228,6 +302,43 @@ export class TrainingSession { const initialMemoryMB = Number(piston.gpu.usageBytes()) / (1024 * 1024); console.debug(`Initial memory: ${initialMemoryMB} MB`); + // If resuming from a checkpoint, parse and use checkpoint config + let resumePayload: { + modelState: Record; + optimizerPacked?: { state: Record; paramGroups: piston.ParamGroupConfig[] }; + schedulerState?: unknown; + numSteps: number; + config: Config; + dataState?: CheckpointDataState; + startTimeMs?: number; + } | null = null; + + if (this.resumeFrom) { + const loaded = piston.load(this.resumeFrom, piston.gpu); + const split = splitLoadedState( + loaded as { state: Record; extra?: CheckpointExtra } + ); + resumePayload = { + modelState: split.modelState, + optimizerPacked: split.optimizerState as unknown as { + state: Record; + paramGroups: piston.ParamGroupConfig[]; + }, + schedulerState: split.schedulerState, + numSteps: split.numSteps, + config: split.config, + dataState: split.dataState, + startTimeMs: split.startTimeMs + }; + if (resumePayload.config) { + this.config = resumePayload.config as Config; + } + // If blockSize present in extras, prefer it + if (split.dataState && split.dataState.blockSize !== undefined) { + this.blockSize = split.dataState.blockSize; + } + } + // Determine model type and create appropriate dataloader/model const isEncoderOnly = this.config.model.topology === 'encoder'; const isDecoderOnly = this.config.model.topology === 'decoder'; @@ -249,7 +360,22 @@ export class TrainingSession { const trainGenerator = seededRandom(seed); const maskGenerator = isEncoderOnly ? forkRandom(trainGenerator) : null; - this.trainDataset = buildDataset(this.config, trainGenerator, 'train'); + const trainDataset: PistonDatasetType = buildDataset(this.config, trainGenerator, 'train'); + // Restore dataset state if present + if (resumePayload && resumePayload.dataState) { + const dsState = resumePayload.dataState; + if (trainDataset instanceof NaturalLanguageDataset && dsState.natural) { + await trainDataset.importState(dsState.natural); + } else if (trainDataset instanceof ToyDataset && dsState.toy) { + // Restore cursor and baseSeed for toy datasets + trainDataset.cursor = dsState.toy.cursor | 0; + if (typeof dsState.toy.baseSeed === 'number') { + trainDataset.baseSeed = dsState.toy.baseSeed; + } + } + } + this.trainDataset = trainDataset; + const validationDisabled = ('disableValidation' in this.trainDataset && this.trainDataset.disableValidation) || false; @@ -332,10 +458,13 @@ export class TrainingSession { // Create model this.model = createModel(this.config, vocabSize, blockSize); - initializeModel(this.config, this.model); + // If starting from scratch, initialize model parameters + if (!resumePayload) { + initializeModel(this.config, this.model); - // We need to flatten down initialization to the constant tensors they're on top of - await piston.gpu.markStep(); + // We need to flatten down initialization to the constant tensors they're on top of + await piston.gpu.markStep(); + } // Build or refresh capture plan using CaptureManager this.captureManager = new CaptureManager(); @@ -357,14 +486,33 @@ export class TrainingSession { maskGenerator, this.trainDataset ); + + // If resuming, load model state BEFORE creating the optimizer so param identities match + let startStep = 0; + if (resumePayload) { + this.model.loadStateDict(resumePayload.modelState, { strict: false }); + startStep = (resumePayload.numSteps ?? 0) + 1; + this.stepCount = startStep; + // If checkpoint carried a startTimeMs, use it for wall-clock continuity + if (typeof resumePayload.startTimeMs === 'number') { + this.startTimeMs = resumePayload.startTimeMs; + } + } + // Create optimizer based on model type, using the (possibly restored) model parameters - this.optimizer = configureOptimizerForModel( + const optimizer = configureOptimizerForModel( this.model, isEncoderOnly, isEncoderDecoder, this.config.optimizer, piston.gpu ); + this.optimizer = optimizer; + + // If resuming, load optimizer state NOW that groups refer to current model parameters + if (resumePayload && resumePayload.optimizerPacked) { + optimizer.loadStateDict(resumePayload.optimizerPacked as piston.StateDict); + } // Create learning rate scheduler if configured if (this.config.optimizer.lrScheduler.present) { @@ -400,6 +548,11 @@ export class TrainingSession { } } + // If resuming, load scheduler state after it is created + if (resumePayload && this.scheduler && resumePayload.schedulerState) { + this.scheduler.loadStateDict(resumePayload.schedulerState as AnySchedulerState); + } + this.model.train(); this.isSetup = true; @@ -718,6 +871,17 @@ export class TrainingSession { this.lastLogTime = currentTime; this.lastLogStep = this.stepCount; } + + // Trigger periodic restart if configured + const restartEvery = this.config.training.restartEverySteps ?? 0; + const willRestart = restartEvery > 0 && (this.stepCount + 1) % restartEvery === 0; + if (willRestart) { + console.debug(`Routine restart at step ${this.stepCount}`); + await piston.gpu.markStep(); + const bytes = await this.saveLatestCheckpoint(); + this.post({ type: 'restart', buffer: bytes }); + return { done: true, value: 'restarted' }; + } } finally { finalWeakModeForStep[Symbol.dispose](); } diff --git a/examples/piston-train-toy/src/lib/train/utils/checkpoint.ts b/examples/piston-train-toy/src/lib/train/utils/checkpoint.ts new file mode 100644 index 00000000..afc2538d --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/utils/checkpoint.ts @@ -0,0 +1,224 @@ +import type { Config } from '$lib/workspace/config'; + +import * as piston from '@piston-ml/piston-web'; +import { + type ConstantConfig, + type CosineAnnealingConfig, + type ExponentialConfig, + type LinearConfig, + LRScheduler, + Optimizer, + type OptimizerParamState, + type ParamGroupConfig, + type SchedulerStateDict, + type StateDict, + type StepConfig, + Tensor +} from '@piston-ml/piston-web'; + +/** + * Recursively walks an object and extracts any Tensor values into `out` as Buffers. + * Replaces extracted tensors in the returned structure with a small marker object + * containing the tensor storage key that was used in `out`. + */ +export function splitTensorsFromObject( + value: unknown, + baseKey: string, + out: Record +): unknown { + if (value instanceof Tensor) { + out[baseKey] = new piston.Buffer(value, true); + return { __tensor__: baseKey }; + } + if (Array.isArray(value)) { + return value.map((v, i) => splitTensorsFromObject(v, `${baseKey}.${i}`, out)); + } + if (value && typeof value === 'object') { + const result: Record = {}; + for (const [k, v] of Object.entries(value)) { + result[k] = splitTensorsFromObject(v, `${baseKey}.${k}`, out); + } + return result; + } + return value; +} + +export type AnySchedulerState = SchedulerStateDict< + StepConfig | CosineAnnealingConfig | ExponentialConfig | LinearConfig | ConstantConfig | unknown +>; + +export type CheckpointOptimizerExtra = { + name: string; + // JSON with __tensor__ markers + state: unknown; + paramGroups: ParamGroupConfig[]; +} | null; + +export interface CheckpointDataState { + blockSize?: number | { source: number; target: number }; + toy?: { cursor: number; baseSeed?: number; datasetName?: string } | null; + natural?: { shardIndex: number; cursor: number } | null; +} + +export interface CheckpointExtra { + config: Config; + optimizer: CheckpointOptimizerExtra; + numSteps: number; + lrScheduler?: { state: AnySchedulerState }; + dataState?: CheckpointDataState; + // Optional wall-clock training start time in ms to persist across restarts + startTimeMs?: number; +} + +/** + * Builds a checkpoint payload by combining model parameters with optimizer state. + * - Model parameters/buffers go into `tensors` directly + * - Any Tensor values found inside optimizer.stateDict().state are lifted into `tensors` + * under keys prefixed with `optimizer/state/...` + * - Extra contains { config, optimizer, numSteps } + */ + +export function buildCheckpoint( + model: piston.Module, + optimizer: Optimizer, + numSteps: number, + configForExtra: Config, + scheduler?: LRScheduler, + dataState?: CheckpointDataState, + startTimeMs?: number +): { tensors: Record; extra: CheckpointExtra } { + const tensors: Record = model.stateDict(); + + let optimizerExtra: CheckpointOptimizerExtra = null; + try { + const name = optimizer.constructor.name ?? 'Optimizer'; + const packed = optimizer.stateDict(); + const tensorSlots: Record = {}; + const jsonState = splitTensorsFromObject(packed.state, 'optimizer.state', tensorSlots); + Object.assign(tensors, tensorSlots); + optimizerExtra = { + name, + state: jsonState, + paramGroups: packed.paramGroups + }; + } catch (e) { + console.warn('Failed to pack optimizer stateDict for checkpoint extra:', e); + } + + const extra: CheckpointExtra = { + config: configForExtra, + optimizer: optimizerExtra, + numSteps, + lrScheduler: scheduler ? { state: scheduler.stateDict() } : undefined, + dataState, + startTimeMs + }; + + return { tensors, extra }; +} + +/** + * Replace any marker objects of the form { __tensor__: key } inside a JSON structure + * with actual Tensors from the provided mapping. + */ +export function rehydrateTensorsInObject(value: unknown, lifted: Record): T { + if (value && typeof value === 'object' && !Array.isArray(value)) { + const marker = value as { __tensor__?: string }; + if (typeof marker.__tensor__ === 'string') { + const key = marker.__tensor__; + if (!(key in lifted)) { + throw new Error(`Missing lifted tensor for key '${key}' during optimizer rehydration`); + } + return lifted[key] as unknown as T; + } + const out: Record = {}; + for (const [k, v] of Object.entries(value)) { + out[k] = rehydrateTensorsInObject(v, lifted); + } + return out as unknown as T; + } + if (Array.isArray(value)) { + return value.map((v) => rehydrateTensorsInObject(v, lifted)) as unknown as T; + } + return value as T; +} + +export interface SplitLoadedStateResult { + modelState: Record; + schedulerState?: AnySchedulerState; + optimizerState: StateDict; + numSteps: number; + config: Config; + dataState?: CheckpointDataState; + startTimeMs?: number; +} + +/** + * Given loaded state from piston.load, split out model state from lifted optimizer tensors + * and rehydrate optimizer and scheduler states from extras. + */ +export function splitLoadedState(loaded: { + state: Record; + extra?: CheckpointExtra; +}): SplitLoadedStateResult { + const prefix = 'optimizer.state'; + const liftedOptimizerTensors: Record = {}; + const modelState: Record = {}; + + for (const [key, t] of Object.entries(loaded.state)) { + if (key.startsWith(prefix)) { + liftedOptimizerTensors[key] = t; + } else { + modelState[key] = t; + } + } + + let optimizerState: StateDict | undefined; + let schedulerState: AnySchedulerState | undefined = undefined; + let numSteps = 0; + let config: Config | null = null; + let dataState: CheckpointDataState | undefined = undefined; + let startTimeMs: number | undefined = undefined; + + const { extra } = loaded; + + if (extra) { + config = extra.config; + numSteps = extra.numSteps; + if (extra.optimizer) { + const rehydratedState = rehydrateTensorsInObject>( + extra.optimizer.state, + liftedOptimizerTensors + ); + optimizerState = { + state: rehydratedState, + paramGroups: extra.optimizer.paramGroups ?? [] + }; + } + if (extra.lrScheduler && extra.lrScheduler.state) { + schedulerState = extra.lrScheduler.state; + } + if (extra.dataState) { + dataState = extra.dataState; + } + if (typeof extra.startTimeMs === 'number') { + startTimeMs = extra.startTimeMs; + } + } + + if (!config) { + throw new Error('No config found in checkpoint'); + } + + if (numSteps == null) { + throw new Error('No numSteps found in checkpoint'); + } + + if (!optimizerState) { + throw new Error('No optimizer state found in checkpoint'); + } + + // Some runs don't use a scheduler, so we don't validate that it's present + + return { modelState, optimizerState, schedulerState, numSteps, config, dataState, startTimeMs }; +} diff --git a/examples/piston-train-toy/src/lib/workspace/config.svelte.ts b/examples/piston-train-toy/src/lib/workspace/config.svelte.ts index c4f4191f..e8917c40 100644 --- a/examples/piston-train-toy/src/lib/workspace/config.svelte.ts +++ b/examples/piston-train-toy/src/lib/workspace/config.svelte.ts @@ -53,7 +53,8 @@ const CONFIG_DEFAULTS: Config = { vramLimitMb: { present: true, value: 4096 - } + }, + restartEverySteps: 1000 }, data: { dataset: 'sort', diff --git a/examples/piston-train-toy/src/lib/workspace/config.ts b/examples/piston-train-toy/src/lib/workspace/config.ts index 3ee746bb..ce7a1b61 100644 --- a/examples/piston-train-toy/src/lib/workspace/config.ts +++ b/examples/piston-train-toy/src/lib/workspace/config.ts @@ -63,6 +63,7 @@ export interface TrainingConfig { cachingEnabled: boolean; inplaceSupport: boolean; enableVisualization: boolean; + restartEverySteps: number; } export interface TransformerAttentionConfig { diff --git a/examples/piston-train-toy/src/lib/workspace/ui.svelte.ts b/examples/piston-train-toy/src/lib/workspace/ui.svelte.ts index ba20ef3b..605681e5 100644 --- a/examples/piston-train-toy/src/lib/workspace/ui.svelte.ts +++ b/examples/piston-train-toy/src/lib/workspace/ui.svelte.ts @@ -5,6 +5,7 @@ import { trainingState, workerPauseTraining, workerReady, + workerRequestSave, workerResumeTraining, workerStartTraining, workerStep, @@ -214,6 +215,10 @@ export function toggleConfig() { configOpen.current = !configOpen.current; } +export function saveModel() { + workerRequestSave(); +} + // Function to start training export function startTraining() { if (trainingState.current !== 'stopped' || !workerReady.current) return; diff --git a/examples/piston-train-toy/src/lib/workspace/workers.svelte.ts b/examples/piston-train-toy/src/lib/workspace/workers.svelte.ts index d2e83543..9da9efbd 100644 --- a/examples/piston-train-toy/src/lib/workspace/workers.svelte.ts +++ b/examples/piston-train-toy/src/lib/workspace/workers.svelte.ts @@ -150,6 +150,47 @@ export async function initializeWorker() { void releaseScreenWakeLock(); break; + case 'restart': + console.log(`[Main] Worker requested restart for run ${data.runId}`); + const buffer = data.buffer as Uint8Array; + const runId = data.runId as string; + // Terminate and recreate worker + trainWorker?.terminate(); + workerReady.current = false; + // Ensure training state reflects continuity across restart + trainingState.current = 'training'; + initializeWorker().then(() => { + // Send start with resumeFrom to resume same run id + trainWorker!.postMessage({ + type: 'start', + data: { runId, config: $state.snapshot(config), resumeFrom: buffer } + }); + }); + break; + + case 'checkpoint': + let url; + const uint8array = data.buffer as Uint8Array | undefined; + if (uint8array && typeof URL !== 'undefined' && typeof Blob !== 'undefined') { + const blob = new Blob([uint8array.buffer as ArrayBuffer], { + type: 'application/octet-stream' + }); + url = URL.createObjectURL(blob); + } + + if (!url) { + console.error('[Main] checkpoint did not include a URL or buffer'); + break; + } + + const a = document.createElement('a'); + a.href = url; + a.download = `${currentRun.current?.runId}.safetensors`; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + break; + case 'error': if (data.name === 'VRAMLimitExceededError') { console.error(`[Main] VRAM limit exceeded for run ${data.runId}:`, data.message); @@ -207,6 +248,13 @@ export function workerStartTraining() { void acquireScreenWakeLock(); } +export function workerRequestSave() { + if (!trainWorker) { + throw new Error('Worker not initialized'); + } + trainWorker.postMessage({ type: 'save' }); +} + export async function workerStopTraining() { if (!trainWorker || trainingState.current === 'stopped') return; void releaseScreenWakeLock(); diff --git a/examples/piston-train-toy/src/routes/+page.svelte b/examples/piston-train-toy/src/routes/+page.svelte index 45889325..4fc027c2 100644 --- a/examples/piston-train-toy/src/routes/+page.svelte +++ b/examples/piston-train-toy/src/routes/+page.svelte @@ -10,6 +10,7 @@ hasWebGPU, isMobile, restartTraining, + saveModel, selectTab, setupUI, startTraining, @@ -27,6 +28,7 @@ } from '$lib/workspace/workers.svelte'; import { ChartLine, + DownloadIcon, Info, PauseIcon, PlayIcon, @@ -182,6 +184,9 @@ class="p-1 border-b border-panel-border-base shrink-0 flex items-center justify-between" > {currentRun.current?.runId} +
    {/if}
    From 74698e8a43902a40f5d251e55181a9e20696b175 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 1 Nov 2025 15:50:42 -0600 Subject: [PATCH 562/590] Add max steps option --- .../lib/components/controls/Controls.svelte | 20 +++++++++++++++++++ .../piston-train-toy/src/lib/train/session.ts | 20 +++++++++++++++++-- .../src/lib/workspace/config.svelte.ts | 4 ++++ .../src/lib/workspace/config.ts | 4 ++++ 4 files changed, 46 insertions(+), 2 deletions(-) diff --git a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte index 47a0a560..104a5b0e 100644 --- a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte +++ b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte @@ -585,6 +585,26 @@ + resetConfigToDefaults('training.limitTraining.present')} + > + resetConfigToDefaults('training.limitTraining.steps')} + /> + + = this.config.training.limitTraining.steps + ) { + console.log( + `Stopping training at step ${this.stepCount} because it reached the limit of ${this.config.training.limitTraining.steps} steps` + ); + isLastStep = true; + } + + const loggingStep = + manual || isLastStep || this.stepCount % this.config.training.logSteps === 0; let captureSession: piston.CaptureSession | null = null; if (loggingStep && this.captureManager && this.config.visualization.target === 'train') { @@ -707,7 +719,7 @@ export class TrainingSession { if ( this.config.training.validation.present && - this.stepCount % this.config.training.validation.valSteps === 0 && + (this.stepCount % this.config.training.validation.valSteps === 0 || isLastStep) && this.validationExamples && this.validationDataset && this.validationCollateFn @@ -882,6 +894,10 @@ export class TrainingSession { this.post({ type: 'restart', buffer: bytes }); return { done: true, value: 'restarted' }; } + + if (isLastStep) { + return { done: true, value: 'completed' }; + } } finally { finalWeakModeForStep[Symbol.dispose](); } diff --git a/examples/piston-train-toy/src/lib/workspace/config.svelte.ts b/examples/piston-train-toy/src/lib/workspace/config.svelte.ts index e8917c40..448e5f81 100644 --- a/examples/piston-train-toy/src/lib/workspace/config.svelte.ts +++ b/examples/piston-train-toy/src/lib/workspace/config.svelte.ts @@ -33,6 +33,10 @@ const CONFIG_DEFAULTS: Config = { subsetSize: 4 } }, + limitTraining: { + present: false, + steps: 50_000 + }, dropout: { present: false, embedding: 0.1, diff --git a/examples/piston-train-toy/src/lib/workspace/config.ts b/examples/piston-train-toy/src/lib/workspace/config.ts index ce7a1b61..7987c1ac 100644 --- a/examples/piston-train-toy/src/lib/workspace/config.ts +++ b/examples/piston-train-toy/src/lib/workspace/config.ts @@ -47,6 +47,10 @@ export interface ValidationConfig { export interface TrainingConfig { logSteps: number; + limitTraining: { + present: boolean; + steps: number; + }; batchSize: number; dropout: DropoutConfig; validation: ValidationConfig; From 4c5b041b6eea86e59e994b3fc51af763ce58197c Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 1 Nov 2025 15:56:32 -0600 Subject: [PATCH 563/590] Add grad norm tracking, clipping to app --- .../lib/components/controls/Controls.svelte | 39 +++++++++++++++++++ .../piston-train-toy/src/lib/train/session.ts | 25 +++++++++++- .../src/lib/workspace/config.svelte.ts | 8 ++++ .../src/lib/workspace/config.ts | 8 ++++ 4 files changed, 79 insertions(+), 1 deletion(-) diff --git a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte index 104a5b0e..df3d6798 100644 --- a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte +++ b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte @@ -509,6 +509,45 @@ onReset={() => resetConfigToDefaults('training.enableVisualization')} /> + resetConfigToDefaults('training.gradNorm.track')} + > + resetConfigToDefaults('training.gradNorm.errorIfNonfinite')} + /> + + resetConfigToDefaults('training.clipGradNorm.present')} + > + resetConfigToDefaults('training.clipGradNorm.value')} + /> + + + Date: Sat, 1 Nov 2025 16:01:40 -0600 Subject: [PATCH 564/590] Add warmup steps --- .../lib/components/controls/Controls.svelte | 19 +++++++++++++++++++ .../controls/LRSchedulePicker.svelte | 10 ++++++++++ .../piston-train-toy/src/lib/train/session.ts | 14 ++++++++++++++ .../src/lib/workspace/config.svelte.ts | 4 ++++ .../src/lib/workspace/config.ts | 1 + 5 files changed, 48 insertions(+) diff --git a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte index df3d6798..a16edde6 100644 --- a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte +++ b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte @@ -807,6 +807,25 @@ onReset={() => resetConfigToDefaults('optimizer.lr')} /> + resetConfigToDefaults('optimizer.warmupSteps.present')} + > + resetConfigToDefaults('optimizer.warmupSteps.value')} + /> + + 0) { + const warm = new LinearLR(mockOptimizer, 1e-8, 1.0, n); + scheduler = new SequentialLR(mockOptimizer, [warm, scheduler], [n]); + } + } + // Generate points by stepping through the scheduler for (let step = 0; step <= maxSteps; step++) { const currentLr = scheduler.getLastLr()[0] || baseLr; diff --git a/examples/piston-train-toy/src/lib/train/session.ts b/examples/piston-train-toy/src/lib/train/session.ts index d72f037b..22448a9d 100644 --- a/examples/piston-train-toy/src/lib/train/session.ts +++ b/examples/piston-train-toy/src/lib/train/session.ts @@ -7,6 +7,7 @@ import { ExponentialLR, LinearLR, type LRScheduler, + SequentialLR, StepLR, type Tensor } from '@piston-ml/piston-web'; @@ -546,6 +547,19 @@ export class TrainingSession { default: throw new Error(`Unknown scheduler type: ${lrConfig.type}`); } + + if (this.scheduler && this.config.optimizer.warmupSteps.present) { + const n = this.config.optimizer.warmupSteps.value; + if (n > 0) { + const warmup = new LinearLR(optimizer, 1e-8, 1.0, n); + this.scheduler = new SequentialLR(optimizer, [warmup, this.scheduler], [n]); + } + } + } else if (this.config.optimizer.warmupSteps.present) { + const n = this.config.optimizer.warmupSteps.value; + if (n > 0) { + this.scheduler = new LinearLR(optimizer, 1e-8, 1.0, n); + } } // If resuming, load scheduler state after it is created diff --git a/examples/piston-train-toy/src/lib/workspace/config.svelte.ts b/examples/piston-train-toy/src/lib/workspace/config.svelte.ts index 572a16db..9e6fbaeb 100644 --- a/examples/piston-train-toy/src/lib/workspace/config.svelte.ts +++ b/examples/piston-train-toy/src/lib/workspace/config.svelte.ts @@ -145,6 +145,10 @@ const CONFIG_DEFAULTS: Config = { value: 1e-2, useWeightDecayGroups: true }, + warmupSteps: { + present: true, + value: 100 + }, lrScheduler: { present: true, type: 'cosine', diff --git a/examples/piston-train-toy/src/lib/workspace/config.ts b/examples/piston-train-toy/src/lib/workspace/config.ts index 292471bb..50eb911a 100644 --- a/examples/piston-train-toy/src/lib/workspace/config.ts +++ b/examples/piston-train-toy/src/lib/workspace/config.ts @@ -177,6 +177,7 @@ export interface OptimizerConfig { value: number; useWeightDecayGroups: boolean; }; + warmupSteps: { present: boolean; value: number }; lrScheduler: { present: boolean; type: string; From dc3aaffacd6fffb9b13fc1f30cc413cde647a4e0 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 1 Nov 2025 16:05:41 -0600 Subject: [PATCH 565/590] Add rounding vocab size to nearest multiple --- .../lib/components/controls/Controls.svelte | 29 +++++++++++++++++++ .../piston-train-toy/src/lib/train/session.ts | 2 +- .../src/lib/train/utils/model.ts | 20 +++++++++---- .../src/lib/workspace/config.svelte.ts | 6 +++- .../src/lib/workspace/config.ts | 4 +++ 5 files changed, 54 insertions(+), 7 deletions(-) diff --git a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte index a16edde6..ebc69033 100644 --- a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte +++ b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte @@ -215,6 +215,35 @@ onReset={() => resetConfigToDefaults('model.transformer.headDim')} /> + resetConfigToDefaults('model.roundVocabSizeToNearestMultiple.present')} + > + resetConfigToDefaults('model.roundVocabSizeToNearestMultiple.value')} + /> + + ( @@ -257,7 +267,7 @@ export function inspectModel(config: Config): { const [dataloader] = createDataloader(config, dataset, generator, tensorWrap); const isEncoderDecoder = config.model.topology === 'encoder-decoder'; const blockSizeOrSizes = calculateBlockSize(config, dataloader); - const vocabSize = calculateVocabSize(dataset); + const vocabSize = calculateVocabSize(config, dataset); const model = createModel(config, vocabSize, blockSizeOrSizes); const parameterCount = countParameters(model); diff --git a/examples/piston-train-toy/src/lib/workspace/config.svelte.ts b/examples/piston-train-toy/src/lib/workspace/config.svelte.ts index 9e6fbaeb..4055b212 100644 --- a/examples/piston-train-toy/src/lib/workspace/config.svelte.ts +++ b/examples/piston-train-toy/src/lib/workspace/config.svelte.ts @@ -84,6 +84,10 @@ const CONFIG_DEFAULTS: Config = { model: { topology: 'decoder', layers: 1, + roundVocabSizeToNearestMultiple: { + present: false, + value: 64 + }, encoderDecoder: { encoderLayers: 1, decoderLayers: 1 @@ -472,7 +476,7 @@ function datasetFromConfig(config: Config) { const dataset = buildDataset(config, generator, 'train'); const [dataloader, collateFn] = createDataloader(config, dataset, generator, null); const blockSize = calculateBlockSize(config, dataloader); - const vocabSize = calculateVocabSize(dataset); + const vocabSize = calculateVocabSize(config, dataset); const collatedData = getCollatedSampleData(dataset, collateFn, 4); diff --git a/examples/piston-train-toy/src/lib/workspace/config.ts b/examples/piston-train-toy/src/lib/workspace/config.ts index 50eb911a..d8dc1c2a 100644 --- a/examples/piston-train-toy/src/lib/workspace/config.ts +++ b/examples/piston-train-toy/src/lib/workspace/config.ts @@ -161,6 +161,10 @@ export interface TransformerConfig { export interface ModelConfig { topology: ModelType; layers: number; + roundVocabSizeToNearestMultiple: { + present: boolean; + value: number; + }; encoderDecoder: { decoderLayers: number; encoderLayers: number; From bd6e20be87f60dff3fd62ef565bdc1640cca5dc1 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 1 Nov 2025 16:10:53 -0600 Subject: [PATCH 566/590] Add grouped-query attention controls --- .../lib/components/controls/Controls.svelte | 36 +++++++++++++++++++ .../src/lib/train/model/config.ts | 5 ++- .../src/lib/train/model/modules/attention.ts | 6 +++- .../src/lib/workspace/config.svelte.ts | 12 +++++-- .../src/lib/workspace/config.ts | 4 +++ 5 files changed, 59 insertions(+), 4 deletions(-) diff --git a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte index ebc69033..33125273 100644 --- a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte +++ b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte @@ -268,6 +268,42 @@ hasDefaultValue={equalsConfigDefault('model.transformer.attention.nKeyValueHeads')} onReset={() => resetConfigToDefaults('model.transformer.attention.nKeyValueHeads')} /> + + resetConfigToDefaults('model.transformer.attention.groupedQueryAttention.present')} + > + + resetConfigToDefaults( + 'model.transformer.attention.groupedQueryAttention.queryHeadsPerKeyValueHead' + )} + /> + diff --git a/examples/piston-train-toy/src/lib/train/model/config.ts b/examples/piston-train-toy/src/lib/train/model/config.ts index 5f733373..6ca92303 100644 --- a/examples/piston-train-toy/src/lib/train/model/config.ts +++ b/examples/piston-train-toy/src/lib/train/model/config.ts @@ -24,7 +24,10 @@ export function buildTransformerConfigCommon( vocabSize: number ): TransformerModuleConfig { const effectiveHeads = config.model.transformer.attention.present - ? config.model.transformer.attention.nKeyValueHeads + ? config.model.transformer.attention.nKeyValueHeads * + (config.model.transformer.attention.groupedQueryAttention.present + ? config.model.transformer.attention.groupedQueryAttention.queryHeadsPerKeyValueHead + : 1) : 1; return { diff --git a/examples/piston-train-toy/src/lib/train/model/modules/attention.ts b/examples/piston-train-toy/src/lib/train/model/modules/attention.ts index b71d6198..ef977a79 100644 --- a/examples/piston-train-toy/src/lib/train/model/modules/attention.ts +++ b/examples/piston-train-toy/src/lib/train/model/modules/attention.ts @@ -21,7 +21,11 @@ abstract class CoreAttention extends nn.Module { constructor(embeddingSize: number, config: TransformerModuleConfig, isCausal: boolean = false) { super(); - this.nHeads = config.attention.nKeyValueHeads; + this.nHeads = + config.attention.nKeyValueHeads * + (config.attention.groupedQueryAttention.present + ? config.attention.groupedQueryAttention.queryHeadsPerKeyValueHead + : 1); this.nKvHeads = config.attention.nKeyValueHeads; this.embeddingSize = embeddingSize; this.headDim = embeddingSize / this.nHeads; diff --git a/examples/piston-train-toy/src/lib/workspace/config.svelte.ts b/examples/piston-train-toy/src/lib/workspace/config.svelte.ts index 4055b212..f21e73b4 100644 --- a/examples/piston-train-toy/src/lib/workspace/config.svelte.ts +++ b/examples/piston-train-toy/src/lib/workspace/config.svelte.ts @@ -122,7 +122,11 @@ const CONFIG_DEFAULTS: Config = { }, attention: { present: true, - nKeyValueHeads: 4 + nKeyValueHeads: 4, + groupedQueryAttention: { + present: false, + queryHeadsPerKeyValueHead: 2 + } }, positionalEncoding: { present: true, @@ -521,7 +525,11 @@ export function getLatestRunDataset() { } const transformerHiddenSize = $derived( - config.model.transformer.headDim * config.model.transformer.attention.nKeyValueHeads + config.model.transformer.headDim * + (config.model.transformer.attention.groupedQueryAttention.present + ? config.model.transformer.attention.groupedQueryAttention.queryHeadsPerKeyValueHead + : 1) * + config.model.transformer.attention.nKeyValueHeads ); const mlpIntermediateSize = $derived( diff --git a/examples/piston-train-toy/src/lib/workspace/config.ts b/examples/piston-train-toy/src/lib/workspace/config.ts index d8dc1c2a..6eeb0365 100644 --- a/examples/piston-train-toy/src/lib/workspace/config.ts +++ b/examples/piston-train-toy/src/lib/workspace/config.ts @@ -81,6 +81,10 @@ export interface TrainingConfig { export interface TransformerAttentionConfig { present: boolean; nKeyValueHeads: number; + groupedQueryAttention: { + present: boolean; + queryHeadsPerKeyValueHead: number; + }; } export interface DataConfig { From bda35ccff06b1ac15bb880f2b4044a0c71cd80ba Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 1 Nov 2025 16:26:28 -0600 Subject: [PATCH 567/590] Add attention gating from newish paper --- .../lib/components/controls/Controls.svelte | 72 +++++++++++++++++++ .../src/lib/train/model/modules/attention.ts | 60 +++++++++++++++- .../src/lib/train/model/modules/gate.ts | 19 +++++ .../src/lib/workspace/config.svelte.ts | 11 +++ .../src/lib/workspace/config.ts | 15 ++++ 5 files changed, 175 insertions(+), 2 deletions(-) create mode 100644 examples/piston-train-toy/src/lib/train/model/modules/gate.ts diff --git a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte index 33125273..9a107c74 100644 --- a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte +++ b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte @@ -304,6 +304,78 @@ )} /> + + resetConfigToDefaults('model.transformer.attention.gating.present')} + > + resetConfigToDefaults('model.transformer.attention.gating.activation')} + /> + + resetConfigToDefaults('model.transformer.attention.gating.sites.afterSdpaOutput')} + /> + + resetConfigToDefaults('model.transformer.attention.gating.sites.afterValueProjection')} + /> + + resetConfigToDefaults('model.transformer.attention.gating.sites.afterKeyProjection')} + /> + + resetConfigToDefaults('model.transformer.attention.gating.sites.afterQueryProjection')} + /> + + resetConfigToDefaults( + 'model.transformer.attention.gating.sites.afterFinalOutputProjection' + )} + /> + diff --git a/examples/piston-train-toy/src/lib/train/model/modules/attention.ts b/examples/piston-train-toy/src/lib/train/model/modules/attention.ts index ef977a79..a7a01de1 100644 --- a/examples/piston-train-toy/src/lib/train/model/modules/attention.ts +++ b/examples/piston-train-toy/src/lib/train/model/modules/attention.ts @@ -4,6 +4,7 @@ import type { SelfAttentionCache } from '../cache'; import type { TransformerModuleConfig } from '../config'; import { createCausalMask, maskedFill } from '../utils'; +import { SimpleHadamardGate } from './gate'; abstract class CoreAttention extends nn.Module { protected readonly nHeads: number; @@ -17,6 +18,11 @@ abstract class CoreAttention extends nn.Module { protected alibi?: AlibiEmbedding; protected readonly isCausal: boolean; protected readonly config: TransformerModuleConfig; + protected readonly gateAfterSdpa?: SimpleHadamardGate; + protected readonly gateAfterQ?: SimpleHadamardGate; + protected readonly gateAfterK?: SimpleHadamardGate; + protected readonly gateAfterV?: SimpleHadamardGate; + protected readonly gateAfterFinal?: SimpleHadamardGate; constructor(embeddingSize: number, config: TransformerModuleConfig, isCausal: boolean = false) { super(); @@ -32,9 +38,49 @@ abstract class CoreAttention extends nn.Module { this.isCausal = isCausal; this.config = config; + + const gatingConfig = config.attention.gating; + if (gatingConfig?.present) { + const controlDim = this.embeddingSize; + const act = gatingConfig.activation; + if (gatingConfig.sites.afterSdpaOutput) { + this.gateAfterSdpa = new SimpleHadamardGate(controlDim, this.embeddingSize, act); + } + if (gatingConfig.sites.afterFinalOutputProjection) { + this.gateAfterFinal = new SimpleHadamardGate(controlDim, this.embeddingSize, act); + } + if (gatingConfig.sites.afterQueryProjection) { + this.gateAfterQ = new SimpleHadamardGate(controlDim, this.headDim, act); + } + if (gatingConfig.sites.afterKeyProjection) { + this.gateAfterK = new SimpleHadamardGate(controlDim, this.headDim, act); + } + if (gatingConfig.sites.afterValueProjection) { + this.gateAfterV = new SimpleHadamardGate(controlDim, this.headDim, act); + } + } + } + + protected applyQkvGating( + control: Tensor, + q: Tensor, + k: Tensor, + v: Tensor + ): [Tensor, Tensor, Tensor] { + if (this.gateAfterQ) { + q = this.gateAfterQ.forward(q, control); + } + if (this.gateAfterK) { + k = this.gateAfterK.forward(k, control); + } + if (this.gateAfterV) { + v = this.gateAfterV.forward(v, control); + } + return [q, k, v]; } protected runAttention( + input: Tensor, q: Tensor, k: Tensor, v: Tensor, @@ -47,6 +93,8 @@ abstract class CoreAttention extends nn.Module { const B = q.size(0); const T_q = q.size(2); + [q, k, v] = this.applyQkvGating(input, q, k, v); + // Optional RoPE with separate offsets for q and k if (options.ropeOffsets) { [q, k] = this.applyRoPE(q, k, options.ropeOffsets.qOffset, options.ropeOffsets.kOffset); @@ -78,10 +126,18 @@ abstract class CoreAttention extends nn.Module { att = this.attnDropout ? this.attnDropout.forward(att) : att; let y = att.matmul(vCat).transpose(1, 2).view([B, T_q, this.embeddingSize]); + if (this.gateAfterSdpa) { + y = this.gateAfterSdpa.forward(y, input); + } // Apply output projection y = this.cProj.forward(y); + // Optional gating after final output projection + if (this.gateAfterFinal) { + y = this.gateAfterFinal.forward(y, input); + } + if (this.residDropout) { y = this.residDropout.forward(y); } @@ -212,7 +268,7 @@ export class SelfAttention extends CoreAttention { const qkv = this.projectQkv(input); const [q, k, v] = qkv; const pastLen = options.cache?.length ?? 0; - const [y, newCache] = this.runAttention(q, k, v, { + const [y, newCache] = this.runAttention(input, q, k, v, { attentionMask: options.attentionMask ?? null, cache: options.cache ?? null, ropeOffsets: { qOffset: pastLen, kOffset: pastLen } @@ -281,7 +337,7 @@ export class CrossAttention extends CoreAttention { returnCache = { k, v, length: keyValue.size(1) }; } - const [y, _ignored] = this.runAttention(q, k, v, { + const [y, _ignored] = this.runAttention(query, q, k, v, { attentionMask: options.attentionMask ?? null, // No KV concatenation for cross-attention; K/V are static from encoder cache: null, diff --git a/examples/piston-train-toy/src/lib/train/model/modules/gate.ts b/examples/piston-train-toy/src/lib/train/model/modules/gate.ts new file mode 100644 index 00000000..c700e20e --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/model/modules/gate.ts @@ -0,0 +1,19 @@ +import type { Activation } from '$lib/workspace/config'; + +import { nn, Tensor } from '@piston-ml/piston-web'; + +export class SimpleHadamardGate extends nn.Module { + private readonly tau: nn.Linear; + private readonly activation: (x: Tensor) => Tensor; + + constructor(controlDim: number, targetDim: number, activationName: Activation) { + super(); + this.tau = new nn.Linear(controlDim, targetDim); + this.activation = (x: Tensor): Tensor => x[activationName](); + } + + forward(gatedTensor: Tensor, gateInput: Tensor): Tensor { + const g = this.activation(this.tau.forward(gateInput)); + return gatedTensor.mul(g.size().length === gatedTensor.size().length ? g : g.unsqueeze(1)); + } +} diff --git a/examples/piston-train-toy/src/lib/workspace/config.svelte.ts b/examples/piston-train-toy/src/lib/workspace/config.svelte.ts index f21e73b4..f84d749b 100644 --- a/examples/piston-train-toy/src/lib/workspace/config.svelte.ts +++ b/examples/piston-train-toy/src/lib/workspace/config.svelte.ts @@ -126,6 +126,17 @@ const CONFIG_DEFAULTS: Config = { groupedQueryAttention: { present: false, queryHeadsPerKeyValueHead: 2 + }, + gating: { + present: true, + activation: 'sigmoid', + sites: { + afterSdpaOutput: true, + afterValueProjection: false, + afterKeyProjection: false, + afterQueryProjection: false, + afterFinalOutputProjection: false + } } }, positionalEncoding: { diff --git a/examples/piston-train-toy/src/lib/workspace/config.ts b/examples/piston-train-toy/src/lib/workspace/config.ts index 6eeb0365..2a318d56 100644 --- a/examples/piston-train-toy/src/lib/workspace/config.ts +++ b/examples/piston-train-toy/src/lib/workspace/config.ts @@ -85,6 +85,7 @@ export interface TransformerAttentionConfig { present: boolean; queryHeadsPerKeyValueHead: number; }; + gating: AttentionGatingConfig; } export interface DataConfig { @@ -130,6 +131,20 @@ export interface LayerNormalizationConfig { export type Activation = 'relu' | 'relu2' | 'gelu' | 'silu' | 'sigmoid' | 'swiglu' | 'tanh'; +export interface AttentionGatingSitesConfig { + afterSdpaOutput: boolean; + afterValueProjection: boolean; + afterKeyProjection: boolean; + afterQueryProjection: boolean; + afterFinalOutputProjection: boolean; +} + +export interface AttentionGatingConfig { + present: boolean; + activation: Activation; + sites: AttentionGatingSitesConfig; +} + export interface MLPConfig { present: boolean; activation: Activation; From 0511a88e5c608f400ff7db4bac4a9528bb72bf07 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 1 Nov 2025 16:31:39 -0600 Subject: [PATCH 568/590] Add attention sink column config --- .../lib/components/controls/Controls.svelte | 10 +++++ .../src/lib/train/model/modules/attention.ts | 40 ++++++++++++++++++- .../src/lib/workspace/config.svelte.ts | 3 ++ .../src/lib/workspace/config.ts | 3 ++ 4 files changed, 54 insertions(+), 2 deletions(-) diff --git a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte index 9a107c74..5795eb21 100644 --- a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte +++ b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte @@ -268,6 +268,16 @@ hasDefaultValue={equalsConfigDefault('model.transformer.attention.nKeyValueHeads')} onReset={() => resetConfigToDefaults('model.transformer.attention.nKeyValueHeads')} /> + resetConfigToDefaults('model.transformer.attention.sinks.present')} + /> Date: Sat, 1 Nov 2025 16:43:59 -0600 Subject: [PATCH 569/590] Add QK normalization --- .../lib/components/controls/Controls.svelte | 39 +++++++++++++++++++ .../src/lib/train/model/config.ts | 5 ++- .../src/lib/train/model/modules/attention.ts | 17 ++++++++ .../src/lib/workspace/config.svelte.ts | 7 ++++ .../src/lib/workspace/config.ts | 11 ++++++ 5 files changed, 78 insertions(+), 1 deletion(-) diff --git a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte index 5795eb21..2b827eb4 100644 --- a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte +++ b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte @@ -590,6 +590,45 @@ />
    + resetConfigToDefaults('model.transformer.normalization.qkNorm.present')} + > + resetConfigToDefaults('model.transformer.normalization.qkNorm.type')} + /> + resetConfigToDefaults('model.transformer.normalization.qkNorm.eps')} + /> + { return { diff --git a/examples/piston-train-toy/src/lib/train/model/modules/attention.ts b/examples/piston-train-toy/src/lib/train/model/modules/attention.ts index 71593f00..cb3ca76d 100644 --- a/examples/piston-train-toy/src/lib/train/model/modules/attention.ts +++ b/examples/piston-train-toy/src/lib/train/model/modules/attention.ts @@ -107,6 +107,8 @@ abstract class CoreAttention extends nn.Module { const B = q.size(0); const T_q = q.size(2); + // Optional QK norm and gating + [q, k] = this.applyQKNorm(q, k); [q, k, v] = this.applyQkvGating(input, q, k, v); // Optional RoPE with separate offsets for q and k @@ -210,6 +212,21 @@ abstract class CoreAttention extends nn.Module { })(); } + applyQKNorm(q: Tensor, k: Tensor): [Tensor, Tensor] { + if (this.config.normalization.qkNorm.present) { + if (this.config.normalization.qkNorm.type === 'rmsnorm') { + q = q.rmsNorm({ eps: this.config.normalization.qkNorm.eps }); + k = k.rmsNorm({ eps: this.config.normalization.qkNorm.eps }); + } else if (this.config.normalization.qkNorm.type === 'layernorm') { + q = q.layerNorm({ eps: this.config.normalization.qkNorm.eps }); + k = k.layerNorm({ eps: this.config.normalization.qkNorm.eps }); + } else { + throw new Error(`Unknown QKNorm type: ${this.config.normalization.qkNorm.type}`); + } + } + return [q, k]; + } + /** * Apply grouped-query attention broadcasting to key and value tensors * @param k Key tensor [B, nKvHeads, seqLen, headDim] diff --git a/examples/piston-train-toy/src/lib/workspace/config.svelte.ts b/examples/piston-train-toy/src/lib/workspace/config.svelte.ts index e0191b0a..1d3e081b 100644 --- a/examples/piston-train-toy/src/lib/workspace/config.svelte.ts +++ b/examples/piston-train-toy/src/lib/workspace/config.svelte.ts @@ -152,6 +152,13 @@ const CONFIG_DEFAULTS: Config = { base: 10000.0 } }, + normalization: { + qkNorm: { + present: true, + type: 'rmsnorm', + eps: 1e-5 + } + }, mlp: { present: true, activation: 'relu2', diff --git a/examples/piston-train-toy/src/lib/workspace/config.ts b/examples/piston-train-toy/src/lib/workspace/config.ts index d1127f9a..a5c2476d 100644 --- a/examples/piston-train-toy/src/lib/workspace/config.ts +++ b/examples/piston-train-toy/src/lib/workspace/config.ts @@ -132,6 +132,16 @@ export interface LayerNormalizationConfig { }; } +export interface QKNormConfig { + present: boolean; + type: NormalizationType; + eps: number; +} + +export interface NormalizationConfig { + qkNorm: QKNormConfig; +} + export type Activation = 'relu' | 'relu2' | 'gelu' | 'silu' | 'sigmoid' | 'swiglu' | 'tanh'; export interface AttentionGatingSitesConfig { @@ -177,6 +187,7 @@ export interface TransformerConfig { attention: TransformerAttentionConfig; positionalEncoding: PositionEncodingConfig; initialization: InitializationConfig; + normalization: NormalizationConfig; mlp: MLPConfig; } From 72781b38c1522d04253f109fccf3e7ce196a6796 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 1 Nov 2025 17:02:03 -0600 Subject: [PATCH 570/590] Add logit/attention soft-capping --- .../lib/components/controls/Controls.svelte | 62 +++++++++++++++++++ .../src/lib/train/model/modules/attention.ts | 5 ++ .../src/lib/train/model/modules/utils.ts | 5 ++ .../src/lib/train/model/transformer.ts | 10 ++- .../src/lib/train/model/utils.ts | 17 ++++- .../src/lib/workspace/config.svelte.ts | 10 +++ .../src/lib/workspace/config.ts | 12 ++++ 7 files changed, 118 insertions(+), 3 deletions(-) create mode 100644 examples/piston-train-toy/src/lib/train/model/modules/utils.ts diff --git a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte index 2b827eb4..b8b9ddb2 100644 --- a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte +++ b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte @@ -629,6 +629,68 @@ onReset={() => resetConfigToDefaults('model.transformer.normalization.qkNorm.eps')} /> + + + resetConfigToDefaults('model.transformer.normalization.softcap.attention.present')} + > + + resetConfigToDefaults('model.transformer.normalization.softcap.attention.value')} + /> + + + resetConfigToDefaults('model.transformer.normalization.softcap.logits.present')} + > + + resetConfigToDefaults('model.transformer.normalization.softcap.logits.value')} + /> + + Date: Sat, 1 Nov 2025 17:13:16 -0600 Subject: [PATCH 571/590] Add weight tying --- .../src/lib/components/controls/Controls.svelte | 11 +++++++++++ .../src/lib/train/model/bidirectional.ts | 17 +++++++++++++++-- .../src/lib/train/model/transformer.ts | 17 +++++++++++++++-- .../src/lib/train/model/utils.ts | 14 ++++++++++++-- .../src/lib/workspace/config.svelte.ts | 1 + .../src/lib/workspace/config.ts | 1 + 6 files changed, 55 insertions(+), 6 deletions(-) diff --git a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte index b8b9ddb2..2e12b44c 100644 --- a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte +++ b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte @@ -215,6 +215,17 @@ onReset={() => resetConfigToDefaults('model.transformer.headDim')} /> + resetConfigToDefaults('model.tieEmbeddingsAndLmHead')} + /> + + resetConfigToDefaults('training.labelSmoothing.present')} + > + resetConfigToDefaults('training.labelSmoothing.value')} + /> + Date: Sat, 1 Nov 2025 17:17:01 -0600 Subject: [PATCH 573/590] Add gated MLP variant, make default --- .../lib/components/controls/Controls.svelte | 21 +++++++++++++++++++ .../src/lib/train/model/modules/mlp.ts | 9 ++++++++ .../src/lib/workspace/config.svelte.ts | 3 ++- .../src/lib/workspace/config.ts | 1 + 4 files changed, 33 insertions(+), 1 deletion(-) diff --git a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte index ae6dd1af..1203d483 100644 --- a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte +++ b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte @@ -409,6 +409,27 @@ hasDefaultValue={equalsConfigDefault('model.transformer.mlp.present')} onReset={() => resetConfigToDefaults('model.transformer.mlp.present')} > + resetConfigToDefaults('model.transformer.mlp.variant')} + /> {getMlpIntermediateSize()} diff --git a/examples/piston-train-toy/src/lib/train/model/modules/mlp.ts b/examples/piston-train-toy/src/lib/train/model/modules/mlp.ts index 6556d893..6569fa28 100644 --- a/examples/piston-train-toy/src/lib/train/model/modules/mlp.ts +++ b/examples/piston-train-toy/src/lib/train/model/modules/mlp.ts @@ -3,6 +3,7 @@ import type { MLPConfig } from '$lib/workspace/config'; import { nn, Tensor } from '@piston-ml/piston-web'; export class MLP extends nn.Module { + private readonly gateProj: nn.Linear | undefined; private readonly upProj: nn.Linear; private readonly downProj: nn.Linear; private readonly activation: (x: Tensor) => Tensor; @@ -18,6 +19,10 @@ export class MLP extends nn.Module { const intermediateSize = this.config.hiddenExpansionFactor * nEmbed; + if (this.config.variant === 'gated') { + this.gateProj = new nn.Linear(nEmbed, intermediateSize); + } + this.upProj = new nn.Linear(nEmbed, intermediateSize); this.downProj = new nn.Linear(intermediateSize, nEmbed); @@ -32,6 +37,10 @@ export class MLP extends nn.Module { forward(input: Tensor): Tensor { let h = this.upProj.forward(input); h = this.activation(h); + if (this.gateProj) { + const gate = this.gateProj.forward(input); + h = h.mul(gate); + } return this.downProj.forward(h); } } diff --git a/examples/piston-train-toy/src/lib/workspace/config.svelte.ts b/examples/piston-train-toy/src/lib/workspace/config.svelte.ts index d340e617..215eeb41 100644 --- a/examples/piston-train-toy/src/lib/workspace/config.svelte.ts +++ b/examples/piston-train-toy/src/lib/workspace/config.svelte.ts @@ -177,7 +177,8 @@ const CONFIG_DEFAULTS: Config = { mlp: { present: true, activation: 'relu2', - hiddenExpansionFactor: 4 + hiddenExpansionFactor: 4, + variant: 'gated' } } }, diff --git a/examples/piston-train-toy/src/lib/workspace/config.ts b/examples/piston-train-toy/src/lib/workspace/config.ts index e987341a..40278c11 100644 --- a/examples/piston-train-toy/src/lib/workspace/config.ts +++ b/examples/piston-train-toy/src/lib/workspace/config.ts @@ -178,6 +178,7 @@ export interface MLPConfig { present: boolean; activation: Activation; hiddenExpansionFactor: number; + variant: 'standard' | 'gated'; } export type ModelType = 'decoder' | 'encoder' | 'encoder-decoder'; From 0546bbf5ec573413b091cabc1c65a6684a85c3b1 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 1 Nov 2025 17:27:23 -0600 Subject: [PATCH 574/590] parameter sum to verify inits are identical --- examples/piston-train-toy/src/lib/train/session.ts | 6 ++++++ .../piston-train-toy/src/lib/train/utils/model.ts | 14 +++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/examples/piston-train-toy/src/lib/train/session.ts b/examples/piston-train-toy/src/lib/train/session.ts index 7baceee2..3cc4c6fa 100644 --- a/examples/piston-train-toy/src/lib/train/session.ts +++ b/examples/piston-train-toy/src/lib/train/session.ts @@ -44,6 +44,7 @@ import { } from './utils/checkpoint'; import { calculateBlockSize, + calculateParameterSum, calculateVocabSize, createCollateFn, createDataloader, @@ -465,6 +466,11 @@ export class TrainingSession { // We need to flatten down initialization to the constant tensors they're on top of await piston.gpu.markStep(); + + const parameterSum = new BigUint64Array( + new Float64Array([await (await calculateParameterSum(this.model).to('cpu')).item()]).buffer + ); + console.debug(`Initialization parameter sum: ${parameterSum}`); } // Build or refresh capture plan using CaptureManager diff --git a/examples/piston-train-toy/src/lib/train/utils/model.ts b/examples/piston-train-toy/src/lib/train/utils/model.ts index 7e1d78b1..14bf5c95 100644 --- a/examples/piston-train-toy/src/lib/train/utils/model.ts +++ b/examples/piston-train-toy/src/lib/train/utils/model.ts @@ -1,6 +1,13 @@ import type { Random } from 'random-js'; -import { CaptureIndexMode, DataLoader, type IndexState, Tensor, weak } from '@piston-ml/piston-web'; +import { + CaptureIndexMode, + DataLoader, + type IndexState, + Module, + Tensor, + weak +} from '@piston-ml/piston-web'; import * as piston from '@piston-ml/piston-web'; import type { Config } from '../../workspace/config'; @@ -230,6 +237,11 @@ export function initializeModel( initTransformerParameters(model, config); } +export function calculateParameterSum(model: Module): Tensor { + const sums = model.parameters().map((param) => param.sum()); + return piston.stack(sums).sum(); +} + export function countParameters( model: DecoderTransformer | EncoderTransformer | EncoderDecoderTransformer ): number { From 8fcaf1ef9c7f05f47d91298ecd40fb668e99b67c Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 1 Nov 2025 17:32:57 -0600 Subject: [PATCH 575/590] Do a pass to standardize control ids --- .../lib/components/controls/Controls.svelte | 167 ++++++++---------- 1 file changed, 78 insertions(+), 89 deletions(-) diff --git a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte index 1203d483..35dcf26c 100644 --- a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte +++ b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte @@ -77,7 +77,7 @@ {#snippet projectionBlock(configName: 'attention' | 'mlp' | 'lmHead', displayName: string)} resetConfigToDefaults('model.transformer.positionalEncoding.present')} > resetConfigToDefaults('model.layerNormalization.transformer.present')} > -
    - resetConfigToDefaults('model.layerNormalization.type')} - /> - resetConfigToDefaults('model.layerNormalization.transformer.position')} - /> - resetConfigToDefaults('model.layerNormalization.eps')} - /> -
    + } + ]} + hasDefaultValue={equalsConfigDefault('model.layerNormalization.type')} + onReset={() => resetConfigToDefaults('model.layerNormalization.type')} + /> + resetConfigToDefaults('model.layerNormalization.transformer.position')} + /> + resetConfigToDefaults('model.layerNormalization.eps')} + />
    + resetConfigToDefaults('training.validation.valSteps')} /> resetConfigToDefaults('training.validation.batchSize')} /> From 750010e42afae041cd8710c84159cef2193de2e6 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sat, 1 Nov 2025 17:33:53 -0600 Subject: [PATCH 576/590] Do away with most obvious controls comments --- .../src/lib/components/controls/Controls.svelte | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte index 35dcf26c..19960b94 100644 --- a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte +++ b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte @@ -106,7 +106,6 @@ {/snippet}
    - - - - - - resetConfigToDefaults('training.labelSmoothing.value')} /> - - - - Date: Sat, 1 Nov 2025 17:33:59 -0600 Subject: [PATCH 577/590] Spacing --- .../piston-train-toy/src/lib/components/controls/Controls.svelte | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte index 19960b94..e614ed94 100644 --- a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte +++ b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte @@ -654,6 +654,7 @@ onReset={() => resetConfigToDefaults('model.transformer.normalization.qkNorm.eps')} /> + Date: Sun, 2 Nov 2025 19:50:22 -0700 Subject: [PATCH 578/590] Add RNNs in one blob, afterthought that they are --- .../src/lib/components/Visualize.svelte | 2 +- .../lib/components/controls/Controls.svelte | 1365 +++++++++++------ .../select/SelectModelTopology.svelte | 80 +- .../piston-train-toy/src/lib/train/capture.ts | 20 +- .../src/lib/train/generate.ts | 10 +- .../src/lib/train/model/config.ts | 70 +- .../src/lib/train/model/modules/oneHot.ts | 14 + .../src/lib/train/model/rnn.ts | 958 ++++++++++++ .../src/lib/train/model/utils.ts | 46 +- .../piston-train-toy/src/lib/train/session.ts | 40 +- .../piston-train-toy/src/lib/train/types.ts | 27 +- .../src/lib/train/utils/init.ts | 274 +++- .../src/lib/train/utils/model.ts | 60 +- .../src/lib/train/validation.ts | 35 +- .../src/lib/train/validationHelpers.ts | 23 +- .../src/lib/workspace/config.svelte.ts | 60 +- .../src/lib/workspace/config.ts | 92 +- .../src/lib/workspace/runs.svelte.ts | 7 +- .../lib/workspace/visualizationExamples.ts | 25 +- 19 files changed, 2625 insertions(+), 583 deletions(-) create mode 100644 examples/piston-train-toy/src/lib/train/model/modules/oneHot.ts create mode 100644 examples/piston-train-toy/src/lib/train/model/rnn.ts diff --git a/examples/piston-train-toy/src/lib/components/Visualize.svelte b/examples/piston-train-toy/src/lib/components/Visualize.svelte index ee4ccce1..46fe4a96 100644 --- a/examples/piston-train-toy/src/lib/components/Visualize.svelte +++ b/examples/piston-train-toy/src/lib/components/Visualize.svelte @@ -335,7 +335,7 @@ applyEffectiveScript(); } - const exampleOptions = $derived(getVisualizationExampleOptions()); + const exampleOptions = $derived(getVisualizationExampleOptions(config)); function moveEditorToQueryStart(idx: number | null | undefined) { if (idx == null || idx < 0) return; diff --git a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte index e614ed94..92330e71 100644 --- a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte +++ b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte @@ -105,6 +105,95 @@ {/snippet} +{#snippet layerNormalization()} + {#if config.model.family === 'rnn'} + resetConfigToDefaults('model.layerNormalization.rnn.withinCell')} + /> + resetConfigToDefaults('model.layerNormalization.rnn.betweenLayers')} + /> + {/if} + {#if config.model.family === 'transformer' || config.model.layerNormalization.rnn.withinCell || config.model.layerNormalization.rnn.betweenLayers} + resetConfigToDefaults('model.layerNormalization.type')} + /> + {#if config.model.family === 'transformer'} + resetConfigToDefaults('model.layerNormalization.transformer.position')} + /> + {/if} + resetConfigToDefaults('model.layerNormalization.eps')} + /> + {/if} +{/snippet} +
    toggleControlSection('model')} contentClass={collapsibleSectionClass} > + resetConfigToDefaults('model.family')} + /> +
    resetConfigToDefaults('model.topology')} /> + {#if config.model.family === 'rnn'} + resetConfigToDefaults('model.rnn.cellType')} + /> + {/if} + {#if config.model.topology !== 'encoder-decoder'} {/if} - resetConfigToDefaults('model.transformer.headDim')} - /> + {#if config.model.family === 'transformer'} + resetConfigToDefaults('model.transformer.headDim')} + /> + {/if} - resetConfigToDefaults('model.tieEmbeddingsAndLmHead')} - /> + {#if config.model.family !== 'rnn' || config.model.rnn.embedding.type !== 'one-hot'} + resetConfigToDefaults('model.tieEmbeddingsAndLmHead')} + /> + {/if} + + {#if config.model.family === 'rnn'} + {#if (config.model.family === 'rnn' && config.model.topology === 'encoder') || config.model.topology === 'encoder-decoder'} + resetConfigToDefaults('model.rnn.encoder.bidirectional')} + /> + {/if} + + resetConfigToDefaults('model.rnn.embedding.type')} + /> + {#if config.model.rnn.embedding.type === 'learned'} + resetConfigToDefaults('model.rnn.embedding.learned.size')} + /> + {/if} + + resetConfigToDefaults('model.rnn.separateHiddenSize.present')} + > + resetConfigToDefaults('model.rnn.separateHiddenSize.value')} + /> + + resetConfigToDefaults('model.rnn.hiddenStateProjection.present')} + > + resetConfigToDefaults('model.rnn.hiddenStateProjection.size')} + /> + + {/if} - resetConfigToDefaults('model.transformer.attention.present')} - > - resetConfigToDefaults('model.transformer.attention.nKeyValueHeads')} - /> - resetConfigToDefaults('model.transformer.attention.sinks.present')} - /> + {#if config.model.family === 'transformer'} - resetConfigToDefaults('model.transformer.attention.groupedQueryAttention.present')} + bind:enabled={config.model.transformer.attention.present} + hasDefaultValue={equalsConfigDefault('model.transformer.attention.present')} + onReset={() => resetConfigToDefaults('model.transformer.attention.present')} > - resetConfigToDefaults( - 'model.transformer.attention.groupedQueryAttention.queryHeadsPerKeyValueHead' - )} - /> - - - resetConfigToDefaults('model.transformer.attention.gating.present')} - > - resetConfigToDefaults('model.transformer.attention.gating.activation')} + hasDefaultValue={equalsConfigDefault('model.transformer.attention.nKeyValueHeads')} + onReset={() => resetConfigToDefaults('model.transformer.attention.nKeyValueHeads')} /> - resetConfigToDefaults('model.transformer.attention.gating.sites.afterSdpaOutput')} - /> - - resetConfigToDefaults('model.transformer.attention.gating.sites.afterValueProjection')} - /> - - resetConfigToDefaults('model.transformer.attention.gating.sites.afterKeyProjection')} - /> - - resetConfigToDefaults('model.transformer.attention.gating.sites.afterQueryProjection')} + id="model-attention-sinks" + label="Attention Sinks" + citations={{ + entries: [ + { name: 'Gu et al., 2025', url: 'https://openreview.net/forum?id=78Nn4QJTEN' } + ] + }} + bind:checked={config.model.transformer.attention.sinks.present} + hasDefaultValue={equalsConfigDefault('model.transformer.attention.sinks.present')} + onReset={() => resetConfigToDefaults('model.transformer.attention.sinks.present')} /> - - resetConfigToDefaults( - 'model.transformer.attention.gating.sites.afterFinalOutputProjection' - )} - /> - - - - resetConfigToDefaults('model.transformer.mlp.present')} - > - + resetConfigToDefaults('model.transformer.mlp.variant')} - /> - - {getMlpIntermediateSize()} - - resetConfigToDefaults('model.transformer.mlp.activation')} - /> - resetConfigToDefaults('model.transformer.mlp.hiddenExpansionFactor')} - /> - + min={2} + max={32} + step={1} + base={2} + hasDefaultValue={equalsConfigDefault( + 'model.transformer.attention.groupedQueryAttention.queryHeadsPerKeyValueHead' + )} + onReset={() => + resetConfigToDefaults( + 'model.transformer.attention.groupedQueryAttention.queryHeadsPerKeyValueHead' + )} + /> + - resetConfigToDefaults('model.transformer.positionalEncoding.present')} - > - resetConfigToDefaults('model.transformer.attention.gating.present')} + > + resetConfigToDefaults('model.transformer.attention.gating.activation')} + /> + + resetConfigToDefaults('model.transformer.attention.gating.sites.afterSdpaOutput')} + /> + + resetConfigToDefaults( + 'model.transformer.attention.gating.sites.afterValueProjection' + )} + /> + + resetConfigToDefaults('model.transformer.attention.gating.sites.afterKeyProjection')} + /> + + resetConfigToDefaults( + 'model.transformer.attention.gating.sites.afterQueryProjection' + )} + /> + + resetConfigToDefaults( + 'model.transformer.attention.gating.sites.afterFinalOutputProjection' + )} + /> + + + + resetConfigToDefaults('model.transformer.mlp.present')} + > + resetConfigToDefaults('model.transformer.positionalEncoding.type')} - /> - {#if config.model.transformer.positionalEncoding.type === 'rope'} - resetConfigToDefaults('model.transformer.positionalEncoding.rope.base')} + ]} + hasDefaultValue={equalsConfigDefault('model.transformer.mlp.variant')} + onReset={() => resetConfigToDefaults('model.transformer.mlp.variant')} + /> + + {getMlpIntermediateSize()} + + resetConfigToDefaults('model.transformer.mlp.activation')} /> - {/if} - {#if config.model.transformer.positionalEncoding.type === 'alibi'} - resetConfigToDefaults('model.transformer.positionalEncoding.alibi.maxBias')} + hasDefaultValue={equalsConfigDefault('model.transformer.mlp.hiddenExpansionFactor')} + onReset={() => resetConfigToDefaults('model.transformer.mlp.hiddenExpansionFactor')} /> - {/if} - + - resetConfigToDefaults('model.layerNormalization.transformer.present')} + bind:enabled={config.model.transformer.positionalEncoding.present} + hasDefaultValue={equalsConfigDefault('model.transformer.positionalEncoding.present')} + onReset={() => resetConfigToDefaults('model.transformer.positionalEncoding.present')} > resetConfigToDefaults('model.layerNormalization.type')} - /> - resetConfigToDefaults('model.layerNormalization.transformer.position')} - /> - resetConfigToDefaults('model.layerNormalization.eps')} + hasDefaultValue={equalsConfigDefault('model.transformer.positionalEncoding.type')} + onReset={() => resetConfigToDefaults('model.transformer.positionalEncoding.type')} /> + {#if config.model.transformer.positionalEncoding.type === 'rope'} + resetConfigToDefaults('model.transformer.positionalEncoding.rope.base')} + /> + {/if} + {#if config.model.transformer.positionalEncoding.type === 'alibi'} + + resetConfigToDefaults('model.transformer.positionalEncoding.alibi.maxBias')} + /> + {/if} - resetConfigToDefaults('model.rnn.encoderDecoderAttention.present')} + > + resetConfigToDefaults('model.rnn.encoderDecoderAttention.type')} + /> + {#if config.model.rnn.encoderDecoderAttention.type === 'multiplicative'} + + resetConfigToDefaults( + 'model.rnn.encoderDecoderAttention.multiplicative.scaleByInverseSqrtHiddenSize' + )} + /> + {/if} + + resetConfigToDefaults('model.rnn.encoderDecoderAttention.inputFeedingProjection')} + /> + + {/if} + {/if} + + {#if config.model.family === 'transformer'} + + resetConfigToDefaults('model.layerNormalization.transformer.present')} + > + {@render layerNormalization()} + + resetConfigToDefaults('model.transformer.normalization.qkNorm.present')} + > + resetConfigToDefaults('model.transformer.normalization.qkNorm.type')} + /> + resetConfigToDefaults('model.transformer.normalization.qkNorm.eps')} + /> + + + + + resetConfigToDefaults('model.transformer.normalization.softcap.attention.present')} + > + + resetConfigToDefaults('model.transformer.normalization.softcap.attention.value')} + /> + + + resetConfigToDefaults('model.transformer.normalization.softcap.logits.present')} + > + + resetConfigToDefaults('model.transformer.normalization.softcap.logits.value')} + /> + + + + {:else} + resetConfigToDefaults('model.transformer.normalization.qkNorm.present')} > - resetConfigToDefaults('model.transformer.normalization.qkNorm.type')} - /> + {@render layerNormalization()} + + {/if} + + resetConfigToDefaults(`model.${config.model.family}.initialization.present`)} + > + {#if config.model.family === 'transformer'} resetConfigToDefaults('model.transformer.normalization.qkNorm.eps')} + step={0.0001} + hasDefaultValue={equalsConfigDefault('model.transformer.initialization.std')} + onReset={() => resetConfigToDefaults('model.transformer.initialization.std')} /> - - - - + {@render projectionBlock('attention', 'Attention')} + {@render projectionBlock('mlp', 'MLP')} + {@render projectionBlock('lmHead', 'LM Head')} + + {:else if config.model.family === 'rnn'} + - resetConfigToDefaults('model.transformer.normalization.softcap.attention.present')} - > - - resetConfigToDefaults('model.transformer.normalization.softcap.attention.value')} - /> - + resetConfigToDefaults(`model.rnn.initialization.orthogonalRecurrentColumns`)} + /> + resetConfigToDefaults(`model.rnn.initialization.perGateOrthogonalBlocks`)} + /> + resetConfigToDefaults(`model.rnn.initialization.zeroBiases`)} + /> - resetConfigToDefaults('model.transformer.normalization.softcap.logits.present')} + resetConfigToDefaults(`model.rnn.initialization.xavierInputColumns.present`)} > - - resetConfigToDefaults('model.transformer.normalization.softcap.logits.value')} + resetConfigToDefaults(`model.rnn.initialization.xavierInputColumns.distribution`)} /> - - - - resetConfigToDefaults(`model.transformer.initialization.present`)} - > - resetConfigToDefaults('model.transformer.initialization.std')} - /> - - {@render projectionBlock('attention', 'Attention')} - {@render projectionBlock('mlp', 'MLP')} - {@render projectionBlock('lmHead', 'LM Head')} - + {#if config.model.layerNormalization.type === 'layernorm'} + {#if config.model.rnn.cellType === 'gru'} + + resetConfigToDefaults(`model.rnn.initialization.gru.updateGateBias.present`)} + > + + resetConfigToDefaults(`model.rnn.initialization.gru.updateGateBias.value`)} + /> + + {:else if config.model.rnn.cellType === 'lstm'} + + resetConfigToDefaults(`model.rnn.initialization.lstm.forgetGateBias.present`)} + > + + resetConfigToDefaults(`model.rnn.initialization.lstm.forgetGateBias.value`)} + /> + + {/if} + {/if} + {/if} @@ -945,54 +1343,57 @@ onReset={() => resetConfigToDefaults('training.labelSmoothing.value')} /> - resetConfigToDefaults('training.dropout.present')} - > - resetConfigToDefaults('training.dropout.embedding')} - /> - resetConfigToDefaults('training.dropout.transformer.attention')} - /> - resetConfigToDefaults('training.dropout.transformer.residual')} - /> - + + {#if config.model.family === 'transformer'} + resetConfigToDefaults('training.dropout.present')} + > + resetConfigToDefaults('training.dropout.embedding')} + /> + resetConfigToDefaults('training.dropout.transformer.attention')} + /> + resetConfigToDefaults('training.dropout.transformer.residual')} + /> + + {/if} ).tensors; @@ -145,14 +146,19 @@ export async function runValidationExampleForCapture( targets: await decoderTargets.to('gpu') }); modelName = 'encoder-decoder'; - } else if (model instanceof EncoderTransformer) { + } else if (model instanceof EncoderTransformer || model instanceof RNNEncoder) { // Encoder-only: compute MLM loss over masked tokens const [inputs, labels, attentionMask] = (collated as BidirectionalBatchType).tensors; modelName = 'encoder-only'; - [, , , loss] = model.forward(await inputs.to('gpu'), { - attentionMask: await attentionMask.to('gpu'), - targets: await labels.to('gpu') - }); + if (model instanceof EncoderTransformer) { + [, , , loss] = model.forward(await inputs.to('gpu'), { + attentionMask: await attentionMask.to('gpu'), + targets: await labels.to('gpu') + }); + } else { + // No attention mask here + [, , loss] = model.forward(await inputs.to('gpu'), { targets: await labels.to('gpu') }); + } } else { throw new Error('Unsupported model for validation'); } diff --git a/examples/piston-train-toy/src/lib/train/generate.ts b/examples/piston-train-toy/src/lib/train/generate.ts index 6818ab32..d49859a0 100644 --- a/examples/piston-train-toy/src/lib/train/generate.ts +++ b/examples/piston-train-toy/src/lib/train/generate.ts @@ -9,6 +9,7 @@ import { int32, tensor, WeakTensorFunctionMode } from '@piston-ml/piston-web'; import type { GeneratableModel } from './types'; import { createEmptyDecoderKVCache, type DecoderKVCache } from './model/cache'; +import { RNNDecoder, RNNEncoderDecoder } from './model/rnn'; import { DecoderTransformer, EncoderDecoderTransformer } from './model/transformer'; export interface GenerationConfig { @@ -322,9 +323,16 @@ export async function* generateStream( if (model instanceof EncoderDecoderTransformer) { // This is a Transformer (encoder-decoder) model return yield* generateTransformerStream(model, input, config); - } else if (model instanceof DecoderTransformer) { + } else if (model instanceof DecoderTransformer || model instanceof RNNDecoder) { // This is a GPT (decoder-only) model return yield* generateGPTStream(model as DecoderTransformer, input, config); + } else if (model instanceof RNNEncoderDecoder) { + // RNN encoder-decoder: reuse transformer-style generation loop + return yield* generateTransformerStream( + model as unknown as EncoderDecoderTransformer, + input, + config + ); } else { throw new Error('Unsupported model type for generation'); } diff --git a/examples/piston-train-toy/src/lib/train/model/config.ts b/examples/piston-train-toy/src/lib/train/model/config.ts index dc5afafd..396f29d2 100644 --- a/examples/piston-train-toy/src/lib/train/model/config.ts +++ b/examples/piston-train-toy/src/lib/train/model/config.ts @@ -1,12 +1,16 @@ import type { Config, - DropoutConfig, - InitializationConfig, LayerNormalizationConfig, MLPConfig, PositionEncodingConfig, + RNNDropoutConfig, + RNNEmbeddingConfig, + RNNHiddenStateProjectionConfig, + RNNInitializationConfig, TransformerAttentionConfig, - NormalizationConfig + TransformerDropoutConfig, + TransformerInitializationConfig, + TransformerNormalizationConfig } from '$lib/workspace/config'; export interface TransformerModuleConfig { @@ -14,11 +18,63 @@ export interface TransformerModuleConfig { embeddingSize: number; attention: TransformerAttentionConfig; layerNormalization: LayerNormalizationConfig; - normalization: NormalizationConfig; + normalization: TransformerNormalizationConfig; mlp: MLPConfig; positionalEncoding: PositionEncodingConfig; - dropout: DropoutConfig; - initialization: InitializationConfig; + dropout: TransformerDropoutConfig; + initialization: TransformerInitializationConfig; +} + +export interface RNNModuleConfig { + cellType: 'gru' | 'lstm' | 'rnn'; + embeddingSize: number; + vocabSize: number; + hiddenSize: number; + baseHiddenSize: number; + projectionSize?: number; + embedding: RNNEmbeddingConfig; + dropout: RNNDropoutConfig; + layerNormalization: LayerNormalizationConfig; + initialization: RNNInitializationConfig; + hiddenStateProjection: RNNHiddenStateProjectionConfig; + tieEmbeddingsAndLmHead: boolean; +} + +export function buildRNNConfigCommon(config: Config, vocabSize: number): RNNModuleConfig { + const effectiveEmbeddingSize = + config.model.rnn.embedding.type === 'learned' + ? config.model.rnn.embedding.learned.size + : vocabSize; + const rawHiddenSize = config.model.rnn.separateHiddenSize.present + ? config.model.rnn.separateHiddenSize.value + : effectiveEmbeddingSize; + const projectionSize = config.model.rnn.hiddenStateProjection.present + ? config.model.rnn.hiddenStateProjection.size + : undefined; + const baseHiddenSize = projectionSize ?? rawHiddenSize; + + return { + vocabSize: vocabSize, + cellType: config.model.rnn.cellType, + embeddingSize: effectiveEmbeddingSize, + hiddenSize: rawHiddenSize, + baseHiddenSize, + projectionSize, + embedding: config.model.rnn.embedding, + layerNormalization: config.model.layerNormalization, + hiddenStateProjection: config.model.rnn.hiddenStateProjection, + initialization: config.model.rnn.initialization, + tieEmbeddingsAndLmHead: config.model.tieEmbeddingsAndLmHead, + dropout: (({ present, embedding: embd, rnn }: RNNDropoutConfig) => { + return { + present, + embedding: present ? embd : 0, + rnn: { + interLayer: present ? rnn.interLayer : 0 + } + }; + })(config.training.dropout) + }; } export function buildTransformerConfigCommon( @@ -41,7 +97,7 @@ export function buildTransformerConfigCommon( layerNormalization: config.model.layerNormalization, normalization: config.model.transformer.normalization, initialization: config.model.transformer.initialization, - dropout: (({ present, embedding: embd, transformer }: DropoutConfig) => { + dropout: (({ present, embedding: embd, transformer }: TransformerDropoutConfig) => { return { present, embedding: present ? embd : 0, diff --git a/examples/piston-train-toy/src/lib/train/model/modules/oneHot.ts b/examples/piston-train-toy/src/lib/train/model/modules/oneHot.ts new file mode 100644 index 00000000..ea5cf045 --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/model/modules/oneHot.ts @@ -0,0 +1,14 @@ +import { float32, nn, oneHot, type Tensor } from '@piston-ml/piston-web'; + +export class OneHotEmbedding extends nn.Module { + private readonly vocabSize: number; + + constructor(vocabSize: number) { + super(); + this.vocabSize = vocabSize; + } + + forward(inputIds: Tensor): Tensor { + return oneHot(inputIds, this.vocabSize).cast(float32); + } +} diff --git a/examples/piston-train-toy/src/lib/train/model/rnn.ts b/examples/piston-train-toy/src/lib/train/model/rnn.ts new file mode 100644 index 00000000..2dfc8e3e --- /dev/null +++ b/examples/piston-train-toy/src/lib/train/model/rnn.ts @@ -0,0 +1,958 @@ +/** + * @fileoverview Decoder-only RNN models (LSTM/GRU) styled similarly to GPT + */ + +import type { + Config, + LayerNormalizationConfig, + MultiplicativeRNNAttentionConfig, + RNNAttentionConfig +} from '$lib/workspace/config'; +import type { CrossEntropyLoss, Tensor } from '@piston-ml/piston-web'; + +import * as piston from '@piston-ml/piston-web'; +import { nn } from '@piston-ml/piston-web'; + +import { MLMHead } from './bidirectional'; +import { buildRNNConfigCommon, type RNNModuleConfig } from './config'; +import { createNorm, type NormModule } from './modules/norm'; +import { + buildLmHeadRNN, + computeAutoregressiveCrossEntropyLoss, + createCrossEntropyCriterion, + createPossiblyOneHotRNNWordEmbedding, + maskedFill, + type RNNWordEmbedding +} from './utils'; + +export type RNNEncoderConfig = RNNModuleConfig & { + typeVocabSize: number; + nLayers: number; + bidirectional: boolean; + mlmHead: { present: boolean; shareEmbeddings: boolean }; +}; + +export type RNNDecoderConfig = RNNModuleConfig & { + nLayers: number; +}; + +export type RNNEncoderDecoderConfig = RNNModuleConfig & { + nEncoderLayer: number; + nDecoderLayer: number; + bidirectional: boolean; + encoderDecoderAttention: RNNAttentionConfig; +}; + +type RNNCellType = GRUCell | LSTMCell | RNNCell; + +// Optional attention inputs for sequence decoding inside BaseRNN +type RNNForwardAttentionOptions = { + attentionModule: AdditiveRNNAttention | MultiplicativeRNNAttention; + encoderOutputs: Tensor; + srcPaddingMask?: Tensor | null; + outProjection: nn.Linear; // projects concat([hidden, context]) -> hidden + inputFeedingProjection?: nn.Linear; // projects concat([x_t, prevContext]) -> x_t + decoderStateProjection?: nn.Linear; // projects decoder state H -> baseHidden if needed +}; + +abstract class BaseRNN extends nn.Module { + protected readonly layer: nn.ModuleList; + protected readonly interLayerDropout?: nn.Dropout; + protected readonly interLayerNorms?: nn.ModuleList; + protected readonly nLayers: number; + protected readonly bidirectional: boolean; + protected readonly hiddenSize: number; + protected readonly projectionSize?: number; + protected readonly projections?: nn.ModuleList< + nn.ModuleDict<{ forwardProj: nn.Linear; reverseProj?: nn.Linear }>[] + >; + + constructor( + inputSize: number, + hiddenSize: number, + layerNormalization: LayerNormalizationConfig, + options: RNNOptions = {} + ) { + super(); + const nLayers = options.nLayers ?? 1; + const bidirectional = options.bidirectional ?? false; + const dropout = options.dropout ?? 0; + const projectionSize = options.projectionSize; + + this.nLayers = nLayers; + this.bidirectional = bidirectional; + this.hiddenSize = hiddenSize; + this.projectionSize = projectionSize; + + const cells: CellType[] = []; + for (let l = 0; l < nLayers; l++) { + // Input size calculation: first layer uses inputSize, subsequent layers use previous layer + // output size + let layerInputSize: number; + if (l === 0) { + layerInputSize = inputSize; + } else { + // Previous layer output size depends on projection and bidirectionality + const prevLayerOutputSize = projectionSize || hiddenSize; + layerInputSize = bidirectional ? prevLayerOutputSize * 2 : prevLayerOutputSize; + } + cells.push(this.createCell(layerInputSize, hiddenSize, layerNormalization)); + } + this.layer = new nn.ModuleList(cells); + + // Create projection layers if projectionSize is specified + if (projectionSize) { + const projLayers: nn.ModuleDict<{ forwardProj: nn.Linear; reverseProj?: nn.Linear }>[] = []; + for (let l = 0; l < nLayers; l++) { + const layerProjs: { forwardProj: nn.Linear; reverseProj?: nn.Linear } = { + forwardProj: new nn.Linear(hiddenSize, projectionSize) + }; + if (bidirectional) { + layerProjs.reverseProj = new nn.Linear(hiddenSize, projectionSize); + } + projLayers.push(new nn.ModuleDict(layerProjs)); + } + this.projections = new nn.ModuleList(projLayers); + } + + if (layerNormalization.rnn.betweenLayers && nLayers > 1) { + const normDim = (bidirectional ? 2 : 1) * (projectionSize ?? hiddenSize); + const norms: nn.Module[] = []; + for (let l = 0; l < nLayers - 1; l++) { + norms.push( + createNorm(normDim, layerNormalization) as unknown as nn.Module + ); + } + this.interLayerNorms = new nn.ModuleList(norms); + } + + if (dropout > 0) { + this.interLayerDropout = new nn.Dropout(dropout); + } + + this.resetParameters(); + } + + resetParameters(): void { + // This is consistent with what PyTorch does internally + // Initialize only linear layers; leave norms at their defaults (gamma=1, beta=0) + const stdv = 1.0 / Math.sqrt(this.hiddenSize); + this.apply((module: nn.Module) => { + if (module instanceof nn.Linear) { + piston.initUniform_(module.weight, { low: -stdv, high: stdv }); + if (module.bias) { + piston.initUniform_(module.bias, { low: -stdv, high: stdv }); + } + } + }); + } + + protected abstract createCell( + inputSize: number, + hiddenSize: number, + layerNormalization: LayerNormalizationConfig + ): CellType; + + private stepThroughCell( + cell: CellType, + x_t: Tensor, + hPrev: Tensor, + cPrev: Tensor | null + ): [Tensor, Tensor | null] { + if (cell instanceof LSTMCell) { + const [hNew, cNew] = cell.forward(x_t, hPrev, cPrev!); + return [hNew, cNew]; + } else { + const hNew = (cell as GRUCell | RNNCell).forward(x_t, hPrev); + return [hNew, null]; + } + } + + private applyProjection( + projection: nn.Linear | undefined, + outputs: Tensor, + batchSize: number, + seqLen: number + ): Tensor { + if (!projection) { + return outputs; + } + return projection + .forward(outputs.view([-1, this.hiddenSize])) + .view([batchSize, seqLen, this.projectionSize!]); + } + + forward( + x: Tensor, + initial?: Parameters[1], + attention?: RNNForwardAttentionOptions + ): [Tensor, Tensor, Tensor | null] { + if (attention && this.bidirectional) { + throw new Error('Attention decoding with bidirectional RNN is not supported'); + } + + let layerInput = x; + + // Track final states for each layer + const finalHiddenStates: Tensor[] = []; + const finalCellStates: (Tensor | null)[] = []; + + for (let l = 0; l < this.nLayers; l++) { + const [layerOutput, layerFinalH, layerFinalC] = this.runSingleLayer( + layerInput, + l, + l === this.nLayers - 1 ? initial : undefined, + l === this.nLayers - 1 ? attention : undefined + ); + + finalHiddenStates.push(layerFinalH); + finalCellStates.push(layerFinalC); + + // Apply between-layer normalization and dropout (excluding the last layer) + if (l < this.nLayers - 1) { + let nextInput: Tensor = layerOutput; + if (this.interLayerNorms) { + nextInput = this.interLayerNorms[l]!.forward(nextInput) as Tensor; + } + if (this.interLayerDropout) { + nextInput = this.interLayerDropout.forward(nextInput) as Tensor; + } + layerInput = nextInput; + } else { + layerInput = layerOutput; + } + } + + // Return the output of the last layer and concatenated final states + const finalH = piston.cat(finalHiddenStates, { dim: 0 }); + let finalC: Tensor | null = null; + if (finalCellStates.some((c) => c !== null)) { + const validCellStates = finalCellStates.filter((c) => c !== null) as Tensor[]; + if (validCellStates.length > 0) { + finalC = piston.cat(validCellStates, { dim: 0 }); + } + } + + return [layerInput, finalH, finalC]; + } + + private runSingleLayer( + yIn: Tensor, + layerIdx: number, + initial?: Tensor | [Tensor, Tensor], + attention?: RNNForwardAttentionOptions + ): [Tensor, Tensor, Tensor | null] { + const [batchSize, seqLen, _hiddenSize] = yIn.size(); + const cell = this.layer[layerIdx]!; + + // Get the projection module dict for this layer + const layerProjections = + this.projectionSize && this.projections ? this.projections[layerIdx] : null; + + const initializeStates = ( + initialForLayer?: Tensor | [Tensor, Tensor] + ): { + hState: Tensor; + cState: Tensor | null; + } => { + if (initialForLayer) { + if (Array.isArray(initialForLayer)) { + return { hState: initialForLayer[0], cState: initialForLayer[1] }; + } else { + return { hState: initialForLayer, cState: null }; + } + } else { + // Create initial state directly with correct hidden size (avoid slicing yIn) + const hState = piston.zeros([batchSize, this.hiddenSize], { device: yIn.device }); + const cState = cell instanceof LSTMCell ? piston.zerosLike(hState) : null; + return { hState, cState }; + } + }; + + const runDirection = ( + seq: Tensor, + allowAttention: boolean, + initialForLayer?: Tensor | [Tensor, Tensor] + ): [Tensor, Tensor, Tensor | null] => { + const { hState, cState } = initializeStates(initialForLayer); + let currentH = hState; + let currentC = cState; + let prevContext: Tensor | null = null; + const outputs: Tensor[] = []; + + for (let t = 0; t < seqLen; t++) { + let x_t = seq + .slice([ + [0, batchSize], + [t, t + 1], + [0, seq.size(2)] + ]) + .squeeze({ dim: 1 }); + + if (allowAttention && attention && attention.inputFeedingProjection && prevContext) { + const cat = piston.cat([x_t, prevContext], { dim: 1 }); + x_t = attention.inputFeedingProjection.forward(cat); + } + + const [newH, newC] = this.stepThroughCell(cell, x_t, currentH, currentC); + currentH = newH; + currentC = newC; + + let stepOut: Tensor; + if (allowAttention && attention) { + const query = attention.decoderStateProjection + ? attention.decoderStateProjection.forward(currentH) + : currentH; + const [context] = attention.attentionModule.forward( + query, + attention.encoderOutputs, + attention.srcPaddingMask ?? null + ); + const combined = piston.cat([query, context], { dim: 1 }); + stepOut = attention.outProjection.forward(combined); + prevContext = context; + } else { + stepOut = currentH; + } + + outputs.push(stepOut.unsqueeze(1)); + } + + const y = piston.cat(outputs, { dim: 1 }); + return [y, currentH, currentC]; + }; + + // Forward direction + const [yF, lastHF, lastCF] = runDirection(yIn, !!attention, initial); + + const projectedOutputsForward = this.applyProjection( + layerProjections?.dict.forwardProj, + yF, + batchSize, + seqLen + ); + const projectedLastHForward = layerProjections?.dict.forwardProj?.forward(lastHF) ?? lastHF; + + if (!this.bidirectional) { + return [projectedOutputsForward, projectedLastHForward, lastCF]; + } + + // Backward direction (reverse sequence) + const [yB, lastHB, lastCB] = runDirection(yIn.flip(1), false); + // Flip back to align with forward + const yBFlipped = yB.flip(1); + + // Apply projections if specified + const projectedOutputsReverse = this.applyProjection( + layerProjections?.dict.reverseProj, + yBFlipped, + batchSize, + seqLen + ); + + const projectedLastHB = layerProjections?.dict.reverseProj?.forward(lastHB) ?? lastHB; + + // Concatenate forward and backward outputs + const y = piston.cat([projectedOutputsForward, projectedOutputsReverse], { dim: 2 }); + const lastH = piston.cat([projectedLastHForward, projectedLastHB], { dim: 1 }); + + let lastC: Tensor | null = null; + if (lastCF && lastCB) { + lastC = piston.cat([lastCF, lastCB], { dim: 1 }); + } + + return [y, lastH, lastC]; + } +} + +export class GRUCell extends nn.Module { + readonly WGates: nn.Linear; + readonly WCandidate: nn.Linear; + readonly normGates?: NormModule; + readonly normCandidate?: NormModule; + + constructor(inputSize: number, hiddenSize: number, layerNormalization: LayerNormalizationConfig) { + super(); + + this.WGates = new nn.Linear(inputSize + hiddenSize, 2 * hiddenSize); + this.WCandidate = new nn.Linear(inputSize + hiddenSize, hiddenSize); + + if (layerNormalization.rnn.withinCell) { + this.normGates = createNorm(2 * hiddenSize, layerNormalization); + this.normCandidate = createNorm(hiddenSize, layerNormalization); + } + } + + // Unified step API used by sequence runners + forward(x: Tensor, hPrev: Tensor): Tensor { + const combined = piston.cat([x, hPrev], { dim: 1 }); + let gates = this.WGates.forward(combined); + if (this.normGates) { + gates = this.normGates.forward(gates); + } + let [r, z] = piston.chunk(gates, 2, { dim: 1 }); + r = piston.sigmoid(r); + z = piston.sigmoid(z); + const combinedCandidate = piston.cat([x, r.mul(hPrev)], { dim: 1 }); + let candidate = this.WCandidate.forward(combinedCandidate); + if (this.normCandidate) { + candidate = this.normCandidate.forward(candidate); + } + const hTilde = piston.tanh(candidate); + const hNext = hPrev.mul(z.neg().add(1)).add(hTilde.mul(z)); + return hNext; + } +} + +export class LSTMCell extends nn.Module { + readonly W: nn.Linear; + readonly normGates?: NormModule; + readonly normCell?: NormModule; + + constructor(inputSize: number, hiddenSize: number, layerNormalization: LayerNormalizationConfig) { + super(); + this.W = new nn.Linear(inputSize + hiddenSize, 4 * hiddenSize); + if (layerNormalization.rnn.withinCell) { + this.normGates = createNorm(4 * hiddenSize, layerNormalization); + this.normCell = createNorm(hiddenSize, layerNormalization); + } + } + + // Unified step API used by sequence runners + forward(x: Tensor, hPrev: Tensor, cPrev: Tensor): [Tensor, Tensor] { + const combined = piston.cat([x, hPrev], { dim: 1 }); + let gateOutputs = this.W.forward(combined); + if (this.normGates) { + gateOutputs = this.normGates.forward(gateOutputs) as Tensor; + } + let [i, f, g, o] = piston.chunk(gateOutputs, 4, { dim: 1 }); + i = piston.sigmoid(i); + f = piston.sigmoid(f); + g = piston.tanh(g); + o = piston.sigmoid(o); + let cNext = f.mul(cPrev).add(i.mul(g)); + if (this.normCell) { + cNext = this.normCell.forward(cNext) as Tensor; + } + const hNext = o.mul(piston.tanh(cNext)); + return [hNext, cNext]; + } +} + +export class RNNCell extends nn.Module { + readonly WIh: nn.Linear; + readonly WHh: nn.Linear; + readonly normPreact?: NormModule; + constructor(inputSize: number, hiddenSize: number, layerNormalization: LayerNormalizationConfig) { + super(); + this.WIh = new nn.Linear(inputSize, hiddenSize); + this.WHh = new nn.Linear(hiddenSize, hiddenSize); + if (layerNormalization.rnn.withinCell) { + this.normPreact = createNorm(hiddenSize, layerNormalization); + } + } + + // Unified step API used by sequence runners + forward(x: Tensor, hPrev: Tensor): Tensor { + let preact = this.WIh.forward(x).add(this.WHh.forward(hPrev)); + if (this.normPreact) { + preact = this.normPreact.forward(preact) as Tensor; + } + return preact.tanh(); + } +} + +export interface RNNOptions { + nLayers?: number; + bidirectional?: boolean; + dropout?: number; + projectionSize?: number; +} + +class GRU extends BaseRNN { + protected createCell( + inputSize: number, + hiddenSize: number, + layerNormalization: LayerNormalizationConfig + ): GRUCell { + return new GRUCell(inputSize, hiddenSize, layerNormalization); + } +} + +class LSTM extends BaseRNN { + protected createCell( + inputSize: number, + hiddenSize: number, + layerNormalization: LayerNormalizationConfig + ): LSTMCell { + return new LSTMCell(inputSize, hiddenSize, layerNormalization); + } +} + +class RNN extends BaseRNN { + protected createCell( + inputSize: number, + hiddenSize: number, + layerNormalization: LayerNormalizationConfig + ): RNNCell { + return new RNNCell(inputSize, hiddenSize, layerNormalization); + } +} + +export function createRNN( + cellType: 'gru' | 'lstm' | 'rnn', + inputSize: number, + hiddenSize: number, + layerNormalization: LayerNormalizationConfig, + options?: RNNOptions +): GRU | LSTM | RNN { + switch (cellType) { + case 'gru': + return new GRU(inputSize, hiddenSize, layerNormalization, options); + case 'lstm': + return new LSTM(inputSize, hiddenSize, layerNormalization, options); + case 'rnn': + return new RNN(inputSize, hiddenSize, layerNormalization, options); + default: + throw new Error(`Invalid RNN cell type: ${cellType}`); + } +} + +class AdditiveRNNAttention extends nn.Module { + private readonly Wh: nn.Linear; + private readonly Ws: nn.Linear; + private readonly v: nn.Linear; // projects attn dim -> 1 + + constructor(hiddenSize: number, attnDim: number) { + super(); + this.Wh = new nn.Linear(hiddenSize, attnDim); + this.Ws = new nn.Linear(hiddenSize, attnDim); + this.v = new nn.Linear(attnDim, 1); + } + + forward( + decoderState: Tensor, + encoderOutputs: Tensor, + srcPaddingMask: Tensor | null = null + ): [Tensor, Tensor] { + const [B, S, _H] = encoderOutputs.size(); + const projEnc = this.Wh.forward(encoderOutputs); // [B,S,A] + const projDec = this.Ws.forward(decoderState) + .unsqueeze(1) + .broadcastTo([B, S, projEnc.size(2)]); // [B,S,A] + const e = projEnc.add(projDec).tanh(); // [B,S,A] + let scores = this.v.forward(e).squeeze({ dim: -1 }); // [B,S] + if (srcPaddingMask) { + const mask = srcPaddingMask; + scores = maskedFill(scores, mask, -1e9); + } + const alpha = scores.softmax(1); // [B,S] + const context = encoderOutputs.mul(alpha.unsqueeze(-1)).sum({ dim: 1 }); // [B,H] + return [context, alpha]; + } +} + +class MultiplicativeRNNAttention extends nn.Module { + private readonly Wa: nn.Linear; // general form s^T Wa h + private readonly config: MultiplicativeRNNAttentionConfig; + constructor(hiddenSize: number, config: MultiplicativeRNNAttentionConfig) { + super(); + this.Wa = new nn.Linear(hiddenSize, hiddenSize, false); + this.config = config; + } + + forward( + decoderState: Tensor, + encoderOutputs: Tensor, + srcPaddingMask: Tensor | null = null + ): [Tensor, Tensor] { + const [_B, _S, H] = encoderOutputs.size(); + const encProj = this.Wa.forward(encoderOutputs); // [B,S,H] + let scores = encProj.mul(decoderState.unsqueeze(1)).sum({ dim: -1 }); // [B,S] + if (this.config.scaleByInverseSqrtHiddenSize) { + scores = scores.div(Math.sqrt(H)); + } + if (srcPaddingMask) { + const mask = srcPaddingMask; + scores = maskedFill(scores, mask, -1e9); + } + const alpha = scores.softmax(1); // [B,S] + const context = encoderOutputs.mul(alpha.unsqueeze(-1)).sum({ dim: 1 }); // [B,H] + return [context, alpha]; + } +} + +export class RNNDecoder extends nn.Module { + public config: RNNDecoderConfig; + private readonly wordEmbeddings: RNNWordEmbedding; + private readonly embeddingDropout?: nn.Dropout; + private readonly decoder: GRU | LSTM | RNN; + readonly lmHead: nn.Module<[Tensor], Tensor>; + private readonly criterion: CrossEntropyLoss; + + constructor(config: Config, vocabSize: number) { + super(); + + this.config = { ...buildRNNConfigCommon(config, vocabSize), nLayers: config.model.layers }; + + this.embeddingDropout = this.config.dropout.present + ? new nn.Dropout(this.config.dropout.embedding) + : undefined; + + // Embedding selection: learned vs one-hot + this.wordEmbeddings = createPossiblyOneHotRNNWordEmbedding( + this.config.embedding, + this.config.vocabSize, + this.config.embeddingSize + ); + + // Build the RNN stack once so parameters are registered and trainable + this.decoder = createRNN( + this.config.cellType, + this.config.embeddingSize, + this.config.hiddenSize, + this.config.layerNormalization, + { + nLayers: this.config.nLayers, + // Decoder-only, so no bidirectional + bidirectional: false, + dropout: this.config.dropout.rnn.interLayer, + projectionSize: this.config.projectionSize + } + ); + + this.lmHead = buildLmHeadRNN(this.config, this.wordEmbeddings); + this.criterion = createCrossEntropyCriterion(config); + } + + /** + * @param input - Input tensor of token IDs [batch_size, seq_len] + * @param targets - Target tensor of token IDs [batch_size, seq_len] + * @returns [logits, loss] + */ + forward( + input: Tensor, + { targets }: { targets: Tensor | null } = { targets: null } + ): [Tensor, Tensor | null] { + const [_batchSize, seqLen] = input.size(); + if (!seqLen) { + throw new Error( + 'Input tensor has no sequence length (did you forget to pass input as batches?)' + ); + } + + // Embedding + let embeddings = this.wordEmbeddings.forward(input); + + if (this.embeddingDropout) { + embeddings = this.embeddingDropout.forward(embeddings) as Tensor; + } + + // Process full sequence via pre-built core RNN + const [hiddenStatesStacked] = this.decoder.forward(embeddings); + + // Project to vocabulary + const logits = this.lmHead.forward(hiddenStatesStacked); + + const loss = computeAutoregressiveCrossEntropyLoss(logits, targets, this.criterion); + + return [logits, loss ?? null]; + } +} + +export class RNNEncoder extends nn.Module { + public config: RNNEncoderConfig; + private readonly wordEmbedding: RNNWordEmbedding; + private readonly tokenTypeEmbeddings?: nn.Embedding; + private readonly embeddingDropout?: nn.Dropout; + private readonly encoder: GRU | LSTM | RNN; + private readonly criterion: CrossEntropyLoss; + private readonly mlmHead?: MLMHead; + private readonly mlmPreProj?: nn.Linear; + + constructor(config: Config, vocabSize: number) { + super(); + + this.config = { + ...buildRNNConfigCommon(config, vocabSize), + nLayers: config.model.layers, + bidirectional: config.model.rnn.encoder.bidirectional, + typeVocabSize: 2, + mlmHead: { + present: true, + shareEmbeddings: config.model.tieEmbeddingsAndLmHead + } + }; + + this.embeddingDropout = this.config.dropout.present + ? new nn.Dropout(this.config.dropout.embedding) + : undefined; + this.wordEmbedding = createPossiblyOneHotRNNWordEmbedding( + this.config.embedding, + this.config.vocabSize, + this.config.embeddingSize + ); + this.tokenTypeEmbeddings = new nn.Embedding( + this.config.typeVocabSize, + this.config.embeddingSize + ); + + // Build the encoder RNN once so params are registered + this.encoder = createRNN( + this.config.cellType, + this.config.embeddingSize, + this.config.hiddenSize, + this.config.layerNormalization, + { + nLayers: this.config.nLayers, + bidirectional: this.config.bidirectional, + dropout: this.config.dropout.rnn.interLayer, + projectionSize: this.config.hiddenStateProjection.present + ? this.config.hiddenStateProjection.size + : undefined + } + ); + + if (this.config.mlmHead.present) { + const finalHiddenSize = this.config.bidirectional + ? this.config.baseHiddenSize * 2 + : this.config.baseHiddenSize; + const canTie = + this.config.embedding.type === 'learned' && this.config.mlmHead.shareEmbeddings; + // If tying and dims differ, add pre-projection to embedding size + if (canTie && finalHiddenSize !== this.config.embeddingSize) { + this.mlmPreProj = new nn.Linear(finalHiddenSize, this.config.embeddingSize); + } + this.mlmHead = new MLMHead( + canTie ? this.config.embeddingSize : finalHiddenSize, + this.config.vocabSize, + this.config.layerNormalization, + canTie ? (this.wordEmbedding as nn.Embedding) : undefined + ); + } + + this.criterion = createCrossEntropyCriterion(config); + } + + forward( + inputIds: Tensor, + { tokenTypeIds, targets }: { tokenTypeIds?: Tensor | null; targets?: Tensor | null } = { + tokenTypeIds: null, + targets: null + } + ): [Tensor, Tensor | null, Tensor | null] { + const [batchSize, seqLen] = inputIds.size(); + if (!seqLen) { + throw new Error( + 'Input tensor has no sequence length (did you forget to pass input as batches?)' + ); + } + + // Get token embeddings + let wordEmbeddings = this.wordEmbedding.forward(inputIds); + + // Add segment/token type embeddings + if (this.tokenTypeEmbeddings) { + if (tokenTypeIds == null) { + tokenTypeIds = piston.zeros([batchSize, seqLen], { + device: inputIds.device, + dtype: piston.int32 + }); + } + const typeEmbeddings = this.tokenTypeEmbeddings.forward(tokenTypeIds!); + wordEmbeddings = wordEmbeddings.add(typeEmbeddings); + } + + if (this.embeddingDropout) { + wordEmbeddings = this.embeddingDropout.forward(wordEmbeddings) as Tensor; + } + + let x = wordEmbeddings; + const [xSeq] = this.encoder.forward(x); + x = xSeq; + + // If present, reconcile dims for tied MLM head + if (this.mlmPreProj) { + x = this.mlmPreProj.forward(x) as Tensor; + } + + // MLM head + let mlmLogits: Tensor | null = null; + if (this.mlmHead) { + mlmLogits = this.mlmHead.forward(x); + } + + // Calculate MLM loss if targets are provided + let loss: Tensor | null = null; + if (targets && mlmLogits) { + // Only compute loss on masked positions (where targets != -100) + const flatLogits = mlmLogits.view([-1, mlmLogits.size(-1)]); + const flatTargets = targets.view(-1); + loss = this.criterion.forward(flatLogits, flatTargets); + } + + return [x, mlmLogits, loss]; + } +} + +export class RNNEncoderDecoder extends nn.Module { + public config: RNNEncoderDecoderConfig; + + private readonly criterion: CrossEntropyLoss; + readonly lmHead: nn.Module<[Tensor], Tensor>; + private readonly encoderEmbedding: RNNWordEmbedding; + private readonly decoderEmbedding: RNNWordEmbedding; + private readonly encoder: GRU | LSTM | RNN; + private readonly decoder: GRU | LSTM | RNN; + private readonly attention?: AdditiveRNNAttention | MultiplicativeRNNAttention; + private readonly outProj: nn.Linear; + private readonly decoderInputProj?: nn.Linear; + private readonly decoderStateProj?: nn.Linear; + private readonly encCombineProjections?: nn.ModuleList; + + constructor(config: Config, vocabSize: number) { + super(); + + this.config = { + ...buildRNNConfigCommon(config, vocabSize), + nEncoderLayer: config.model.encoderDecoder.encoderLayers, + nDecoderLayer: config.model.encoderDecoder.decoderLayers, + bidirectional: config.model.rnn.encoder.bidirectional, + encoderDecoderAttention: config.model.rnn.encoderDecoderAttention + }; + + // Effective hidden width used throughout encoder/decoder stacks + const baseHidden = this.config.baseHiddenSize; + + this.encoderEmbedding = createPossiblyOneHotRNNWordEmbedding( + this.config.embedding, + this.config.vocabSize, + this.config.embeddingSize + ); + this.decoderEmbedding = createPossiblyOneHotRNNWordEmbedding( + this.config.embedding, + this.config.vocabSize, + this.config.embeddingSize + ); + + // Build encoder/decoder stacks once so params are registered + this.encoder = createRNN( + this.config.cellType, + this.config.embeddingSize, + this.config.hiddenSize, + this.config.layerNormalization, + { + nLayers: this.config.nEncoderLayer, + bidirectional: this.config.bidirectional, + dropout: this.config.dropout.rnn.interLayer, + projectionSize: this.config.projectionSize + } + ); + + this.decoder = createRNN( + this.config.cellType, + this.config.embeddingSize, + this.config.hiddenSize, + this.config.layerNormalization, + { + nLayers: this.config.nDecoderLayer, + bidirectional: false, + dropout: this.config.dropout.rnn.interLayer, + projectionSize: this.config.projectionSize + } + ); + + // Encoder bidirectionality support: per-layer 2H->H projections + if (this.config.bidirectional) { + this.encCombineProjections = new nn.ModuleList( + Array.from( + { length: this.config.nEncoderLayer }, + () => new nn.Linear(baseHidden * 2, baseHidden) as nn.Module + ) + ); + } + + // Attention selection + if (config.model.rnn.encoderDecoderAttention?.present) { + const attnType = config.model.rnn.encoderDecoderAttention.type; + if (attnType === 'additive') { + this.attention = new AdditiveRNNAttention(baseHidden, baseHidden); + } else { + this.attention = new MultiplicativeRNNAttention( + baseHidden, + this.config.encoderDecoderAttention.multiplicative + ); + } + if (config.model.rnn.encoderDecoderAttention.inputFeedingProjection) { + const topIn = + this.config.nDecoderLayer === 1 ? this.config.embeddingSize : this.config.baseHiddenSize; + this.decoderInputProj = new nn.Linear(topIn + baseHidden, topIn); + } + } + + // Attention out projection maps back to the cell hidden size; projection (if any) + // will be applied afterwards by BaseRNN + this.outProj = new nn.Linear(baseHidden * 2, this.config.hiddenSize); + + // If hidden state differs from baseHidden (due to projection), project decoder + // state to baseHidden for attention computations + if (this.config.hiddenSize !== baseHidden) { + this.decoderStateProj = new nn.Linear(this.config.hiddenSize, baseHidden); + } + this.lmHead = buildLmHeadRNN(this.config, this.decoderEmbedding); + + this.criterion = createCrossEntropyCriterion(config); + } + + encode(inputIdsSrc: Tensor): Tensor { + const [_batchSize, seqLen] = inputIdsSrc.size(); + if (!seqLen) { + throw new Error('Source input tensor has no sequence length'); + } + const x = this.encoderEmbedding.forward(inputIdsSrc); // [B,S,H] + const [encSeq] = this.encoder.forward(x); // [B,S, H or 2H] + if (this.config.bidirectional && this.encCombineProjections) { + const proj = this.encCombineProjections[this.config.nEncoderLayer - 1]!; + return proj.forward(encSeq); + } + return encSeq; + } + + forward( + inputIdsSrc: Tensor, + inputIdsTgt: Tensor, + { + targets, + srcPaddingMask, + tgtPaddingMask: _tgtPaddingMask, + encoderHiddenStates + }: { + targets?: Tensor | null; + srcPaddingMask?: Tensor | null; + tgtPaddingMask?: Tensor | null; + encoderHiddenStates?: Tensor | null; + } = { + targets: null, + srcPaddingMask: null, + tgtPaddingMask: null, + encoderHiddenStates: null + } + ): [Tensor, Tensor | null] { + const encStates = encoderHiddenStates ?? this.encode(inputIdsSrc); + const y = this.decoderEmbedding.forward(inputIdsTgt); // [B,T,H] + const attentionOptions: RNNForwardAttentionOptions | undefined = this.attention + ? { + attentionModule: this.attention, + encoderOutputs: encStates, + srcPaddingMask: srcPaddingMask ?? null, + outProjection: this.outProj, + inputFeedingProjection: this.decoderInputProj, + decoderStateProjection: this.decoderStateProj + } + : undefined; + const [hiddenStates] = this.decoder.forward(y, undefined, attentionOptions); + const logits = this.lmHead.forward(hiddenStates); + const loss = computeAutoregressiveCrossEntropyLoss(logits, targets ?? null, this.criterion); + return [logits, loss]; + } +} diff --git a/examples/piston-train-toy/src/lib/train/model/utils.ts b/examples/piston-train-toy/src/lib/train/model/utils.ts index dbd3dd42..84ef17c7 100644 --- a/examples/piston-train-toy/src/lib/train/model/utils.ts +++ b/examples/piston-train-toy/src/lib/train/model/utils.ts @@ -1,8 +1,9 @@ import type { Config, LayerNormalizationConfig, - NormalizationConfig, - PositionEncodingConfig + PositionEncodingConfig, + RNNEmbeddingConfig, + TransformerNormalizationConfig } from '$lib/workspace/config'; import { @@ -17,7 +18,10 @@ import { type Tensor } from '@piston-ml/piston-web'; +import type { RNNModuleConfig } from './config'; + import { createNorm } from './modules/norm'; +import { OneHotEmbedding } from './modules/oneHot'; import { SinusoidalEncoding } from './modules/positional'; import { applySoftcap } from './modules/utils'; @@ -138,6 +142,21 @@ export function maybeCreateLearnedPositionEmbedding( return undefined; } +export type RNNWordEmbedding = nn.Embedding | OneHotEmbedding; + +/** + * Create a possibly one-hot RNN word embeddings module based on the embedding config + */ +export function createPossiblyOneHotRNNWordEmbedding( + embeddings: RNNEmbeddingConfig, + vocabSize: number, + embeddingSize: number +): RNNWordEmbedding { + return embeddings.type === 'learned' + ? new nn.Embedding(vocabSize, embeddingSize) + : new OneHotEmbedding(vocabSize); +} + /** * Optionally create sinusoidal encoding module based on positional encoding config */ @@ -181,9 +200,30 @@ export function buildLmHead( return head; } +export function buildLmHeadRNN( + config: RNNModuleConfig, + tiedEmbeddings?: RNNWordEmbedding +): nn.Module<[Tensor], Tensor> { + const canTie = config.embedding.type === 'learned' && config.tieEmbeddingsAndLmHead; + const inFeatures = canTie ? config.embeddingSize : config.baseHiddenSize; + let lmHead: nn.Module<[Tensor], Tensor> = buildLmHead( + inFeatures, + config.vocabSize, + canTie, + canTie ? (tiedEmbeddings as nn.Embedding) : undefined + ); + if (canTie && config.baseHiddenSize !== config.embeddingSize) { + lmHead = new nn.Sequential( + new nn.Linear(config.baseHiddenSize, config.embeddingSize) as nn.Module, + lmHead as nn.Module + ) as nn.Module<[Tensor], Tensor>; + } + return lmHead; +} + export function maybeApplyLogitsSoftcap( logits: Tensor, - normalization: NormalizationConfig + normalization: TransformerNormalizationConfig ): Tensor { if (normalization.softcap.logits.present) { return applySoftcap(logits, normalization.softcap.logits.value); diff --git a/examples/piston-train-toy/src/lib/train/session.ts b/examples/piston-train-toy/src/lib/train/session.ts index 3cc4c6fa..5434e8a5 100644 --- a/examples/piston-train-toy/src/lib/train/session.ts +++ b/examples/piston-train-toy/src/lib/train/session.ts @@ -21,6 +21,7 @@ import type { ToyEncoderDecoderBatch, ToySequence } from './data/toy/dataset'; +import type { RNNEncoder, RNNEncoderDecoder } from './model/rnn'; import type { RunWorkerEvent, RunWorkerEventWithoutRunId, WorkerEvent } from './protocol'; import type { GeneratableModel, PistonCollateFnType, PistonDatasetType } from './types'; @@ -632,13 +633,19 @@ export class TrainingSession { const [inputIds, bertLabels, attentionMask] = tensors; let computedLoss: Tensor | null = null; - [, , , computedLoss] = (this.model as EncoderTransformer).forward( - await inputIds.to('gpu'), - { - attentionMask: await attentionMask.to('gpu'), + if (this.model instanceof EncoderTransformer) { + [, , , computedLoss] = (this.model as EncoderTransformer).forward( + await inputIds.to('gpu'), + { + attentionMask: await attentionMask.to('gpu'), + targets: await bertLabels.to('gpu') + } + ); + } else { + [, , computedLoss] = (this.model as RNNEncoder).forward(await inputIds.to('gpu'), { targets: await bertLabels.to('gpu') - } - ); + }); + } if (!computedLoss) { throw new Error('No loss tensor returned from encoder-only model'); @@ -646,14 +653,23 @@ export class TrainingSession { loss = computedLoss; } else if (this.config.model.topology === 'encoder-decoder') { - // For Transformer: batch contains [encoderInputs, decoderInputs, decoderTargets] + // For Transformer or RNN seq2seq: batch contains [encoderInputs, decoderInputs, decoderTargets] const { tensors } = batch as ToyEncoderDecoderBatch; const [encoderInputs, decoderInputs, decoderTargets] = tensors; - const [, computedLoss] = (this.model as EncoderDecoderTransformer).forward( - await encoderInputs.to('gpu'), - await decoderInputs.to('gpu'), - { targets: await decoderTargets.to('gpu') } - ); + let computedLoss: Tensor | null; + if (this.model instanceof EncoderDecoderTransformer) { + [, computedLoss] = (this.model as EncoderDecoderTransformer).forward( + await encoderInputs.to('gpu'), + await decoderInputs.to('gpu'), + { targets: await decoderTargets.to('gpu') } + ); + } else { + [, computedLoss] = (this.model as RNNEncoderDecoder).forward( + await encoderInputs.to('gpu'), + await decoderInputs.to('gpu'), + { targets: await decoderTargets.to('gpu') } + ); + } if (!computedLoss) { throw new Error('No loss tensor returned from encoder-decoder model'); diff --git a/examples/piston-train-toy/src/lib/train/types.ts b/examples/piston-train-toy/src/lib/train/types.ts index a8a073a9..468259c4 100644 --- a/examples/piston-train-toy/src/lib/train/types.ts +++ b/examples/piston-train-toy/src/lib/train/types.ts @@ -12,6 +12,7 @@ import type { ToyEncoderDecoderBatch, ToySequence } from './data/toy/dataset'; +import type { RNNDecoder, RNNEncoder, RNNEncoderDecoder } from './model/rnn'; import type { DecoderTransformer, EncoderDecoderTransformer, @@ -50,6 +51,23 @@ export type ToyEncoderDecoderCollateFnType = CollateFn< ToyEncoderDecoderBatch >; +export type NaturalAutoregressiveCollateFnType = CollateFn< + NaturalCollateInput, + NaturalLanguageAutoregressiveBatch +>; +export type NaturalBidirectionalCollateFnType = CollateFn< + NaturalCollateInput, + NaturalLanguageBidirectionalBatch +>; + +export type AutoregressiveCollateFnType = + | ToyAutoregressiveCollateFnType + | NaturalAutoregressiveCollateFnType; + +export type BidirectionalCollateFnType = + | ToyBidirectionalCollateFnType + | NaturalBidirectionalCollateFnType; + export type EncoderDecoderCollateFnType = CollateFn>; export type NaturalCollateFnType = CollateFn>; @@ -60,6 +78,13 @@ export type NaturalDataloaderType = DataLoader> export type ToyDataloaderType = DataLoader>; export type PistonDataloaderType = ToyDataloaderType | NaturalDataloaderType; -export type GeneratableModel = DecoderTransformer | EncoderTransformer | EncoderDecoderTransformer; +export type AutoregressiveModelType = DecoderTransformer | RNNDecoder; +export type BidirectionalModelType = EncoderTransformer | RNNEncoder; +export type EncoderDecoderModelType = EncoderDecoderTransformer | RNNEncoderDecoder; + +export type GeneratableModel = + | AutoregressiveModelType + | BidirectionalModelType + | EncoderDecoderModelType; export type PistonDatasetType = ToyDatasetLike | NaturalLanguageDataset; diff --git a/examples/piston-train-toy/src/lib/train/utils/init.ts b/examples/piston-train-toy/src/lib/train/utils/init.ts index 75968075..93aad0d0 100644 --- a/examples/piston-train-toy/src/lib/train/utils/init.ts +++ b/examples/piston-train-toy/src/lib/train/utils/init.ts @@ -1,6 +1,25 @@ -import type { Config, ProjectionInitializationConfig } from '$lib/workspace/config'; +import type { + Config, + ProjectionInitializationConfig, + RNNInitializationConfig, + XavierInitializationDistribution +} from '$lib/workspace/config'; -import { initNormal_, initOnes_, initZeros_, nn } from '@piston-ml/piston-web'; +import { + initConstant_, + initNormal_, + initOnes_, + initOrthogonal_, + initXavierNormal_, + initXavierUniform_, + initZeros_, + LayerNorm, + nn, + Tensor +} from '@piston-ml/piston-web'; +import * as piston from '@piston-ml/piston-web'; + +import { GRUCell, LSTMCell, RNNCell } from '../model/rnn'; export function initTransformerParameters(self: nn.Module, config: Config): void { const initializationConfig = config.model.transformer.initialization; @@ -61,3 +80,254 @@ export function initTransformerParameters(self: nn.Module, config: Config): void } } } + +function initInputPart_( + part: Tensor, + dist: XavierInitializationDistribution, + enable: boolean +): Tensor { + if (!enable) return part; + return dist === 'uniform' ? initXavierUniform_(part) : initXavierNormal_(part); +} + +function initRecurrentParts_(recParts: Tensor[], doOrth: boolean, perGateOrth: boolean): Tensor[] { + if (!doOrth) return recParts; + if (perGateOrth) { + return recParts.map((p) => initOrthogonal_(p)); + } else { + // One tall orthogonal across all gates (works for rectangular via QR) + const tall = piston.cat(recParts, { dim: 0 }); + const tallInit = initOrthogonal_(tall); + return piston.chunk(tallInit, recParts.length, { dim: 0 }) as Tensor[]; + } +} + +function initGatedWeight_( + W: Tensor, // shape: (G*H) x (I+H) + I: number, + H: number, + G: number, + dist: XavierInitializationDistribution, + xavierInput: boolean, + orthRec: boolean, + perGateOrth: boolean +): Tensor { + // Split rows into gate blocks + const gates = piston.chunk(W, G, { dim: 0 }) as Tensor[]; // G × [H x (I+H)] + // For each gate, split columns into [input | recurrent] + const inParts: Tensor[] = []; + const recParts: Tensor[] = []; + for (const g of gates) { + const [inp, rec] = piston.split(g, [I, H], { dim: 1 }) as [Tensor, Tensor]; + inParts.push(inp); + recParts.push(rec); + } + const inInit = inParts.map((p) => initInputPart_(p, dist, xavierInput)); + const recInit = initRecurrentParts_(recParts, orthRec, perGateOrth); + + // Reassemble gates, then full matrix + const gateBlocks = inInit.map((inp, k) => piston.cat([inp, recInit[k]], { dim: 1 })); + return piston.cat(gateBlocks, { dim: 0 }); +} + +function buildParameterHacky(tensor: Tensor): Tensor { + return new nn.Parameter(tensor.debugTensor); +} + +const GRU_GATES_COUNT = 2; +const LSTM_GATES_COUNT = 4; + +export function initGRUCell_(cell: GRUCell, config: RNNInitializationConfig) { + const dist = config.xavierInputColumns.distribution; + + const [wgRows, wgCols] = cell.WGates.weight.size(); + const hiddenWG = wgRows / GRU_GATES_COUNT; + const inputWG = wgCols - hiddenWG; + + // WGates: shape (2H) x (I+H), rows chunked as [r, z] + cell.WGates.weight = buildParameterHacky( + initGatedWeight_( + cell.WGates.weight, + inputWG, + hiddenWG, + GRU_GATES_COUNT, + dist, + config.xavierInputColumns.present, + config.orthogonalRecurrentColumns, + config.perGateOrthogonalBlocks + ) + ); + + const [wcRows, wcCols] = cell.WCandidate.weight.size(); + const hiddenWCand = wcRows; + const inputWCand = wcCols - hiddenWCand; + const [inp, rec] = piston.split(cell.WCandidate.weight, [inputWCand, hiddenWCand], { dim: 1 }); + const inpInit = initInputPart_(inp, dist, config.xavierInputColumns.present); + const recInit = config.orthogonalRecurrentColumns ? initOrthogonal_(rec) : rec; + cell.WCandidate.weight = buildParameterHacky(piston.cat([inpInit, recInit], { dim: 1 })); + + const normGates = cell.normGates; + + // Biases + if (normGates instanceof LayerNorm && normGates.bias) { + // Keep Linear biases zeroed if requested + if (cell.WGates.bias) + cell.WGates.bias = config.zeroBiases + ? buildParameterHacky(initZeros_(cell.WGates.bias)) + : cell.WGates.bias; + if (cell.WCandidate.bias) + cell.WCandidate.bias = config.zeroBiases + ? buildParameterHacky(initZeros_(cell.WCandidate.bias)) + : cell.WCandidate.bias; + + // Apply update-gate bias to LN β on z block; r block to zero + const [beta_r, beta_z] = piston.chunk(normGates.bias, GRU_GATES_COUNT, { dim: 0 }); + const updateGateBias = + config.gru.updateGateBias?.present && config.gru.updateGateBias.value > 0 + ? config.gru.updateGateBias.value + : 0; + normGates.bias = buildParameterHacky( + piston.cat( + [ + initZeros_(beta_r), + updateGateBias !== 0 ? initConstant_(beta_z, updateGateBias) : initZeros_(beta_z) + ], + { dim: 0 } + ) + ); + } else { + // No LN on gates: write into WGates.bias directly (WCandidate bias stays zero if configured) + if (cell.WGates.bias) { + const [b_r, b_z] = piston.chunk(cell.WGates.bias, GRU_GATES_COUNT, { dim: 0 }); + const updateGateBias = + config.gru.updateGateBias?.present && config.gru.updateGateBias.value > 0 + ? config.gru.updateGateBias.value + : 0; + + cell.WGates.bias = buildParameterHacky( + piston.cat( + [ + config.zeroBiases ? initZeros_(b_r) : b_r, + updateGateBias !== 0 + ? initConstant_(b_z, updateGateBias) + : config.zeroBiases + ? initZeros_(b_z) + : b_z + ], + { dim: 0 } + ) + ); + } + if (cell.WCandidate.bias) { + cell.WCandidate.bias = config.zeroBiases + ? buildParameterHacky(initZeros_(cell.WCandidate.bias)) + : cell.WCandidate.bias; + } + } +} + +export function initLSTMCell_(cell: LSTMCell, config: RNNInitializationConfig) { + const [wRows, wCols] = cell.W.weight.size(); + const inferredHidden = wRows / LSTM_GATES_COUNT; + const inferredInput = wCols - inferredHidden; + + cell.W.weight = buildParameterHacky( + initGatedWeight_( + cell.W.weight, + inferredInput, + inferredHidden, + LSTM_GATES_COUNT, + config.xavierInputColumns.distribution, + config.xavierInputColumns.present, + config.orthogonalRecurrentColumns, + config.perGateOrthogonalBlocks + ) + ); + + // Biases/LN handling + const normGates = cell.normGates; + const forgetGateBiasPresent = config.lstm.forgetGateBias.present; + const forgetGateBiasValue = config.lstm.forgetGateBias.value; + + if (normGates instanceof LayerNorm && normGates.bias) { + // Keep Linear bias zero if requested (recommended with LN) + if (cell.W.bias) + cell.W.bias = config.zeroBiases ? buildParameterHacky(initZeros_(cell.W.bias)) : cell.W.bias; + + // LN beta blocks: [i, f, g, o]; set f to forgetVal, others to 0 + const [b_i, b_f, b_g, b_o] = piston.chunk(normGates.bias, LSTM_GATES_COUNT, { + dim: 0 + }); + normGates.bias = buildParameterHacky( + piston.cat( + [ + initZeros_(b_i), + forgetGateBiasPresent ? initConstant_(b_f, forgetGateBiasValue) : initZeros_(b_f), + initZeros_(b_g), + initZeros_(b_o) + ], + { dim: 0 } + ) + ); + } else if (cell.W.bias) { + const [b_i, b_f, b_g, b_o] = piston.chunk(cell.W.bias, LSTM_GATES_COUNT, { + dim: 0 + }); + cell.W.bias = buildParameterHacky( + piston.cat( + [ + config.zeroBiases ? initZeros_(b_i) : b_i, + forgetGateBiasPresent + ? initConstant_(b_f, forgetGateBiasValue) + : config.zeroBiases + ? initZeros_(b_f) + : b_f, + config.zeroBiases ? initZeros_(b_g) : b_g, + config.zeroBiases ? initZeros_(b_o) : b_o + ], + { dim: 0 } + ) + ); + } +} + +export function initRNNCell_(cell: RNNCell, config: RNNInitializationConfig) { + if (config.xavierInputColumns.present) { + const dist = config.xavierInputColumns.distribution; + cell.WIh.weight = buildParameterHacky( + dist === 'uniform' ? initXavierUniform_(cell.WIh.weight) : initXavierNormal_(cell.WIh.weight) + ); + } + + if (cell.WIh.bias) { + cell.WIh.bias = config.zeroBiases + ? buildParameterHacky(initZeros_(cell.WIh.bias)) + : cell.WIh.bias; + } + + if (config.orthogonalRecurrentColumns) { + cell.WHh.weight = buildParameterHacky(initOrthogonal_(cell.WHh.weight)); + } + + if (cell.WHh.bias) { + cell.WHh.bias = config.zeroBiases + ? buildParameterHacky(initZeros_(cell.WHh.bias)) + : cell.WHh.bias; + } +} + +export function initRNNParameters(root: nn.Module, config: Config) { + if (!config.model.rnn.initialization.present) return; + + const initializeRNNCell = (module: nn.Module) => { + if (module instanceof GRUCell) { + initGRUCell_(module, config.model.rnn.initialization); + } else if (module instanceof LSTMCell) { + initLSTMCell_(module, config.model.rnn.initialization); + } else if (module instanceof RNNCell) { + initRNNCell_(module, config.model.rnn.initialization); + } + }; + + root.apply(initializeRNNCell); +} diff --git a/examples/piston-train-toy/src/lib/train/utils/model.ts b/examples/piston-train-toy/src/lib/train/utils/model.ts index 14bf5c95..be4722d3 100644 --- a/examples/piston-train-toy/src/lib/train/utils/model.ts +++ b/examples/piston-train-toy/src/lib/train/utils/model.ts @@ -35,12 +35,13 @@ import { toyDatasetEncoderDecoderCollate } from '../data/toy/collate'; import { type ToyDatasetLike, type ToySequence } from '../data/toy/dataset'; +import { RNNDecoder, RNNEncoder, RNNEncoderDecoder } from '../model/rnn'; import { DecoderTransformer, EncoderDecoderTransformer, EncoderTransformer } from '../model/transformer'; -import { initTransformerParameters } from './init'; +import { initRNNParameters, initTransformerParameters } from './init'; import { parseSeed, seededRandom } from './random'; type EncoderDecoderBlockSize = { source: number; target: number }; @@ -209,7 +210,13 @@ export function createModel( config: Config, vocabSize: number, blockSize: number | { source: number; target: number } -): DecoderTransformer | EncoderTransformer | EncoderDecoderTransformer { +): + | DecoderTransformer + | EncoderTransformer + | EncoderDecoderTransformer + | RNNDecoder + | RNNEncoder + | RNNEncoderDecoder { const isEncoderOnly = config.model.topology === 'encoder'; const isDecoderOnly = config.model.topology === 'decoder'; const isEncoderDecoder = config.model.topology === 'encoder-decoder'; @@ -221,20 +228,42 @@ export function createModel( } if (isEncoderOnly) { - return new EncoderTransformer(config, vocabSize, blockSize as number); + if (config.model.family === 'rnn') { + return new RNNEncoder(config, vocabSize); + } else { + return new EncoderTransformer(config, vocabSize, blockSize as number); + } } else if (isEncoderDecoder) { const { source, target } = blockSize as { source: number; target: number }; - return new EncoderDecoderTransformer(config, vocabSize, source, target); + if (config.model.family === 'rnn') { + return new RNNEncoderDecoder(config, vocabSize); + } else { + return new EncoderDecoderTransformer(config, vocabSize, source, target); + } } else { - return new DecoderTransformer(config, vocabSize, blockSize as number); + if (config.model.family === 'rnn') { + return new RNNDecoder(config, vocabSize); + } else { + return new DecoderTransformer(config, vocabSize, blockSize as number); + } } } export function initializeModel( config: Config, - model: DecoderTransformer | EncoderTransformer | EncoderDecoderTransformer + model: + | DecoderTransformer + | EncoderTransformer + | EncoderDecoderTransformer + | RNNDecoder + | RNNEncoder + | RNNEncoderDecoder ) { - initTransformerParameters(model, config); + if (config.model.family === 'rnn') { + initRNNParameters(model, config); + } else { + initTransformerParameters(model, config); + } } export function calculateParameterSum(model: Module): Tensor { @@ -243,7 +272,13 @@ export function calculateParameterSum(model: Module): Tensor { } export function countParameters( - model: DecoderTransformer | EncoderTransformer | EncoderDecoderTransformer + model: + | DecoderTransformer + | EncoderTransformer + | EncoderDecoderTransformer + | RNNDecoder + | RNNEncoder + | RNNEncoderDecoder ): number { let totalParams = 0; @@ -288,11 +323,14 @@ export function inspectModel(config: Config): { indexMode = new CaptureIndexMode(model); // Run the model forward with an input from the dataloader - if (model instanceof DecoderTransformer) { + if (model instanceof DecoderTransformer || model instanceof RNNDecoder) { model.forward(piston.zeros([1, blockSizeOrSizes as number], { dtype: piston.int32 })); - } else if (model instanceof EncoderTransformer) { + } else if (model instanceof EncoderTransformer || model instanceof RNNEncoder) { model.forward(piston.zeros([1, blockSizeOrSizes as number], { dtype: piston.int32 })); - } else if (model instanceof EncoderDecoderTransformer) { + } else if ( + model instanceof EncoderDecoderTransformer || + model instanceof RNNEncoderDecoder + ) { model.forward( piston.zeros([1, (blockSizeOrSizes as EncoderDecoderBlockSize).source], { dtype: piston.int32 diff --git a/examples/piston-train-toy/src/lib/train/validation.ts b/examples/piston-train-toy/src/lib/train/validation.ts index 19f0fb86..5dc4c54d 100644 --- a/examples/piston-train-toy/src/lib/train/validation.ts +++ b/examples/piston-train-toy/src/lib/train/validation.ts @@ -22,6 +22,7 @@ import { toyDatasetEncoderDecoderCollate } from './data/toy/collate'; import { type ToyDatasetLike, type ToySequence } from './data/toy/dataset'; +import { RNNDecoder, RNNEncoder, RNNEncoderDecoder } from './model/rnn'; import { DecoderTransformer, EncoderDecoderTransformer, @@ -196,7 +197,7 @@ export async function computeToyValidationMetrics( const maxTokens = Math.max(...valExamples.targets.map((t) => t.length)); - if (isDecoderOnly && model instanceof DecoderTransformer) { + if (isDecoderOnly && (model instanceof DecoderTransformer || model instanceof RNNDecoder)) { const prompts = valExamples.prompts.map((prompt) => prompt.length > 0 ? prompt : [dataset.bosId!] ); @@ -208,7 +209,10 @@ export async function computeToyValidationMetrics( }); completions = result.completions; tokensPerSecond = result.tokensPerSecond; - } else if (isEncoderDecoder && model instanceof EncoderDecoderTransformer) { + } else if ( + isEncoderDecoder && + (model instanceof EncoderDecoderTransformer || model instanceof RNNEncoderDecoder) + ) { const sources = valExamples.prompts.map((prompt) => prompt.length > 0 ? prompt : [dataset.bosId!] ); @@ -222,7 +226,7 @@ export async function computeToyValidationMetrics( completions = result.completions; tokensPerSecond = result.tokensPerSecond; } else { - if (model instanceof EncoderTransformer) { + if (model instanceof EncoderTransformer || model instanceof RNNEncoder) { const result = await predictEncoderOnlyCompletions( model, valExamples.prompts, @@ -313,7 +317,7 @@ export async function computeNaturalValidationMetrics( const contextSize = dataset.contextSize; - if (isDecoderOnly && model instanceof DecoderTransformer) { + if (isDecoderOnly && (model instanceof DecoderTransformer || model instanceof RNNDecoder)) { // promptLen = Math.max(Math.floor(contextSize / 4), 1); promptLen = 8; const eosId = dataset.eosId as number; @@ -343,8 +347,8 @@ export async function computeNaturalValidationMetrics( const labels = collated.tensors[1]; // -100 for unmasked encoderOnlyTargets = labels; - if (model instanceof EncoderTransformer) { - const attentionMask = collated.tensors[2]; + if (model instanceof EncoderTransformer || model instanceof RNNEncoder) { + const attentionMask = model instanceof EncoderTransformer ? collated.tensors[2] : undefined; const result = await predictEncoderOnlyCompletions(model, inputs, labels, { attentionMask, temperature: valConfig.temperature @@ -487,13 +491,13 @@ export async function computeLikelihoodMetrics( let loss: Tensor | null = null; let modelName = ''; - if (model instanceof DecoderTransformer) { + if (model instanceof DecoderTransformer || model instanceof RNNDecoder) { const [inputs, targets] = collated.tensors; [, loss] = model.forward(await inputs.to('gpu'), { targets: await targets.to('gpu') }); modelName = 'decoder-only'; - } else if (model instanceof EncoderDecoderTransformer) { + } else if (model instanceof EncoderDecoderTransformer || model instanceof RNNEncoderDecoder) { const [encoderInputs, decoderInputs, decoderTargets] = ( collated as EncoderDecoderBatchType ).tensors; @@ -501,15 +505,20 @@ export async function computeLikelihoodMetrics( targets: await decoderTargets.to('gpu') }); modelName = 'encoder-decoder'; - } else if (model instanceof EncoderTransformer) { + } else if (model instanceof EncoderTransformer || model instanceof RNNEncoder) { // Encoder-only: compute MLM loss over masked tokens const [inputs, labels, attentionMask] = (collated as BidirectionalBatchType) .tensors; modelName = 'encoder-only'; - [, , , loss] = model.forward(await inputs.to('gpu'), { - attentionMask: await attentionMask.to('gpu'), - targets: await labels.to('gpu') - }); + if (model instanceof EncoderTransformer) { + [, , , loss] = model.forward(await inputs.to('gpu'), { + attentionMask: await attentionMask.to('gpu'), + targets: await labels.to('gpu') + }); + } else { + // No attention mask here + [, , loss] = model.forward(await inputs.to('gpu'), { targets: await labels.to('gpu') }); + } } else { throw new Error('Unsupported model for validation'); } diff --git a/examples/piston-train-toy/src/lib/train/validationHelpers.ts b/examples/piston-train-toy/src/lib/train/validationHelpers.ts index 786d01e7..625eb3cf 100644 --- a/examples/piston-train-toy/src/lib/train/validationHelpers.ts +++ b/examples/piston-train-toy/src/lib/train/validationHelpers.ts @@ -4,6 +4,7 @@ import { pin, type Tensor, weak } from '@piston-ml/piston-web'; import { tensorWrap } from './data'; import { generateStream } from './generate'; +import { RNNDecoder, RNNEncoder, RNNEncoderDecoder } from './model/rnn'; import { DecoderTransformer, EncoderDecoderTransformer, @@ -31,7 +32,7 @@ export type EncoderOnlyPredictionOptions = { }; export async function generateDecoderCompletions( - model: DecoderTransformer, + model: DecoderTransformer | RNNDecoder, startSequences: number[][], options: DecoderGenerationOptions ): Promise<{ completions: TokenRollout[]; tokensPerSecond?: number }> { @@ -78,7 +79,7 @@ export async function generateDecoderCompletions( } export async function generateEncoderDecoderCompletions( - model: EncoderDecoderTransformer, + model: EncoderDecoderTransformer | RNNEncoderDecoder, sourceSequences: number[][], options: EncoderDecoderGenerationOptions ): Promise<{ completions: TokenRollout[]; tokensPerSecond?: number }> { @@ -126,7 +127,7 @@ export async function generateEncoderDecoderCompletions( } export async function predictEncoderOnlyCompletions( - model: EncoderTransformer, + model: EncoderTransformer | RNNEncoder, inputs: number[][], labels: number[][], options: EncoderOnlyPredictionOptions @@ -138,13 +139,17 @@ export async function predictEncoderOnlyCompletions( const inputsTensor = tensorWrap(inputs); let mlmLogits: Tensor | null = null; - if (attentionMask) { - const attentionMaskTensor = tensorWrap(attentionMask); - [, , mlmLogits] = model.forward(await inputsTensor.to('gpu'), { - attentionMask: await attentionMaskTensor.to('gpu') - }); + if (model instanceof EncoderTransformer) { + if (attentionMask) { + const attentionMaskTensor = tensorWrap(attentionMask); + [, , mlmLogits] = model.forward(await inputsTensor.to('gpu'), { + attentionMask: await attentionMaskTensor.to('gpu') + }); + } else { + [, , mlmLogits] = model.forward(await inputsTensor.to('gpu')); + } } else { - [, , mlmLogits] = model.forward(await inputsTensor.to('gpu')); + [, mlmLogits] = model.forward(await inputsTensor.to('gpu')); } if (!mlmLogits) { diff --git a/examples/piston-train-toy/src/lib/workspace/config.svelte.ts b/examples/piston-train-toy/src/lib/workspace/config.svelte.ts index 215eeb41..3fe91242 100644 --- a/examples/piston-train-toy/src/lib/workspace/config.svelte.ts +++ b/examples/piston-train-toy/src/lib/workspace/config.svelte.ts @@ -47,6 +47,9 @@ const CONFIG_DEFAULTS: Config = { transformer: { attention: 0.1, residual: 0.1 + }, + rnn: { + interLayer: 0.1 } }, randomSeed: { @@ -86,6 +89,7 @@ const CONFIG_DEFAULTS: Config = { } }, model: { + family: 'transformer', topology: 'decoder', layers: 1, tieEmbeddingsAndLmHead: false, @@ -103,6 +107,10 @@ const CONFIG_DEFAULTS: Config = { transformer: { present: true, position: 'pre' + }, + rnn: { + withinCell: true, + betweenLayers: false } }, transformer: { @@ -180,6 +188,56 @@ const CONFIG_DEFAULTS: Config = { hiddenExpansionFactor: 4, variant: 'gated' } + }, + rnn: { + cellType: 'lstm', + embedding: { + type: 'learned', + learned: { + size: 16 + } + }, + separateHiddenSize: { + present: false, + value: 16 + }, + initialization: { + present: true, + xavierInputColumns: { + present: true, + distribution: 'uniform' + }, + orthogonalRecurrentColumns: true, + perGateOrthogonalBlocks: true, + zeroBiases: true, + gru: { + updateGateBias: { + present: false, + value: 0.0 + } + }, + lstm: { + forgetGateBias: { + present: true, + value: 1.0 + } + } + }, + hiddenStateProjection: { + present: false, + size: 16 + }, + encoder: { + bidirectional: false + }, + encoderDecoderAttention: { + present: false, + type: 'additive', + inputFeedingProjection: true, + multiplicative: { + scaleByInverseSqrtHiddenSize: true + } + } } }, optimizer: { @@ -592,7 +650,7 @@ export function validateConfig() { if ( config.visualization.example !== 'custom' && - !getVisualizationExampleOptions().some((e) => e.value === config.visualization.example) + !getVisualizationExampleOptions(config).some((e) => e.value === config.visualization.example) ) { config.visualization.example = 'all-activations'; } diff --git a/examples/piston-train-toy/src/lib/workspace/config.ts b/examples/piston-train-toy/src/lib/workspace/config.ts index 40278c11..debcb0d1 100644 --- a/examples/piston-train-toy/src/lib/workspace/config.ts +++ b/examples/piston-train-toy/src/lib/workspace/config.ts @@ -27,8 +27,14 @@ export interface DropoutConfig { attention: number; residual: number; }; + rnn: { + interLayer: number; + }; } +export type TransformerDropoutConfig = Omit; +export type RNNDropoutConfig = Omit; + export interface ValidationCompletionsConfig { present: boolean; decodingBatchSize: number; @@ -134,6 +140,10 @@ export interface LayerNormalizationConfig { present: boolean; position: LayerNormPosition; }; + rnn: { + withinCell: boolean; + betweenLayers: boolean; + }; } export interface QKNormConfig { @@ -153,7 +163,7 @@ export interface SoftcapConfig { }; } -export interface NormalizationConfig { +export interface TransformerNormalizationConfig { qkNorm: QKNormConfig; softcap: SoftcapConfig; } @@ -182,6 +192,46 @@ export interface MLPConfig { } export type ModelType = 'decoder' | 'encoder' | 'encoder-decoder'; +export type ModelFamily = 'transformer' | 'rnn'; + +export interface MultiplicativeRNNAttentionConfig { + scaleByInverseSqrtHiddenSize: boolean; +} + +export interface RNNAttentionConfig { + present: boolean; + type: 'additive' | 'multiplicative'; + inputFeedingProjection: boolean; + multiplicative: MultiplicativeRNNAttentionConfig; +} + +export interface RNNHiddenStateProjectionConfig { + present: boolean; + size: number; +} + +export interface RNNEmbeddingConfig { + type: 'learned' | 'one-hot'; + learned: { + size: number; + }; +} + +export interface RNNConfig { + cellType: 'lstm' | 'gru' | 'rnn'; + // RNN token embedding configuration + embedding: RNNEmbeddingConfig; + separateHiddenSize: { + present: boolean; + value: number; + }; + initialization: RNNInitializationConfig; + hiddenStateProjection: RNNHiddenStateProjectionConfig; + encoder: { + bidirectional: boolean; + }; + encoderDecoderAttention: RNNAttentionConfig; +} export type ProjectionInitializationStrategy = 'layer-scaled' | 'zero'; export interface ProjectionInitializationConfig { @@ -189,7 +239,7 @@ export interface ProjectionInitializationConfig { strategy: ProjectionInitializationStrategy; } -export interface InitializationConfig { +export interface TransformerInitializationConfig { present: boolean; std: number; projections: { @@ -199,16 +249,49 @@ export interface InitializationConfig { }; } +export interface LSTMInitializationConfig { + forgetGateBias: { + present: boolean; + value: number; + }; +} + +export type XavierInitializationDistribution = 'uniform' | 'normal'; + +export interface RNNInitializationConfig { + present: boolean; + xavierInputColumns: { + present: boolean; + distribution: XavierInitializationDistribution; + }; + orthogonalRecurrentColumns: boolean; + perGateOrthogonalBlocks: boolean; + zeroBiases: boolean; + gru: { + updateGateBias: { + present: boolean; + value: number; + }; + }; + lstm: { + forgetGateBias: { + present: boolean; + value: number; + }; + }; +} + export interface TransformerConfig { headDim: number; attention: TransformerAttentionConfig; positionalEncoding: PositionEncodingConfig; - initialization: InitializationConfig; - normalization: NormalizationConfig; + initialization: TransformerInitializationConfig; + normalization: TransformerNormalizationConfig; mlp: MLPConfig; } export interface ModelConfig { + family: ModelFamily; topology: ModelType; layers: number; tieEmbeddingsAndLmHead: boolean; @@ -222,6 +305,7 @@ export interface ModelConfig { }; layerNormalization: LayerNormalizationConfig; transformer: TransformerConfig; + rnn: RNNConfig; } export interface OptimizerConfig { diff --git a/examples/piston-train-toy/src/lib/workspace/runs.svelte.ts b/examples/piston-train-toy/src/lib/workspace/runs.svelte.ts index 8b6527ca..f75cc061 100644 --- a/examples/piston-train-toy/src/lib/workspace/runs.svelte.ts +++ b/examples/piston-train-toy/src/lib/workspace/runs.svelte.ts @@ -14,7 +14,12 @@ export type TokenRollout = { tokenIds: number[]; probs: number[][]; }; -export type StepData = Point | ValidationStep; + +export type VisualizationStep = BaseStepData & { + type: 'visualization'; +}; + +export type StepData = Point | ValidationStep | CaptureStep; export type MetricData = { metricName: string; diff --git a/examples/piston-train-toy/src/lib/workspace/visualizationExamples.ts b/examples/piston-train-toy/src/lib/workspace/visualizationExamples.ts index fead26d3..4a33d359 100644 --- a/examples/piston-train-toy/src/lib/workspace/visualizationExamples.ts +++ b/examples/piston-train-toy/src/lib/workspace/visualizationExamples.ts @@ -49,28 +49,37 @@ layer[0] ./.*Attention/ #* :label("attention weights") :scale(2)`; const MLP_ACTIVATIONS = `layer[0] .MLP @ * :label("mlp activations") :scale(2)`; +const ATTENTION_PREDICATE = (config: Config) => { + return config.model.family === 'transformer'; +}; + export const VISUALIZATION_EXAMPLES: Record = { 'attention-probabilities': { label: 'Attention Probabilities', - script: ATTENTION_PROBABILITIES + script: ATTENTION_PROBABILITIES, + predicate: ATTENTION_PREDICATE }, 'attention-activations': { label: 'Attention Activations', - script: ATTENTION_ACTIVATIONS + script: ATTENTION_ACTIVATIONS, + predicate: ATTENTION_PREDICATE }, 'attention-gradients': { label: 'Attention Gradients', - script: ATTENTION_GRADIENTS + script: ATTENTION_GRADIENTS, + predicate: ATTENTION_PREDICATE }, 'attention-parameters': { label: 'Attention Parameters', - script: ATTENTION_PARAMETERS + script: ATTENTION_PARAMETERS, + predicate: ATTENTION_PREDICATE }, 'mlp activations': { label: 'MLP Activations', - script: MLP_ACTIVATIONS + script: MLP_ACTIVATIONS, + predicate: ATTENTION_PREDICATE }, - 'kitchen-sink': { label: 'Kitchen Sink', script: KITCHEN_SINK }, + 'kitchen-sink': { label: 'Kitchen Sink', script: KITCHEN_SINK, predicate: ATTENTION_PREDICATE }, 'all-activations': { label: 'All Activations', script: '* @ * :scale(3)' @@ -118,7 +127,9 @@ export function getVisualizationExampleOptions( config: Config ): Array<{ value: string; text: string }> { return [ - ...Object.entries(VISUALIZATION_EXAMPLES).map(([id, e]) => ({ value: id, text: e.label })), + ...Object.entries(VISUALIZATION_EXAMPLES) + .filter(([_, e]) => (e.predicate ? e.predicate(config) : true)) + .map(([id, e]) => ({ value: id, text: e.label })), { value: 'custom', text: 'Custom' } ]; } From 2859817d554d47c0b532d8f7a53774028d68f8ea Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 2 Nov 2025 21:00:03 -0700 Subject: [PATCH 579/590] Update pnpm-lock.yaml --- pnpm-lock.yaml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 194fa1dd..8521629c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -25,6 +25,21 @@ importers: examples/piston-train-toy: dependencies: + '@codemirror/autocomplete': + specifier: ^6.19.1 + version: 6.19.1 + '@codemirror/language': + specifier: ^6.11.1 + version: 6.11.3 + '@codemirror/lint': + specifier: ^6.9.1 + version: 6.9.1 + '@codemirror/state': + specifier: ^6.5.2 + version: 6.5.2 + '@codemirror/view': + specifier: ^6.37.2 + version: 6.38.6 '@huggingface/jinja': specifier: ^0.5.1 version: 0.5.1 @@ -34,6 +49,9 @@ importers: '@types/katex': specifier: ^0.16.7 version: 0.16.7 + codemirror: + specifier: ^6.0.2 + version: 6.0.2 echarts: specifier: ^6.0.0 version: 6.0.0 @@ -43,6 +61,9 @@ importers: lucide-svelte: specifier: ^0.548.0 version: 0.548.0(svelte@5.39.11) + maxrects-packer: + specifier: ^2.7.3 + version: 2.7.3 random-js: specifier: ^2.1.0 version: 2.1.0 @@ -4541,6 +4562,9 @@ packages: resolution: {integrity: sha512-NWlApBjW9az9qRPaeg7CX4sQBWwytqz32bIEo1PW9pRW+kBP9KLRfJO3UC+TV31EcQZEUq7eMzikC7zt3zPJcw==} engines: {node: '>=0.12'} + maxrects-packer@2.7.3: + resolution: {integrity: sha512-bG6qXujJ1QgttZVIH4WDanhoJtvbud/xP/XPyf6A69C9RdA61BM4TomFALCq2nrTa+tARRIBB4LuIFsnUQU2wA==} + mdn-data@2.0.14: resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} @@ -11081,6 +11105,8 @@ snapshots: gzip-size: 3.0.0 pretty-bytes: 3.0.1 + maxrects-packer@2.7.3: {} + mdn-data@2.0.14: {} mdn-data@2.12.2: From 1b6038d1e8bd12cc71e913e7e6746d5edbc05901 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 2 Nov 2025 21:02:29 -0700 Subject: [PATCH 580/590] Update README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 325064d6..789be9e1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# toy transformer + piston library +# sequence toy + Piston library -Watch a small transformer language model train, in your browser, with WebGPU. +Train small sequence models in your browser with WebGPU. ## Attribution From f728d8e258762e8a8f5c82fc87c87966f8cd597c Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 2 Nov 2025 21:03:58 -0700 Subject: [PATCH 581/590] Rust lint fixes --- crates/piston-web/src/serialization.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/piston-web/src/serialization.rs b/crates/piston-web/src/serialization.rs index c8c1d8cc..022e7198 100644 --- a/crates/piston-web/src/serialization.rs +++ b/crates/piston-web/src/serialization.rs @@ -119,7 +119,7 @@ pub fn load( for name in st.names() { let view = st - .tensor(&name) + .tensor(name) .map_err(|e| JsError::new(&format!("Failed to read tensor '{name}': {e}")))?; // Map safetensors dtype to piston dtype @@ -152,7 +152,7 @@ pub fn load( // Wrap as a JS-visible Tensor let js_tensor = JsTensor::new(tensor); - js_sys::Reflect::set(&obj, &JsValue::from_str(&name), &JsValue::from(js_tensor)) + js_sys::Reflect::set(&obj, &JsValue::from_str(name), &JsValue::from(js_tensor)) .map_err(|e| JsError::new(&format!("Failed to set property '{name}': {e:?}")))?; } From 5787645c78ad9c186917d4d1bbf3340d93f36b16 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 2 Nov 2025 21:24:38 -0700 Subject: [PATCH 582/590] Add quickstart presets --- .../lib/components/controls/Controls.svelte | 42 +++ .../src/lib/workspace/config.svelte.ts | 54 ++- .../src/lib/workspace/config.ts | 2 + .../src/lib/workspace/presets.ts | 289 ++++++++++++++++ .../src/lib/workspace/ui.svelte.ts | 26 ++ .../src/routes/tabs/About.svelte | 316 +++++++++++++++++- 6 files changed, 718 insertions(+), 11 deletions(-) create mode 100644 examples/piston-train-toy/src/lib/workspace/presets.ts diff --git a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte index 92330e71..ad787562 100644 --- a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte +++ b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte @@ -8,8 +8,10 @@ getHiddenSize, getMlpIntermediateSize, resetConfigToDefaults, + setPreset, validateConfig } from '$lib/workspace/config.svelte'; + import { getPresetOptions } from '$lib/workspace/presets'; import { controlSectionsOpen, toggleControlSection } from '$lib/workspace/ui.svelte'; import { getParameterCount, triggerModelInspection } from '$lib/workspace/workers.svelte'; import { untrack } from 'svelte'; @@ -69,6 +71,21 @@ // Trigger parameter count when config changes, but triggerParameterCount itself needs to be untracked untrack(() => triggerModelInspection()); }); + + // Preset selection wiring: local selection drives setPreset, stays in sync with config + let lastAppliedPreset = $state(null); + let selectedPresetId = $state(''); + $effect(() => { + const id = selectedPresetId || null; + if (id && id !== lastAppliedPreset) { + lastAppliedPreset = id; + setPreset(id); + } + }); + $effect(() => { + const current = (config.preset ?? '') as string; + if (current !== selectedPresetId) selectedPresetId = current; + }); {#snippet parameterComparisonFunFact()} @@ -195,6 +212,16 @@ {/snippet}
    +
    + +
    + diff --git a/examples/piston-train-toy/src/lib/workspace/config.svelte.ts b/examples/piston-train-toy/src/lib/workspace/config.svelte.ts index 3fe91242..4b956c79 100644 --- a/examples/piston-train-toy/src/lib/workspace/config.svelte.ts +++ b/examples/piston-train-toy/src/lib/workspace/config.svelte.ts @@ -7,6 +7,7 @@ import { calculateBlockSize, calculateVocabSize, createDataloader } from '$lib/t import { seededRandom } from '$lib/train/utils/random'; import { SvelteURL, SvelteURLSearchParams } from 'svelte/reactivity'; +import { getPresetLayers } from './presets'; import { getCurrentRun, getLatestRun } from './runs.svelte'; import { getVisualizationExampleOptions } from './visualizationExamples'; @@ -17,6 +18,7 @@ export const MODEL_TYPES = [ ] as const satisfies readonly ModelType[]; const CONFIG_DEFAULTS: Config = { + preset: null, training: { logSteps: 5, batchSize: 32, @@ -305,6 +307,22 @@ const CONFIG_DEFAULTS: Config = { version: 1 }; +function computeEffectiveDefaults(presetId: string | null | undefined): Config { + // Start from root defaults + const base = JSON.parse(JSON.stringify(CONFIG_DEFAULTS)) as Config; + if (presetId) { + const layers = getPresetLayers(presetId); + for (const layer of layers) { + mergeDeep( + base as unknown as Record, + layer as unknown as Record + ); + } + base.preset = presetId; + } + return base; +} + /** * Parses a value based on the type of the default value in the config. This is not wildly general, * but it seems to work for the current config. @@ -401,11 +419,13 @@ function mergeDeep(target: Record, source: Record, - path - ); + const defaultValue = getValueAtPath(configDefaults as unknown as Record, path); if (defaultValue === undefined) { console.warn(`resetConfigToDefaults: Unknown config path "${path}"`); continue; @@ -449,13 +467,13 @@ function deepClone(value: T): T { } export function getConfigDefaultValue(path: string): unknown { - const val = getValueAtPath(CONFIG_DEFAULTS as unknown as Record, path); + const val = getValueAtPath(configDefaults as unknown as Record, path); return deepClone(val); } export function equalsConfigDefault(path: string): boolean { const current = getValueAtPath(config as unknown as Record, path); - const def = getValueAtPath(CONFIG_DEFAULTS as unknown as Record, path); + const def = getValueAtPath(configDefaults as unknown as Record, path); return valuesDeepEqual(current, def); } @@ -539,9 +557,10 @@ export function initSharedConfigUrlSync() { const configSnapshot = $state.snapshot(config); const flatParams = flattenNonDefault( configSnapshot, - CONFIG_DEFAULTS as unknown as Record + configDefaults as unknown as Record ); - + // Always include preset when set, so shared URLs preserve selection + if (configSnapshot.preset) flatParams['preset'] = String(configSnapshot.preset); const searchParamsString = new SvelteURLSearchParams(flatParams).toString(); const currentUrl = new SvelteURL(window.location.href); @@ -555,6 +574,21 @@ export function initSharedConfigUrlSync() { } } +export function setPreset(presetId: string) { + if (config.preset === presetId) return; + const next = computeEffectiveDefaults(presetId); + // Replace config fields in-place because we've made config a const and I don't want to figure + // that out right now. + config['preset'] = next.preset; + config['training'] = deepClone(next.training); + config['data'] = deepClone(next.data); + config['model'] = deepClone(next.model); + config['optimizer'] = deepClone(next.optimizer); + config['visualization'] = deepClone(next.visualization); + config['version'] = next.version; + validateConfig(); +} + function ensureDatasetSupportsModelType() { const datasetKey = config.data.dataset as keyof typeof DATASET_CONFIG_METADATA; const meta = DATASET_CONFIG_METADATA[datasetKey]; diff --git a/examples/piston-train-toy/src/lib/workspace/config.ts b/examples/piston-train-toy/src/lib/workspace/config.ts index debcb0d1..c18430cb 100644 --- a/examples/piston-train-toy/src/lib/workspace/config.ts +++ b/examples/piston-train-toy/src/lib/workspace/config.ts @@ -359,6 +359,8 @@ export interface VisualizationConfig { export interface Config { version: number; + // Currently selected layered preset id; null means no preset + preset: string | null; training: TrainingConfig; data: DataConfig; model: ModelConfig; diff --git a/examples/piston-train-toy/src/lib/workspace/presets.ts b/examples/piston-train-toy/src/lib/workspace/presets.ts new file mode 100644 index 00000000..6b3fa432 --- /dev/null +++ b/examples/piston-train-toy/src/lib/workspace/presets.ts @@ -0,0 +1,289 @@ +import type { Config } from './config'; + +type DeepPartial = + // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type + T extends Function + ? T + : T extends Array + ? Array> + : T extends object + ? { [K in keyof T]?: DeepPartial } + : T; + +export type PresetDefinition = { + label: string; + // Layers are merged in order on top of root defaults + layers: Array>; + // If true, this preset is not shown in the preset selector but can be used as a layer + hidden?: boolean; +}; + +export const PRESET_DEFINITIONS: Record = { + 'transformer-toy-base': { + label: 'Transformer Toy (base)', + hidden: true, + layers: [ + { + model: { + family: 'transformer', + topology: 'encoder-decoder' + }, + training: { + validation: { + batchSize: 6, + completions: { + present: true, + decodingBatchSize: 6, + amount: 'all' + } + } + }, + optimizer: { + type: 'Muon', + lr: 1e-3 + } + } + ] + }, + 'encoder-toy-base': { + label: 'Encoder Toy (base)', + hidden: true, + layers: [ + { + model: { + family: 'transformer', + topology: 'encoder', + layers: 2 + }, + training: { + validation: { + batchSize: 6, + completions: { + present: true, + decodingBatchSize: 6, + amount: 'all' + } + } + }, + optimizer: { + lr: 1e-5, + lrScheduler: { + present: false, + cosineAnnealingSchedule: { + etaMin: 5e-6 + } + } + }, + data: { + trainOnPrompt: false + } + } + ] + }, + + 'sort-characters': { + label: 'Toy: Sort Characters', + layers: [{ preset: 'transformer-toy-base' }, { data: { dataset: 'sort' } }] + }, + 'reverse-sequence': { + label: 'Toy: Reverse Sequence', + layers: [{ preset: 'transformer-toy-base' }, { data: { dataset: 'reverse' } }] + }, + 'two-sum': { + label: 'Toy: Two Sum', + layers: [{ preset: 'transformer-toy-base' }, { data: { dataset: 'two-sum' } }] + }, + 'dyck-encoder': { + label: 'Toy: Dyck (Encoder)', + layers: [{ preset: 'encoder-toy-base' }, { data: { dataset: 'dyck' } }] + }, + + tinystories: { + label: 'TinyStories with ~15M parameters', + layers: [ + { + preset: 'transformer-toy-base', + model: { + family: 'transformer', + topology: 'decoder', + layers: 6, + transformer: { + headDim: 32, + attention: { + nKeyValueHeads: 10 + } + } + }, + data: { + trainOnPrompt: false, + natural: { + vocabSize: 8192 + } + }, + optimizer: { + lr: 1e-4, + lrScheduler: { + type: 'cosine', + cosineAnnealingSchedule: { + etaMin: 1e-5 + } + } + }, + training: { + logSteps: 20, + validation: { + batchSize: 16, + valSteps: 500, + completions: { + present: true, + decodingBatchSize: 1, + amount: 'subset', + subsetSize: 1 + } + } + } + }, + { + preset: 'tinystories', + data: { + dataset: 'tinystories' + } + } + ] + }, + + fineweb: { + label: 'FineWeb with ~GPT-2 sized model ⚠️', + layers: [ + { + preset: 'fineweb', + data: { + dataset: 'fineweb', + natural: { + vocabSize: 32_768, + contextSize: 64 + } + }, + model: { + family: 'transformer', + topology: 'decoder', + layers: 12, + transformer: { + headDim: 64, + attention: { + nKeyValueHeads: 12, + gating: { + present: false + } + }, + mlp: { + present: true, + activation: 'gelu', + hiddenExpansionFactor: 4, + variant: 'standard' + }, + normalization: { + qkNorm: { + present: false + } + }, + initialization: { + present: true, + std: 0.02, + projections: { + attention: { + strategy: 'layer-scaled' + }, + mlp: { + strategy: 'layer-scaled' + }, + lmHead: { + strategy: 'layer-scaled' + } + } + } + }, + layerNormalization: { + type: 'layernorm', + eps: 1e-5, + transformer: { + present: true, + position: 'pre' + } + } + }, + optimizer: { + lr: 1e-4, + lrScheduler: { + type: 'cosine', + cosineAnnealingSchedule: { + etaMin: 1e-5 + } + } + }, + training: { + enableVisualization: false, + logSteps: 20, + batchSize: 32, + dropout: { + present: true, + embedding: 0.1, + transformer: { + attention: 0.1, + residual: 0.1 + } + }, + vramLimitMb: { present: true, value: 32_768 }, + validation: { + valSteps: 500, + batchSize: 16, + temperature: 0.0, + completions: { + present: false + } + } + } + } + ] + } +}; + +export function getPresetOptions(): Array<{ value: string; text: string }> { + return Object.entries(PRESET_DEFINITIONS) + .filter(([, def]) => !def.hidden) + .map(([id, def]) => ({ value: id, text: def.label })); +} + +function expandPresetLayers( + presetId: string, + visiting: Set = new Set() +): Array> { + const def = PRESET_DEFINITIONS[presetId]; + if (!def) return []; + if (visiting.has(presetId)) return []; + visiting.add(presetId); + + const expanded = []; + for (const layer of def.layers) { + const maybePreset = layer.preset; + if (typeof maybePreset === 'string' && PRESET_DEFINITIONS[maybePreset]) { + if (!visiting.has(maybePreset)) { + expanded.push(...expandPresetLayers(maybePreset, visiting)); + } + const { preset: _omit, ...rest } = layer; + if (Object.keys(rest).length > 0) { + expanded.push(rest); + } + } else { + expanded.push(layer); + } + } + visiting.delete(presetId); + return expanded; +} + +export function getPresetLayers(presetId: string | null | undefined): Array> { + if (!presetId) return []; + const def = PRESET_DEFINITIONS[presetId]; + return def ? expandPresetLayers(presetId) : []; +} diff --git a/examples/piston-train-toy/src/lib/workspace/ui.svelte.ts b/examples/piston-train-toy/src/lib/workspace/ui.svelte.ts index 605681e5..33003b07 100644 --- a/examples/piston-train-toy/src/lib/workspace/ui.svelte.ts +++ b/examples/piston-train-toy/src/lib/workspace/ui.svelte.ts @@ -207,6 +207,32 @@ export const tourState = new LocalStorage<{ restartedExperiment: false }); +export function openConfigAndScrollToControl( + controlId: string, + sectionsToOpen: Array, + { useLabelFor = false }: { useLabelFor?: boolean } = {} +) { + // Ensure left panel is open + configOpen.current = true; + // Open requested sections + for (const s of sectionsToOpen) { + controlSectionsOpen.current[s] = true; + } + // Scroll after a brief delay to allow layout to settle + setTimeout(() => { + const el = useLabelFor + ? document.querySelector(`label[for="${controlId}"]`) + : document.getElementById(controlId); + if (el) { + el.scrollIntoView({ behavior: 'instant', block: 'center' }); + // Manage manual focus class + const prev = document.querySelector('.is-focused'); + if (prev && prev !== el) prev.classList.remove('is-focused'); + el.classList.add('is-focused'); + } + }, 100); +} + export function switchToMetrics() { selectTab('metrics'); } diff --git a/examples/piston-train-toy/src/routes/tabs/About.svelte b/examples/piston-train-toy/src/routes/tabs/About.svelte index ac9ff6cf..231476f2 100644 --- a/examples/piston-train-toy/src/routes/tabs/About.svelte +++ b/examples/piston-train-toy/src/routes/tabs/About.svelte @@ -3,11 +3,49 @@ import FN from '$lib/components/footnotes/Footnote.svelte'; import FootnotesProvider from '$lib/components/footnotes/FootnotesProvider.svelte'; import UserGuideTooltip from '$lib/components/UserGuideTooltip.svelte'; - import { browserInfo, hasWebGPU } from '$lib/workspace/ui.svelte'; + import { config, setPreset, validateConfig } from '$lib/workspace/config.svelte'; + import { browserInfo, hasWebGPU, startTraining } from '$lib/workspace/ui.svelte'; import { getIconStrokeWidth } from '$lib/workspace/ui.svelte'; + import { openConfigAndScrollToControl, switchToMetrics } from '$lib/workspace/ui.svelte'; + import { getVisualizationExampleById } from '$lib/workspace/visualizationExamples'; + import { trainingState, updateVisualizerScript } from '$lib/workspace/workers.svelte'; import { ExternalLink, Gpu } from 'lucide-svelte'; + import { onMount } from 'svelte'; const iconStrokeWidth = $derived(getIconStrokeWidth()); + + let expanded = $state(false); + let isOverflowing = $state(false); + let contentRef: HTMLDivElement; + + const COLLAPSED_MAX_HEIGHT = 100; + + function measureOverflow() { + if (contentRef) { + isOverflowing = contentRef.scrollHeight > COLLAPSED_MAX_HEIGHT + 1; + } + } + + function wrapClickHandlerForKeyboard(onClick: () => void) { + return (e: KeyboardEvent) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + onClick(); + } + }; + } + + onMount(() => { + measureOverflow(); + const ro = new ResizeObserver(() => measureOverflow()); + if (contentRef) ro.observe(contentRef); + const onResize = () => measureOverflow(); + window.addEventListener('resize', onResize); + return () => { + ro.disconnect(); + window.removeEventListener('resize', onResize); + }; + });
    @@ -111,6 +149,282 @@ Train a language model in your browser with WebGPU

    +
    +
    +

    You could try…

    +
    +
    + {#snippet sClick(text: string, onClick: () => void)} + {@const _onClick = onClick} + {text} + {/snippet} + {#snippet sPreset(text: string, id: string, controlId?: string)} + {@render sClick(text, () => { + setPreset(id); + if (controlId) openConfigAndScrollToControl(controlId, ['task']); + })} + {/snippet} + {#snippet sToyPreset(text: string, id: string, controlId?: string)} + {@render sClick(text, () => { + setPreset(id); + config.model.family = 'transformer'; + validateConfig(); + if (controlId) openConfigAndScrollToControl(controlId, ['task']); + })} + {/snippet} + {#snippet sViz(text: string, exampleId: string)} + {@render sClick(text, () => { + config.visualization.example = exampleId; + config.visualization.script = null; + switchToMetrics(); + if (trainingState.current !== 'stopped') { + const script = getVisualizationExampleById(exampleId).script; + updateVisualizerScript(exampleId, script); + } + startTraining(); + })} + {/snippet} + {#snippet sModelTransform(text: string, controlId: string)} + {@render sClick(text, () => { + config.model.family = 'transformer'; + validateConfig(); + openConfigAndScrollToControl(controlId, ['model']); + })} + {/snippet} + {#snippet sModelRnn(text: string, controlId: string)} + {@render sClick(text, () => { + config.model.family = 'rnn'; + validateConfig(); + openConfigAndScrollToControl(controlId, ['model']); + })} + {/snippet} + {#snippet sModelRnnLstm(text: string, controlId: string)} + {@render sClick(text, () => { + config.model.family = 'rnn'; + config.model.rnn.cellType = 'lstm'; + validateConfig(); + openConfigAndScrollToControl(controlId, ['model']); + })} + {/snippet} + {#snippet sScrollTraining(text: string, controlId: string)} + {@render sClick(text, () => { + openConfigAndScrollToControl(controlId, ['training']); + })} + {/snippet} + {#snippet sScrollOptimizer(text: string, controlId: string)} + {@render sClick(text, () => { + openConfigAndScrollToControl(controlId, ['optimizer']); + })} + {/snippet} + {#snippet sEncoderBidirectional(text: string)} + {@render sClick(text, () => { + if (config.model.topology === 'decoder') config.model.topology = 'encoder'; + validateConfig(); + openConfigAndScrollToControl( + 'model-rnn-bidirectional-encoder-checkbox', + ['model'], + { + useLabelFor: true + } + ); + })} + {/snippet} + {#snippet sEnsureEncDec(text: string)} + {@render sClick(text, () => { + config.model.family = 'rnn'; + config.model.topology = 'encoder-decoder'; + validateConfig(); + openConfigAndScrollToControl('model-rnn-encoder-decoder-attention-group', [ + 'model' + ]); + })} + {/snippet} +

    + …training a + TransformerAttention is All You Need + to {@render sToyPreset( + 'sort characters', + 'sort-characters', + 'dataset-control' + )}, + {@render sToyPreset('reverse a sequence', 'reverse-sequence', 'dataset-control')}, + or + {@render sToyPreset( + 'find the numbers that add up to a sum', + 'two-sum', + 'dataset-control' + )}, then {@render sModelRnnLstm( + 'compare with an LSTM', + 'rnn-cell-type-control' + )}Long Short-Term Memory. + Visualize + {@render sViz('the gradients in the attention layer', 'attention-gradients')}, {@render sViz( + 'all parameters in the network', + 'all-parameters' + )}, or try + {@render sViz('writing your own visualization queries', 'kitchen-sink')}. + Learn to + {@render sPreset( + 'match parentheses in a Dyck language', + 'dyck-encoder', + 'dataset-control' + )}Wikipedia article on Dyck languages + using an encoder-only masked language modeling (MLM) objectiveBERT: Pre-training of Deep Bidirectional Transformers for Language + Understanding + . + Want a taste of natural language? + Try {@render sPreset( + 'training a GPT on TinyStories', + 'tinystories', + 'dataset-control' + )}GPT: Improving Language Understanding by Generative Pre-TrainingTinyStories: How Small Can Language Models Be and Still Speak Coherent + English?, a dataset of short stories generated by GPT-4—and try different tokenizer + sizes. + Play with + {@render sModelTransform('attention gating', 'model-attention-gating-group')}, + {@render sModelTransform('MLP variants', 'model-mlp-group')}, {@render sScrollOptimizer( + 'learning rate schedulers', + 'optimizer-lr-scheduler-group' + )}, + {@render sModelTransform('initialization', 'model-initialization-group')}, {@render sScrollTraining( + 'dropout', + 'training-dropout-group' + )}, or {@render sModelTransform( + 'QK normalization', + 'model-transformer-normalization-qk-norm-group' + )}. + Want to train an RNN? + Mess with + {@render sModelRnn('layer normalization', 'model-rnn-layer-normalization-group')}, + {@render sModelRnn('initialization', 'model-initialization-group')}, + {@render sEncoderBidirectional('bidirectional encoder layers')}, {@render sEnsureEncDec( + 'encoder-decoder attention variants' + )}, or + {@render sScrollTraining( + 'gradient norm clipping', + 'training-clip-grad-norm-group' + )}. + Have a lot of VRAM and want to try something untestedVin, who owns a M1 Pro Macbook with 16GB of unified memory, has never been + able to do this without running out of memory. Good luck!? + Try + {@render sPreset( + 'training a GPT-2-sized model on FineWeb', + 'fineweb', + 'dataset-control' + )}The FineWeb Datasets: Decanting the Web for the Finest Text Data at Scale (and + DM me if you get it to work!). +

    +
    + {#if isOverflowing && !expanded} +
    + {/if} +
    + {#if isOverflowing} +
    + +
    + {/if} +
    +
    +

    Vin Howe built From f9316b4fb7d33c68caedd0fbd79f29b362cec0df Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 2 Nov 2025 22:02:06 -0700 Subject: [PATCH 583/590] Add runs table, short descriptions --- .../lib/components/controls/Controls.svelte | 13 + .../lib/components/controls/RunsTable.svelte | 55 +++ .../src/lib/train/data/toy/addition.ts | 5 + .../src/lib/train/data/toy/copyMemory.ts | 8 + .../src/lib/train/data/toy/dyck.ts | 6 + .../src/lib/train/data/toy/markedAddition.ts | 7 + .../src/lib/train/data/toy/modularAddition.ts | 6 + .../src/lib/train/data/toy/parity.ts | 5 + .../src/lib/train/data/toy/random.ts | 6 + .../src/lib/train/data/toy/repeat.ts | 7 + .../src/lib/train/data/toy/reverse.ts | 7 + .../src/lib/train/data/toy/slapjack.ts | 7 + .../src/lib/train/data/toy/sort.ts | 7 + .../src/lib/train/data/toy/temporalOrder.ts | 6 + .../src/lib/train/data/toy/twoSum.ts | 7 + .../src/lib/train/data/toy/zeros.ts | 4 + .../src/lib/workspace/config.ts | 335 ++++++++++++++++++ .../src/lib/workspace/runs.svelte.ts | 208 ++++++++++- .../src/lib/workspace/ui.svelte.ts | 1 + 19 files changed, 698 insertions(+), 2 deletions(-) create mode 100644 examples/piston-train-toy/src/lib/components/controls/RunsTable.svelte diff --git a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte index ad787562..6c21310f 100644 --- a/examples/piston-train-toy/src/lib/components/controls/Controls.svelte +++ b/examples/piston-train-toy/src/lib/components/controls/Controls.svelte @@ -12,6 +12,7 @@ validateConfig } from '$lib/workspace/config.svelte'; import { getPresetOptions } from '$lib/workspace/presets'; + import { runsMap } from '$lib/workspace/runs.svelte'; import { controlSectionsOpen, toggleControlSection } from '$lib/workspace/ui.svelte'; import { getParameterCount, triggerModelInspection } from '$lib/workspace/workers.svelte'; import { untrack } from 'svelte'; @@ -24,6 +25,7 @@ import DatasetControls from './DatasetControls.svelte'; import LRSchedulePicker from './LRSchedulePicker.svelte'; import NumberInput from './NumberInput.svelte'; + import RunsTable from './RunsTable.svelte'; import SelectInput from './select/SelectInput.svelte'; import SelectModelTopology from './select/SelectModelTopology.svelte'; import SelectWithCitations from './select/SelectWithCitations.svelte'; @@ -222,6 +224,17 @@ />

    + {#if runsMap.size >= 1} + toggleControlSection('runs')} + contentClass="w-full" + > + + + {/if} + + import { clearPastRuns, runsMap } from '$lib/workspace/runs.svelte'; + import { getIconStrokeWidth } from '$lib/workspace/ui.svelte'; + import { TrashIcon } from 'lucide-svelte'; + + const runs = $derived(Array.from(runsMap.values()).toReversed()); + + const frozenColumn = { key: 'runId', label: 'Name' }; + + const iconStrokeWidth = $derived(getIconStrokeWidth()); + + +
    + {#if runs.length > 1} + + {/if} + +
    +
    +
    + + + + + + + + + + + + {#each runs as run (run.runId)} + + + + + {/each} + +
    {frozenColumn.label}Changes
    {run.runId}{run.diffSummary ?? 'initial experiment'}
    +
    +
    +
    +
    diff --git a/examples/piston-train-toy/src/lib/train/data/toy/addition.ts b/examples/piston-train-toy/src/lib/train/data/toy/addition.ts index cc98b1f5..dbb94f49 100644 --- a/examples/piston-train-toy/src/lib/train/data/toy/addition.ts +++ b/examples/piston-train-toy/src/lib/train/data/toy/addition.ts @@ -33,6 +33,11 @@ export const ADDITION_CONFIG_DEFAULTS: AdditionConfig = { includeExpressionTokens: ADDITION_CONFIG_METADATA.parameters.includeExpressionTokens.default }; +export const ADDITION_SHORT_DESCRIPTIONS = { + maxNumber: 'max num', + includeExpressionTokens: 'expr tokens' +}; + export class AdditionDataset extends ToyDataset { /** * Tokenizer for addition. diff --git a/examples/piston-train-toy/src/lib/train/data/toy/copyMemory.ts b/examples/piston-train-toy/src/lib/train/data/toy/copyMemory.ts index e8a53584..f68b74a0 100644 --- a/examples/piston-train-toy/src/lib/train/data/toy/copyMemory.ts +++ b/examples/piston-train-toy/src/lib/train/data/toy/copyMemory.ts @@ -73,6 +73,14 @@ export const COPY_MEMORY_CONFIG_DEFAULTS: CopyMemoryConfig = { includeSeparators: COPY_MEMORY_CONFIG_METADATA.parameters.includeSeparators.default }; +export const COPY_MEMORY_SHORT_DESCRIPTIONS = { + prefixLength: 'prefix', + distractorLength: 'distractor', + vocabSize: 'vocab', + recallTokenEnabled: 'recall', + includeSeparators: 'separators' +}; + export class CopyMemoryDataset extends ToyDataset { /** * Vocab: diff --git a/examples/piston-train-toy/src/lib/train/data/toy/dyck.ts b/examples/piston-train-toy/src/lib/train/data/toy/dyck.ts index 3b8637d1..165ceb32 100644 --- a/examples/piston-train-toy/src/lib/train/data/toy/dyck.ts +++ b/examples/piston-train-toy/src/lib/train/data/toy/dyck.ts @@ -45,6 +45,12 @@ export const DYCK_CONFIG_DEFAULTS: DyckConfig = { onlyTrainOnClosingBrackets: DYCK_CONFIG_METADATA.parameters.onlyTrainOnClosingBrackets.default }; +export const DYCK_SHORT_DESCRIPTIONS = { + sequenceLength: 'seq len', + order: 'order', + onlyTrainOnClosingBrackets: 'only train `)`' +}; + export class DyckDataset extends ToyDataset { /** * For Dyck dataset: diff --git a/examples/piston-train-toy/src/lib/train/data/toy/markedAddition.ts b/examples/piston-train-toy/src/lib/train/data/toy/markedAddition.ts index 5eebbdb8..26938e0b 100644 --- a/examples/piston-train-toy/src/lib/train/data/toy/markedAddition.ts +++ b/examples/piston-train-toy/src/lib/train/data/toy/markedAddition.ts @@ -66,6 +66,13 @@ export const MARKED_ADDITION_CONFIG_DEFAULTS: MarkedAdditionConfig = { includeEqualsToken: MARKED_ADDITION_CONFIG_METADATA.parameters.includeEqualsToken.default }; +export const MARKED_ADDITION_SHORT_DESCRIPTIONS = { + sequenceLength: 'seq len', + maxInputValue: 'max in num', + maxSumValue: 'max sum', + includeEqualsToken: 'include =' +}; + export class MarkedAdditionDataset extends ToyDataset { /** * Vocab design: diff --git a/examples/piston-train-toy/src/lib/train/data/toy/modularAddition.ts b/examples/piston-train-toy/src/lib/train/data/toy/modularAddition.ts index d0cbee6a..faf9295d 100644 --- a/examples/piston-train-toy/src/lib/train/data/toy/modularAddition.ts +++ b/examples/piston-train-toy/src/lib/train/data/toy/modularAddition.ts @@ -42,6 +42,12 @@ export const MODULAR_ADDITION_CONFIG_DEFAULTS: ModularAdditionConfig = { MODULAR_ADDITION_CONFIG_METADATA.parameters.includeExpressionTokens.default }; +export const MODULAR_ADDITION_SHORT_DESCRIPTIONS = { + maxNumber: 'max num', + modulo: 'mod', + includeExpressionTokens: 'incl expr toks' +}; + export class ModularAdditionDataset extends ToyDataset { /** * Tokenizer for modular addition. diff --git a/examples/piston-train-toy/src/lib/train/data/toy/parity.ts b/examples/piston-train-toy/src/lib/train/data/toy/parity.ts index 746ea53a..ceeda076 100644 --- a/examples/piston-train-toy/src/lib/train/data/toy/parity.ts +++ b/examples/piston-train-toy/src/lib/train/data/toy/parity.ts @@ -38,6 +38,11 @@ export const PARITY_CONFIG_DEFAULTS: ParityConfig = { includeColon: PARITY_CONFIG_METADATA.parameters.includeColon.default }; +export const PARITY_SHORT_DESCRIPTIONS = { + sequenceLength: 'seq len', + includeColon: 'include :' +}; + export class ParityDataset extends ToyDataset { /** * For parity dataset: diff --git a/examples/piston-train-toy/src/lib/train/data/toy/random.ts b/examples/piston-train-toy/src/lib/train/data/toy/random.ts index cc63f05b..cd9abe96 100644 --- a/examples/piston-train-toy/src/lib/train/data/toy/random.ts +++ b/examples/piston-train-toy/src/lib/train/data/toy/random.ts @@ -50,6 +50,12 @@ export const RANDOM_CONFIG_DEFAULTS: RandomConfig = { vocabSize: RANDOM_CONFIG_METADATA.parameters.vocabSize.default }; +export const RANDOM_SHORT_DESCRIPTIONS = { + sequenceLength: 'seq len', + responseLength: 'resp len', + vocabSize: 'vocab' +}; + export class RandomDataset extends ToyDataset { /** * For the random dataset: diff --git a/examples/piston-train-toy/src/lib/train/data/toy/repeat.ts b/examples/piston-train-toy/src/lib/train/data/toy/repeat.ts index 6964a5ad..6b84ec76 100644 --- a/examples/piston-train-toy/src/lib/train/data/toy/repeat.ts +++ b/examples/piston-train-toy/src/lib/train/data/toy/repeat.ts @@ -50,6 +50,13 @@ export const REPEAT_CONFIG_DEFAULTS: RepeatConfig = { includeColon: REPEAT_CONFIG_METADATA.parameters.includeColon.default }; +export const REPEAT_SHORT_DESCRIPTIONS = { + sequenceLength: 'seq len', + maxNumber: 'max num', + includeCommas: 'incl `,`', + includeColon: 'incl `:`' +}; + export class RepeatDataset extends ToyDataset { /** * For repeat dataset: diff --git a/examples/piston-train-toy/src/lib/train/data/toy/reverse.ts b/examples/piston-train-toy/src/lib/train/data/toy/reverse.ts index d86b9b71..0c81df77 100644 --- a/examples/piston-train-toy/src/lib/train/data/toy/reverse.ts +++ b/examples/piston-train-toy/src/lib/train/data/toy/reverse.ts @@ -50,6 +50,13 @@ export const REVERSE_CONFIG_DEFAULTS: ReverseConfig = { includeColon: REVERSE_CONFIG_METADATA.parameters.includeColon.default }; +export const REVERSE_SHORT_DESCRIPTIONS = { + sequenceLength: 'seq len', + maxNumber: 'max num', + includeCommas: 'incl `,`', + includeColon: 'incl `:`' +}; + export class ReverseDataset extends ToyDataset { /** * For reverse dataset: diff --git a/examples/piston-train-toy/src/lib/train/data/toy/slapjack.ts b/examples/piston-train-toy/src/lib/train/data/toy/slapjack.ts index aff3f248..c7e320a1 100644 --- a/examples/piston-train-toy/src/lib/train/data/toy/slapjack.ts +++ b/examples/piston-train-toy/src/lib/train/data/toy/slapjack.ts @@ -58,6 +58,13 @@ export const SLAPJACK_CONFIG_DEFAULTS: SlapjackConfig = { // onlyTrainOnSlaps: SLAPJACK_CONFIG_METADATA.parameters.onlyTrainOnSlaps.default, }; +export const SLAPJACK_SHORT_DESCRIPTIONS = { + sequenceLength: 'seq len', + slapOnDoubles: 'slap doubles', + slapOnSandwiches: 'slap sandw', + includeColon: 'incl `:`' +}; + const CARDS = 'A23456789JQK'; export class SlapjackDataset extends ToyDataset { diff --git a/examples/piston-train-toy/src/lib/train/data/toy/sort.ts b/examples/piston-train-toy/src/lib/train/data/toy/sort.ts index bdb50523..6f5644d4 100644 --- a/examples/piston-train-toy/src/lib/train/data/toy/sort.ts +++ b/examples/piston-train-toy/src/lib/train/data/toy/sort.ts @@ -48,6 +48,13 @@ export const SORT_CONFIG_DEFAULTS: SortConfig = { includeColon: SORT_CONFIG_METADATA.parameters.includeColon.default }; +export const SORT_SHORT_DESCRIPTIONS = { + sequenceLength: 'seq len', + maxNumber: 'max num', + includeCommas: 'incl `,`', + includeColon: 'incl `:`' +}; + export class SortDataset extends ToyDataset { /** * For sort dataset: diff --git a/examples/piston-train-toy/src/lib/train/data/toy/temporalOrder.ts b/examples/piston-train-toy/src/lib/train/data/toy/temporalOrder.ts index 7d953c30..4ffed629 100644 --- a/examples/piston-train-toy/src/lib/train/data/toy/temporalOrder.ts +++ b/examples/piston-train-toy/src/lib/train/data/toy/temporalOrder.ts @@ -54,6 +54,12 @@ export const TEMPORAL_ORDER_CONFIG_DEFAULTS: TemporalOrderConfig = { includeSeparators: TEMPORAL_ORDER_CONFIG_METADATA.parameters.includeSeparators.default }; +export const TEMPORAL_ORDER_SHORT_DESCRIPTIONS = { + sequenceLength: 'seq len', + vocabSize: 'vocab', + includeSeparators: 'incl `,`' +}; + export class TemporalOrderDataset extends ToyDataset { /** * Vocab: diff --git a/examples/piston-train-toy/src/lib/train/data/toy/twoSum.ts b/examples/piston-train-toy/src/lib/train/data/toy/twoSum.ts index b1a10e67..2c78cef0 100644 --- a/examples/piston-train-toy/src/lib/train/data/toy/twoSum.ts +++ b/examples/piston-train-toy/src/lib/train/data/toy/twoSum.ts @@ -50,6 +50,13 @@ export const TWO_SUM_CONFIG_DEFAULTS: TwoSumConfig = { includeExpressionTokens: TWO_SUM_CONFIG_METADATA.parameters.includeExpressionTokens.default }; +export const TWO_SUM_SHORT_DESCRIPTIONS = { + sequenceLength: 'seq len', + maxNumber: 'max num', + includeCommas: 'incl `,`', + includeExpressionTokens: 'incl expr toks' +}; + export class TwoSumDataset extends ToyDataset { /** * For Two-Sum dataset: diff --git a/examples/piston-train-toy/src/lib/train/data/toy/zeros.ts b/examples/piston-train-toy/src/lib/train/data/toy/zeros.ts index 18d7346d..41b5a4ea 100644 --- a/examples/piston-train-toy/src/lib/train/data/toy/zeros.ts +++ b/examples/piston-train-toy/src/lib/train/data/toy/zeros.ts @@ -28,6 +28,10 @@ export const ZEROS_CONFIG_DEFAULTS: ZerosConfig = { sequenceLength: ZEROS_CONFIG_METADATA.parameters.sequenceLength.default }; +export const ZEROS_SHORT_DESCRIPTIONS = { + sequenceLength: 'seq len' +}; + export class ZerosDataset extends ToyDataset { /** * For zeros dataset (baseline/debug task): diff --git a/examples/piston-train-toy/src/lib/workspace/config.ts b/examples/piston-train-toy/src/lib/workspace/config.ts index c18430cb..a9391268 100644 --- a/examples/piston-train-toy/src/lib/workspace/config.ts +++ b/examples/piston-train-toy/src/lib/workspace/config.ts @@ -18,6 +18,21 @@ export type { import type { DATASET_CONFIG_DEFAULTS } from '$lib/train/data'; +import { ADDITION_SHORT_DESCRIPTIONS } from '$lib/train/data/toy/addition'; +import { COPY_MEMORY_SHORT_DESCRIPTIONS } from '$lib/train/data/toy/copyMemory'; +import { DYCK_SHORT_DESCRIPTIONS } from '$lib/train/data/toy/dyck'; +import { MARKED_ADDITION_SHORT_DESCRIPTIONS } from '$lib/train/data/toy/markedAddition'; +import { MODULAR_ADDITION_SHORT_DESCRIPTIONS } from '$lib/train/data/toy/modularAddition'; +import { PARITY_SHORT_DESCRIPTIONS } from '$lib/train/data/toy/parity'; +import { RANDOM_SHORT_DESCRIPTIONS } from '$lib/train/data/toy/random'; +import { REPEAT_SHORT_DESCRIPTIONS } from '$lib/train/data/toy/repeat'; +import { REVERSE_SHORT_DESCRIPTIONS } from '$lib/train/data/toy/reverse'; +import { SLAPJACK_SHORT_DESCRIPTIONS } from '$lib/train/data/toy/slapjack'; +import { SORT_SHORT_DESCRIPTIONS } from '$lib/train/data/toy/sort'; +import { TEMPORAL_ORDER_SHORT_DESCRIPTIONS } from '$lib/train/data/toy/temporalOrder'; +import { TWO_SUM_SHORT_DESCRIPTIONS } from '$lib/train/data/toy/twoSum'; +import { ZEROS_SHORT_DESCRIPTIONS } from '$lib/train/data/toy/zeros'; + import type { TOY_DATASET_CONFIG_DEFAULTS } from '../train/data/toy/config'; export interface DropoutConfig { @@ -367,3 +382,323 @@ export interface Config { optimizer: OptimizerConfig; visualization: VisualizationConfig; } + +export type ConfigItemDescription = + | { + shortName: string; + } + | string + | [string, number] + | null; + +type ReplaceValues = T extends object ? { [K in keyof T]: ReplaceValues } : V; + +export type ConfigValues = ReplaceValues; + +export const CONFIG_DESCRIPTIONS: ConfigValues = { + // No default preset; shown as a top-level selector only + preset: null, + training: { + logSteps: 'log steps', + batchSize: 'batch', + clipGradNorm: { + present: 'clip grad norm', + value: 'clip grad norm' + }, + validation: { + present: 'val', + valSteps: 'val steps', + batchSize: 'val size', + temperature: 'val temp', + useKvCache: 'val kv cache', + completions: { + present: 'completions', + decodingBatchSize: 'completions batch', + amount: 'completions amount strategy', + subsetSize: 'completions subset' + } + }, + limitTraining: { + present: 'limit train', + steps: 'max steps' + }, + labelSmoothing: { + present: 'smoothing', + value: 'smoothing' + }, + dropout: { + present: 'dropout', + embedding: 'dropout emb', + transformer: { + attention: 'dropout attn', + residual: 'dropout resid' + }, + rnn: { + interLayer: 'dropout rnn' + } + }, + randomSeed: { + present: 'seed', + value: 'seed' + }, + gradNorm: { + track: 'track grad norm', + errorIfNonfinite: 'error nonfinite' + }, + useWeakTensorReferences: 'weak tensor refs', + sharedObjectAllocation: 'shared objs', + cachingEnabled: 'caching', + inplaceSupport: 'inplace', + enableVisualization: 'viz', + vramLimitMb: { + present: 'vram lim', + value: 'vram lim' + }, + restartEverySteps: 'restart steps' + }, + data: { + dataset: 'dataset', + trainOnPrompt: 'train prompt', + maskRatio: 'mask ratio', + specialTokens: { + includeEos: 'eos' + }, + datasets: { + addition: ADDITION_SHORT_DESCRIPTIONS, + 'copy-memory': COPY_MEMORY_SHORT_DESCRIPTIONS, + dyck: DYCK_SHORT_DESCRIPTIONS, + elman: {}, + 'marked-addition': MARKED_ADDITION_SHORT_DESCRIPTIONS, + 'modular-addition': MODULAR_ADDITION_SHORT_DESCRIPTIONS, + parity: PARITY_SHORT_DESCRIPTIONS, + random: RANDOM_SHORT_DESCRIPTIONS, + repeat: REPEAT_SHORT_DESCRIPTIONS, + reverse: REVERSE_SHORT_DESCRIPTIONS, + slapjack: SLAPJACK_SHORT_DESCRIPTIONS, + sort: SORT_SHORT_DESCRIPTIONS, + 'temporal-order': TEMPORAL_ORDER_SHORT_DESCRIPTIONS, + 'two-sum': TWO_SUM_SHORT_DESCRIPTIONS, + zeros: ZEROS_SHORT_DESCRIPTIONS + }, + // datasets: TOY_DATASET_CONFIG_DEFAULTS, + natural: { + contextSize: 'ctx', + vocabSize: 'vocab' + } + }, + model: { + family: 'family', + topology: 'topology', + layers: 'layers', + tieEmbeddingsAndLmHead: 'tie emb + lm head', + roundVocabSizeToNearestMultiple: { + present: 'vocab mult', + value: 'vocab mult' + }, + encoderDecoder: { + encoderLayers: 'n enc', + decoderLayers: 'n dec' + }, + layerNormalization: { + type: 'ln', + eps: 'eps', + transformer: { + present: 'ln', + position: 'ln pos' + }, + rnn: { + withinCell: 'ln in cell', + betweenLayers: 'ln between' + } + }, + transformer: { + headDim: 'head dim', + initialization: { + present: 'init', + std: 'init std', + projections: { + attention: { + present: 'init attn', + strategy: 'init attn' + }, + mlp: { + present: 'init mlp', + strategy: 'init mlp' + }, + lmHead: { + present: 'init lmhead', + strategy: 'init lmhead' + } + } + }, + attention: { + present: 'attn', + nKeyValueHeads: 'n attn kv', + groupedQueryAttention: { + present: 'gqa', + queryHeadsPerKeyValueHead: 'n attn q/kv' + }, + gating: { + present: 'attn gate', + activation: 'attn gate act', + sites: { + afterSdpaOutput: 'gate@sdpa', + afterValueProjection: 'gate@value', + afterKeyProjection: 'gate@key', + afterQueryProjection: 'gate@query', + afterFinalOutputProjection: 'gate@proj' + } + }, + sinks: { + present: 'attn sinks' + } + }, + positionalEncoding: { + present: 'pos enc', + type: 'pos type', + alibi: { + maxBias: 'alibi max bias' + }, + rope: { + base: 'rope base' + } + }, + normalization: { + qkNorm: { + present: 'qk norm', + type: 'qk norm type', + eps: 'qk norm eps' + }, + softcap: { + attention: { + present: 'softcap attn', + value: 'softcap attn' + }, + logits: { + present: 'softcap logits', + value: 'softcap logits' + } + } + }, + mlp: { + present: 'mlp', + activation: 'mlp act', + hiddenExpansionFactor: 'mlp factor', + variant: 'mlp mode' + } + }, + rnn: { + cellType: 'cell', + embedding: { + type: 'cell emb', + learned: { + size: 'cell emb' + } + }, + separateHiddenSize: { + present: 'cell hid.', + value: 'cell hid.' + }, + initialization: { + present: 'init', + xavierInputColumns: { + present: 'xavier in. cols', + distribution: 'xavier in. dist' + }, + orthogonalRecurrentColumns: 'ortho cols', + perGateOrthogonalBlocks: 'ortho blocks', + zeroBiases: 'zero biases', + gru: { + updateGateBias: { + present: 'gru update gate bias', + value: 'gru update gate bias' + } + }, + lstm: { + forgetGateBias: { + present: 'forget gate bias', + value: 'forget gate bias' + } + } + }, + hiddenStateProjection: { + present: 'hid. proj', + size: 'hid. proj' + }, + encoder: { + bidirectional: 'enc bidir' + }, + encoderDecoderAttention: { + present: 'enc-dec attn', + type: 'enc-dec attn', + inputFeedingProjection: 'enc-dec proj', + multiplicative: { + scaleByInverseSqrtHiddenSize: 'x 1/sqrt(hid.)' + } + } + } + }, + optimizer: { + type: 'optim', + lr: 'lr', + weightDecay: { + present: 'decay', + value: 'decay', + useWeightDecayGroups: 'decay groups' + }, + warmupSteps: { + present: 'warmup', + value: 'warmup steps' + }, + lrScheduler: { + present: 'lr sched', + type: 'lr sched', + stepSchedule: { + stepSize: 'sched step', + gamma: 'sched gamma' + }, + constantSchedule: { + factor: 'const lr factor', + totalIters: 'const lr total' + }, + cosineAnnealingSchedule: { + tMax: 'cos lr tmax', + etaMin: 'cos lr eta min' + }, + exponentialSchedule: { + gamma: 'exp lr gamma' + }, + linearSchedule: { + startFactor: 'lin lr start', + endFactor: 'lin lr end', + totalIters: 'lin lr total' + } + }, + adam: { + beta1: 'adam beta1', + beta2: 'adam beta2', + eps: 'adam eps', + amsgrad: 'adam ams' + }, + sgd: { + momentum: 'sgd moment', + dampening: 'sgd damp', + nesterov: 'sgd nester' + }, + muon: { + momentum: 'muon moment', + nsSteps: 'muon nssteps', + nesterov: 'muon nester' + } + }, + visualization: { + script: null, + example: null, + target: null, + selectedValidation: { + exampleIndex: null, + tokenIndex: null + } + }, + version: null +}; diff --git a/examples/piston-train-toy/src/lib/workspace/runs.svelte.ts b/examples/piston-train-toy/src/lib/workspace/runs.svelte.ts index f75cc061..bdd99377 100644 --- a/examples/piston-train-toy/src/lib/workspace/runs.svelte.ts +++ b/examples/piston-train-toy/src/lib/workspace/runs.svelte.ts @@ -4,7 +4,7 @@ import type { ValidationStep } from '$lib/train/validation'; import { generateMemorableName } from '$lib/workspace/utils'; import { SvelteMap, SvelteSet } from 'svelte/reactivity'; -import { type Config } from './config'; +import { type Config, CONFIG_DESCRIPTIONS } from './config'; export type BaseStepData = { step: number }; @@ -34,6 +34,7 @@ export type RunData = { step: number; lastUpdated: number; createdAt: number; + diffSummary: string; }; export type RunMeta = { @@ -44,6 +45,189 @@ export type RunMeta = { // A palette of distinct colors for runs const RUN_COLORS = ['#f5493b', '#dfa300', '#3bbc4a', '#00b7c0', '#4475f6', '#cc5cc5']; +export const PREFIX_BOOSTS: Record = { data: 10 }; + +type DiffItem = { + label: string; + path: string[]; + display: string; + boost: number; +}; + +function pathToString(path: ReadonlyArray): string { + return path.join('.'); +} + +function getAtPath(obj: unknown, path: ReadonlyArray): unknown { + let cur = obj; + for (const key of path) { + if (cur == null || typeof cur !== 'object') return undefined; + cur = (cur as Record)[key]; + } + return cur; +} + +function getDescriptor(path: ReadonlyArray): { label: string | null; itemBoost?: number } { + let cur = CONFIG_DESCRIPTIONS as unknown; + + for (const key of path) { + if (cur == null || typeof cur !== 'object') return { label: null }; + cur = (cur as Record)[key]; + } + if (cur == null) return { label: null }; + if (Array.isArray(cur)) { + const [shortName, boost] = cur; + return { label: shortName, itemBoost: typeof boost === 'number' ? boost : undefined }; + } + if (typeof cur === 'string') return { label: cur }; + if (typeof cur === 'object' && 'shortName' in cur && typeof cur.shortName === 'string') { + return { label: cur.shortName }; + } + return { label: null }; +} + +function maxPrefixBoost(path: ReadonlyArray, boosts: Record): number { + let maxB = 0; + for (let i = 1; i <= path.length; i++) { + const pref = path.slice(0, i).join('.'); + const b = boosts[pref]; + if (typeof b === 'number' && b > maxB) maxB = b; + } + return maxB; +} + +function orderOfMagnitude(n: number): number { + if (n === 0) return 0; + return Math.floor(Math.log10(Math.abs(n))); +} + +function formatScientific(n: number, significantDigits = 1): string { + if (n === 0) return '0'; + const s = n.toExponential(Math.max(0, significantDigits - 1)); + const [mant, expRaw] = s.split('e'); + const mantTrim = mant + .replace(/\.0+$/, '') + .replace(/(\.[0-9]*?)0+$/, '$1') + .replace(/\.$/, ''); + const exp = String(parseInt(expRaw, 10)); + return `${mantTrim}e${exp}`; +} + +function formatNumberDiff(a: number, b: number): string { + const bothSmallInts = + Number.isInteger(a) && Number.isInteger(b) && Math.abs(a) < 100 && Math.abs(b) < 100; + if (bothSmallInts) return `${a}→${b}`; + + // If signs differ or either is non-integer, prefer concise scientific form + const expA = orderOfMagnitude(a); + const expB = orderOfMagnitude(b); + const bothInts = Number.isInteger(a) && Number.isInteger(b); + const base = Math.pow(10, expA); + if ( + bothInts && + Math.sign(a) >= 0 && + Math.sign(b) >= 0 && + expA === expB && + // Only use suffix form for small changes + Math.abs(b - a) < base + ) { + const lead = Math.floor(a / base); + const suffixA = a - lead * base; + const suffixB = b - lead * base; + return `${lead}e${expA}+(${suffixA}→${suffixB})`; + } + const sa = formatScientific(a, 1); + const sb = formatScientific(b, 1); + return `${sa}⥵${sb}`; +} + +function formatValue(v: unknown): string { + if (typeof v === 'number') return formatScientific(v, 1); + if (typeof v === 'string') return v; + if (typeof v === 'boolean') return v ? 'true' : 'false'; + return String(v); +} + +function comparePrimitive(a: unknown, b: unknown): boolean { + // Strict equality suffices for primitives we expect here + return a === b; +} + +function collectLeafPaths(obj: unknown, prefix: string[] = []): string[][] { + const out: string[][] = []; + if (obj == null || typeof obj !== 'object') return out; + for (const key of Object.keys(obj)) { + const nextPath = [...prefix, key]; + const val = (obj as Record)[key]; + if (val != null && typeof val === 'object') { + // Only traverse objects that are not arrays + if (!Array.isArray(val)) out.push(...collectLeafPaths(val, nextPath)); + } else { + out.push(nextPath); + } + } + return out; +} + +export function describeConfigDiff( + prev: Config | null, + curr: Config, + opts?: { topK?: number; prefixBoosts?: Record } +): string { + const topK = opts?.topK ?? 3; + const boosts = opts?.prefixBoosts ?? PREFIX_BOOSTS; + if (!prev) return 'initial experiment'; + + const prevPaths = collectLeafPaths(prev); + const currPaths = collectLeafPaths(curr); + const allKey = new SvelteSet(); + for (const p of prevPaths) allKey.add(pathToString(p)); + for (const p of currPaths) allKey.add(pathToString(p)); + + const diffs: DiffItem[] = []; + for (const key of allKey) { + const path = key.split('.'); + // if (shouldSkipPath(path)) continue; + const { label, itemBoost } = getDescriptor(path); + if (!label) continue; + const a = getAtPath(prev, path); + const b = getAtPath(curr, path); + if (comparePrimitive(a, b)) continue; + + const prefixB = maxPrefixBoost(path, boosts); + let effBoost = prefixB; + if (typeof itemBoost === 'number') { + if (itemBoost < prefixB) { + console.warn( + `item boost lower than parent prefix; path=${key} itemBoost=${itemBoost} parentMax=${prefixB}` + ); + } + effBoost = Math.max(effBoost, itemBoost); + } + + let display: string; + if (typeof a === 'boolean' && typeof b === 'boolean') { + display = b ? `+${label}` : `-${label}`; + } else if (typeof a === 'number' && typeof b === 'number') { + display = `${label}:${formatNumberDiff(a, b)}`; + } else { + display = `${label}:${formatValue(a)}→${formatValue(b)}`; + } + + diffs.push({ label, path, display, boost: effBoost }); + } + + diffs.sort((x, y) => { + if (y.boost !== x.boost) return y.boost - x.boost; + return x.label.localeCompare(y.label); + }); + + if (diffs.length === 0) return 'no changes'; + const top = diffs.slice(0, topK).map((d) => d.display); + const rest = diffs.length - top.length; + return rest > 0 ? `${top.join(', ')}, etc ${rest} more` : top.join(', '); +} + export const runsMap = new SvelteMap(); export const runCounter = $state({ current: 0 }); export const currentRun = $state<{ current: RunMeta | null }>({ @@ -112,6 +296,20 @@ export function getLastNRuns(n: number): ReadonlyArray { return allRuns.slice(0, n); } +export function clearPastRuns(): void { + const keepRunId = currentRun.current; + if (keepRunId === null) { + // No current run tracked; clear everything + runsMap.clear(); + return; + } + for (const runId of [...runsMap.keys()]) { + if (runId !== keepRunId.runId) { + runsMap.delete(runId); + } + } +} + export function newRun(config: Config, id?: string): RunData { if (id && runsMap.has(id)) { throw new Error(`Run with id ${id} already exists`); @@ -123,6 +321,11 @@ export function newRun(config: Config, id?: string): RunData { // Find baseline as immediately-previous-by-creation const existingRuns = [...runsMap.values()]; existingRuns.sort((a, b) => (a.createdAt ?? a.lastUpdated) - (b.createdAt ?? b.lastUpdated)); + const prevRun = existingRuns.length > 0 ? existingRuns[existingRuns.length - 1] : undefined; + const diffSummary = describeConfigDiff(prevRun?.config ?? null, config, { + topK: 3, + prefixBoosts: PREFIX_BOOSTS + }); const run = { runId: runId, @@ -131,7 +334,8 @@ export function newRun(config: Config, id?: string): RunData { metrics: new SvelteMap(), step: 0, lastUpdated: now, - createdAt: now + createdAt: now, + diffSummary }; runsMap.set(runId, run); runCounter.current += 1; diff --git a/examples/piston-train-toy/src/lib/workspace/ui.svelte.ts b/examples/piston-train-toy/src/lib/workspace/ui.svelte.ts index 33003b07..8d2f89cf 100644 --- a/examples/piston-train-toy/src/lib/workspace/ui.svelte.ts +++ b/examples/piston-train-toy/src/lib/workspace/ui.svelte.ts @@ -170,6 +170,7 @@ export function getIconStrokeWidth() { // Initialize sectionsOpen from localStorage or use defaults export const controlSectionsOpen = new LocalStorage('controlSectionsOpen', { + runs: true, training: true, task: true, model: true, From 11ab5e90ec189ab9180b598838cc3b13c833d6e6 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 2 Nov 2025 22:17:09 -0700 Subject: [PATCH 584/590] Add current revision --- examples/piston-train-toy/src/app.d.ts | 1 + examples/piston-train-toy/src/routes/tabs/About.svelte | 9 +++++++++ examples/piston-train-toy/vite.config.ts | 6 ++++++ 3 files changed, 16 insertions(+) diff --git a/examples/piston-train-toy/src/app.d.ts b/examples/piston-train-toy/src/app.d.ts index da08e6da..da2a8798 100644 --- a/examples/piston-train-toy/src/app.d.ts +++ b/examples/piston-train-toy/src/app.d.ts @@ -1,6 +1,7 @@ // See https://svelte.dev/docs/kit/types#app.d.ts // for information about these interfaces declare global { + const __COMMIT_HASH__: string; namespace App { // interface Error {} // interface Locals {} diff --git a/examples/piston-train-toy/src/routes/tabs/About.svelte b/examples/piston-train-toy/src/routes/tabs/About.svelte index 231476f2..2e46c098 100644 --- a/examples/piston-train-toy/src/routes/tabs/About.svelte +++ b/examples/piston-train-toy/src/routes/tabs/About.svelte @@ -619,6 +619,15 @@ >Vin Howe + + + Revision {__COMMIT_HASH__} +
    diff --git a/examples/piston-train-toy/vite.config.ts b/examples/piston-train-toy/vite.config.ts index 76ffc2b7..36234892 100644 --- a/examples/piston-train-toy/vite.config.ts +++ b/examples/piston-train-toy/vite.config.ts @@ -1,5 +1,6 @@ import { sveltekit } from '@sveltejs/kit/vite'; import tailwindcss from '@tailwindcss/vite'; +import { execSync } from 'node:child_process'; import fs from 'node:fs/promises'; import path from 'path'; import { fileURLToPath } from 'url'; @@ -26,7 +27,12 @@ const pruneStaticDirs = () => { }; }; +const commitHash = execSync('git rev-parse --short HEAD').toString().trim(); + export default defineConfig((_) => ({ + define: { + __COMMIT_HASH__: JSON.stringify(commitHash) + }, plugins: [tailwindcss(), sveltekit(), wasm(), pruneStaticDirs()], worker: { format: 'es', From c4370175c42d2465566c8a026b07b8bca482845d Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 2 Nov 2025 22:22:08 -0700 Subject: [PATCH 585/590] Add README for web app --- examples/piston-train-toy/README.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 examples/piston-train-toy/README.md diff --git a/examples/piston-train-toy/README.md b/examples/piston-train-toy/README.md new file mode 100644 index 00000000..28e15fc5 --- /dev/null +++ b/examples/piston-train-toy/README.md @@ -0,0 +1 @@ +This is the source code for the web demo at [sequence.toys](https://sequence.toys). It is a static Svelte app. From ae79086399cc72419fee6cc1933d1cf2b41f9d1c Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 2 Nov 2025 22:27:56 -0700 Subject: [PATCH 586/590] js_tensor_web_op formatting --- crates/piston-macros/src/js_tensor_web_op.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/piston-macros/src/js_tensor_web_op.rs b/crates/piston-macros/src/js_tensor_web_op.rs index b93368c4..ab2d1c40 100644 --- a/crates/piston-macros/src/js_tensor_web_op.rs +++ b/crates/piston-macros/src/js_tensor_web_op.rs @@ -737,8 +737,10 @@ fn build_options_struct( attrs.push(quote! { #[tsify(type = #lit)] }); // Determine serde `with` strategy for JsValue-backed fields. - // - For Tensor and TensorOrScalar (and their Option variants), use crate::js_util::try_from_js_value_preserve - // - For other JsValue-backed types (Dims, Dim, ShapeWithOneHole, NormOrd), use serde_wasm_bindgen::preserve + // - For Tensor and TensorOrScalar (and their Option variants), use + // crate::js_util::try_from_js_value_preserve + // - For other JsValue-backed types (Dims, Dim, ShapeWithOneHole, NormOrd), use + // serde_wasm_bindgen::preserve let is_tensor_like = is_type(&p.ty, "Tensor") || is_optional_of_type(&p.ty, "Tensor") || is_type(&p.ty, "TensorOrScalar") From ade52142291df299f33dae6b5f135377f7f3b6f4 Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 2 Nov 2025 22:30:43 -0700 Subject: [PATCH 587/590] Formatting rollup.config.ts --- packages/web/rollup.config.ts | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/packages/web/rollup.config.ts b/packages/web/rollup.config.ts index a25bd7ae..9138e199 100644 --- a/packages/web/rollup.config.ts +++ b/packages/web/rollup.config.ts @@ -18,13 +18,7 @@ function tscAlias(options) { }; } -const createConfig = ({ - format, - input, - outputFile, - browser = false, - minify = false, -}) => { +const createConfig = ({ format, input, outputFile, browser = false, minify = false }) => { const plugins = [ // Resolve node modules resolve({ @@ -49,12 +43,14 @@ const createConfig = ({ // Add minification for production builds if (minify) { - plugins.push(terser({ - mangle: { - // We do this so that we can keep track of module scopes - keep_classnames: true, - }, - })); + plugins.push( + terser({ + mangle: { + // We do this so that we can keep track of module scopes in CQL + keep_classnames: true, + }, + }), + ); } return { @@ -73,12 +69,7 @@ const createConfig = ({ }, // Make sure we externalize the @piston-ml/piston-web dependency, and the CodeMirror/Lezer // packages. - external: [ - "@piston-ml/piston-web-wasm", - /@codemirror\/[^/]+/, - // /@lezer\/[^/]+/, - "codemirror" - ], + external: ["@piston-ml/piston-web-wasm", /@codemirror\/[^/]+/, "codemirror"], plugins, // Ensure WASM imports are properly handled onwarn(warning, warn) { From 7c25d9df8f27774eb2771e6b9b8d33a0ce8daecf Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 2 Nov 2025 22:48:35 -0700 Subject: [PATCH 588/590] Add Github link --- examples/piston-train-toy/src/routes/+page.svelte | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/examples/piston-train-toy/src/routes/+page.svelte b/examples/piston-train-toy/src/routes/+page.svelte index 4fc027c2..0c57c462 100644 --- a/examples/piston-train-toy/src/routes/+page.svelte +++ b/examples/piston-train-toy/src/routes/+page.svelte @@ -29,6 +29,7 @@ import { ChartLine, DownloadIcon, + ExternalLink, Info, PauseIcon, PlayIcon, @@ -117,6 +118,16 @@ class="w-full py-0.5 px-1.5 flex items-center justify-between text-purple-900 bg-purple-200 border-b border-purple-300 gap-8" > Sequence Toy +
    Date: Sun, 2 Nov 2025 22:49:58 -0700 Subject: [PATCH 589/590] Add prettier svelte plugin --- examples/piston-train-toy/.prettierrc | 3 +++ examples/piston-train-toy/package.json | 1 + pnpm-lock.yaml | 16 +++++++++++++++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/examples/piston-train-toy/.prettierrc b/examples/piston-train-toy/.prettierrc index 039d96a8..bd92dfb0 100644 --- a/examples/piston-train-toy/.prettierrc +++ b/examples/piston-train-toy/.prettierrc @@ -4,6 +4,9 @@ "trailingComma": "none", "printWidth": 100, "tabWidth": 2, + "plugins": [ + "prettier-plugin-svelte" + ], "overrides": [ { "files": "*.svelte", diff --git a/examples/piston-train-toy/package.json b/examples/piston-train-toy/package.json index 1d59eacf..72b1fef3 100644 --- a/examples/piston-train-toy/package.json +++ b/examples/piston-train-toy/package.json @@ -47,6 +47,7 @@ "glob": "^11.0.3", "globals": "^16.4.0", "prettier": "^3.6.2", + "prettier-plugin-svelte": "^3.4.0", "rollup": "^4.52.4", "svelte": "^5.39.11", "svelte-check": "^4.3.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8521629c..3c743856 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -50,7 +50,7 @@ importers: specifier: ^0.16.7 version: 0.16.7 codemirror: - specifier: ^6.0.2 + specifier: ^6.0.1 version: 6.0.2 echarts: specifier: ^6.0.0 @@ -119,6 +119,9 @@ importers: prettier: specifier: ^3.6.2 version: 3.6.2 + prettier-plugin-svelte: + specifier: ^3.4.0 + version: 3.4.0(prettier@3.6.2)(svelte@5.39.11) rollup: specifier: ^4.52.4 version: 4.52.4 @@ -5248,6 +5251,12 @@ packages: resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} engines: {node: '>=6.0.0'} + prettier-plugin-svelte@3.4.0: + resolution: {integrity: sha512-pn1ra/0mPObzqoIQn/vUTR3ZZI6UuZ0sHqMK5x2jMLGrs53h0sXhkVuDcrlssHwIMk7FYrMjHBPoUSyyEEDlBQ==} + peerDependencies: + prettier: ^3.0.0 + svelte: ^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0 + prettier@3.6.2: resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} engines: {node: '>=14'} @@ -11819,6 +11828,11 @@ snapshots: dependencies: fast-diff: 1.3.0 + prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.39.11): + dependencies: + prettier: 3.6.2 + svelte: 5.39.11 + prettier@3.6.2: {} pretty-bytes@3.0.1: From a39b297448d57fe47ec85c43e3985ab67bab177a Mon Sep 17 00:00:00 2001 From: Vin Howe <24789592+vinhowe@users.noreply.github.com> Date: Sun, 2 Nov 2025 23:00:46 -0700 Subject: [PATCH 590/590] Keep class names for CQL --- examples/piston-train-toy/vite.config.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/piston-train-toy/vite.config.ts b/examples/piston-train-toy/vite.config.ts index 36234892..f10d1482 100644 --- a/examples/piston-train-toy/vite.config.ts +++ b/examples/piston-train-toy/vite.config.ts @@ -50,7 +50,8 @@ export default defineConfig((_) => ({ ] }, esbuild: { - supported: { 'top-level-await': true } + supported: { 'top-level-await': true }, + keepNames: true }, server: { fs: {